Translate

2020年12月7日 星期一

SVG 與 javascript

SVG 可以很不錯的和 CSS 整合在一起,那麼來看看和 javascript 相關的 API

動畫控制

SVG 的動畫控制上,針對單一動畫很奇怪的沒有 暫停 這個功能,只能對全部的 SVG 動畫全部暫停

指令 :

  svg.pauseAnimations();
  svg.unpauseAnimations();

範例如下:


<svg id="a120700_svg" width="300" height="60" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<rect x="10" y="10" width="20" height="20">
  <animate attributeName="x" values="10;200" dur="3s" repeatCount="indefinite"></animate>
</rect>
</svg><br>
<button id="a120700_pause">暫停</button>
<button id="a120700_unpause">繼續</button>
<br>
<script>
$(()=>
{
  $("#a120700_pause").on("click",()=>
  {
    $("#a120700_svg")[0].pauseAnimations();
  });
  $("#a120700_unpause").on("click",()=>
  {
    $("#a120700_svg")[0].unpauseAnimations();
  });
});
</script>  


對於單一動畫比較會用到的 API 有這些

  getCurrentTime()     - 取得目前動畫的時間
  beginElement()       - 開始動畫
  beginElementAt(時間)  - 在時間(秒)後開始動畫
  endElement()         - 停止動畫
  endElementAt(時間)    - 在時間(秒)後停止動畫

EVENT 的部份有這三個

  beginEvent           - 動畫開始時
  endEvent             - 動畫停止時
  repeatEvent          - 動畫重覆時

先來一個簡單的範例,範例中每 100ms 更新一次狀態,用下面這段程式碼來取得動畫執行中的 x 座標,如果要設定計 暫停 的功能,必須先取得座標,在下次執行時從這裡開始,然而這個解法有很多問題,最理想的還是等 SVG 的動畫新增暫停的指令

  document.getElementById("a120701_r1").x.animVal.value;

<svg width="300" height="60">
<rect id="a120701_r1" x="10" y="10" width="30" height="30">
<animate id="a120701_a1" attributeName="x" to="250" dur="6s" repeatCount="indefinite" begin="indefinite" end="indefinite" fill="freeze"></animate>
</rect>
</svg> <br>
<button id="a120701_start">點我開始</button>
<button id="a120701_stop">點我停止</button>
<br><br>
動畫時間 : <span id="a120701_time"></span><br>
x 座標 : <span id="a120701_xloc"></span><br><br>
<textarea id="a120701_status" style="width:300px;height:100px"></textarea>
<script>
$(()=>
{
  let hh="";
  let tt=0;
  let a1=document.getElementById("a120701_a1");
/////////
  a1.addEventListener("beginEvent",function()
  {
    show("beginEvent");
  });
  a1.onbegin=function()//這個用法只有 chrome 有用
  {
    show("onbegin");
  }
/////////
  a1.addEventListener("endEvent",function()
  {
    show("endEvent");
  });
  a1.onend=function()//這個用法只有 chrome 有用
  {
    show("onend");
  }
///////// safari 和 iphone 不支援 repeat 
  a1.addEventListener("repeatEvent",function()
  {
    show("repeatEvent");
  });
  a1.onrepeat=function()//這個用法只有 chrome 有用
  {
    show("onrepeat");
  }
  $("#a120701_start").on("click",function()
  {
    a1.beginElement();
  });
  $("#a120701_stop").on("click",function()
  {
    a1.endElement();
  });
  function show(msg)
  {
    tt=a1.getCurrentTime().toFixed(2);
    hh=tt+":"+msg+"\n"+hh;
    $("#a120701_status").html(hh);
  }
  function update()
  {
    if (!a1) return;
    tt=a1.getCurrentTime().toFixed(2);
    $("#a120701_time").html(tt);
    let r1=document.getElementById("a120701_r1");
    let ax=r1.x.animVal.value.toFixed(2);
    $("#a120701_xloc").html(ax);
    setTimeout(update,100);    
  }
  update();
})
</script> 



動畫時間 : 0.03
x 座標 : 10.00

既然可以用 javascript 控制,那麼先來修改一下上一篇的時鐘,來把時間調成目前時間


<svg width="300" height="300" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:ev="http://www.w3.org/2001/xml-events" stroke="#000">
<circle id="a120702_o1" cx="150" cy="150" r="140" fill="none" stroke="#000" stroke-width="2"></circle>
<g id="a120702_g3">
<g id="a120702_g2">
<g id="a120702_g1" stroke="#00f">
<line x1="10" y1="150" x2="40" y2="150" stroke-width="3"></line>
<defs>
<line id="a120702_d1" x1="10" y1="150" x2="20" y2="150" stroke="#f00"></line>
</defs>
<use xlink:href="#a120702_d1" transform="rotate(6,150,150)"></use>
<use xlink:href="#a120702_d1" transform="rotate(12,150,150)"></use>
<use xlink:href="#a120702_d1" transform="rotate(18,150,150)"></use>
<use xlink:href="#a120702_d1" transform="rotate(24,150,150)"></use>
</g>
<use xlink:href="#a120702_g1" transform="rotate(30,150,150)"></use>
<use xlink:href="#a120702_g1" transform="rotate(60,150,150)"></use>
</g>
<use xlink:href="#a120702_g2" transform="rotate(90,150,150)"></use>
</g>
<use xlink:href="#a120702_g3" transform="rotate(180,150,150)"></use>
<line id="a120702_hh" x1="150" y1="150" x2="150" y2="80" stroke-width="10">
  <animateTransform attributeName="transform" type="rotate" values="0,150,150;360,150,150" dur="43200s" repeatCount="indefinite"></animateTransform>
</line>
<line id="a120702_mm" x1="150" y1="150" x2="150" y2="50" stroke-width="6">
  <animateTransform attributeName="transform" type="rotate" values="0,150,150;360,150,150" dur="3600s" repeatCount="indefinite"></animateTransform>
</line>
<line id="a120702_ss" x1="150" y1="150" x2="150" y2="30" stroke-width="2" stroke="#f00">
  <animateTransform attributeName="transform" type="rotate" values="0,150,150;360,150,150" dur="60s" repeatCount="indefinite"></animateTransform>
</line>
<circle id="o2" cx="150" cy="150" r="8"></circle>
</svg>
<script>
$(()=>
{
  let tt=new Date();
  let hh=tt.getHours();
  let mm=tt.getMinutes();
  let ss=tt.getSeconds();
//
  if (hh>12) hh-=12;
//
  let ahh=$("#a120702_hh > animateTransform");
  let amm=$("#a120702_mm > animateTransform");
  let ass=$("#a120702_ss > animateTransform");
//
  let dh=360*(hh+(mm/60)+(ss/3600))/12;
  ahh.attr("values",dh+",150,150;"+(dh+360)+",150,150");
//    
  let dm=360*(mm+ss/60)/60;
  amm.attr("values",dm+",150,150;"+(dm+360)+",150,150");
//
  let ds=360*ss/60;
  ass.attr("values",ds+",150,150;"+(ds+360)+",150,150");
//
});
</script>  

新增物件

SVG 由於命名空間的問題,用 html 的 append 新增html字串時不會有反應, 之前用過一個技巧,就是重寫裡頭的 html 來產生效果,其實更正確的方式是,建立一個屬於 svg 命名空間的物件,就可以用 append 加入了,方法如下:

  var obj=document.createElementNS('http://www.w3.org/2000/svg', "tag 名稱,例如 rect");

  建立 obj 後可以用

  obj.setAttribute("參數",值);
  或用 jquery
  $(obj).attr("參數",值);

範例如下:


<svg width="300" height="300" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:ev="http://www.w3.org/2001/xml-events">
<g id="a120703_g1">
<rect id="a120703_r1" x="10" y="10" width="30" height="30" stroke="#f00" fill="#ff0"></rect>
</g>
</svg><br>
<button id="a120703_b1">用append新增</button>
<button id="a120703_b2">重新寫入html</button>
<button id="a120703_b3">用createElementNS新增</button>
<br>
<script>
$(()=>
{
  let yloc=50;
  $("#a120703_b1").on("click",()=>
  {
    let i;
    let hh="";
    for (i=0;i<3;i++)
    {
      hh+='<rect id="a120703_h'+(yloc+i)+'" x="'+(10+i*40)+'" y="'+yloc+'" width="30" height="30" stroke="#00f" fill="#0ff"></rect>';
    }
    $("#a120703_g1").append(hh);//append 後,原始碼有新增,但是沒效果
    yloc+=40;
  });
  $("#a120703_b2").on("click",()=>
  {
    $("#a120703_g1").html($("#a120703_g1").html());//重寫整個 html 後才有效
  });
  $("#a120703_b3").on("click",()=>
  {
    let i;
    let hh="";
    for (i=0;i<3;i++)
    {
      let o=document.createElementNS('http://www.w3.org/2000/svg', "rect");//建立一個 svg 命名空間的物件
      let id="a120703_h"+(yloc+i);
      o.setAttribute("id", id);//設定該物件的 ID
      $("#a120703_g1").append(o);//把物件 append 上去,因為是同一個命名空間,會立刻出現
      $("#"+id).attr("x",(10+i*40));//已經 append 後,可以用 jquery 操作
      $(o).attr("y",yloc);//也可以直接用物件的方式修改屬性
      $(o).attr("width",30);
      $(o).attr("height",30);
      $(o).attr("stroke","#00f");
      $(o).attr("fill","#a0f");
    }
    yloc+=40;
  });
});
</script>  


沒有留言:

張貼留言