にせねこメモ

はてなダイアリーがUTF-8じゃないので移ってきました。

SVGでフレームアニメーション w/ Javascript

前回の記事でSMILによるフレームアニメーションを実装したが、これだとIEやEdgeで動かないので、Javascriptを用いてアニメーションを実装しIEやEdgeでも動くようにした。

デモ

<img>タグによる読み込み

<img src="animate_js.svg" style="width:100%">

imgタグで埋め込むとJavascriptによるアニメーションは動かないようだ(Firefox, Chromeで確認)。

セキュリティ上の目的で、GekoはSVGコンテンツを画像として扱う場合にいくつかの制限を設けています。

SVG as an Image - SVG | MDN

<object>タグによる読み込み

<object type="image/svg+xml" data="animate_js.svg" width="100%"></object>

objectタグで埋め込むとアニメーションは動作するようだが、一方で単純に<a>タグで囲むだけなどではリンクを張ることができないので、工夫が必要である。
参考: SVGをobject要素で表示してリンクにする - ういはるかぜの化学 - subtech

ソース

<svg xmlns="http://www.w3.org/2000/svg" 
     xmlns:xlink="http://www.w3.org/1999/xlink"
     width="768" height="432" viewBox="0 0 764 432">
 <def>
  <image width="100%" height="100%" id="00" xlink:href="img/00.jpg"/>
  <image width="100%" height="100%" id="01" xlink:href="img/01.jpg"/>
  <image width="100%" height="100%" id="02" xlink:href="img/02.jpg"/>
  <image width="100%" height="100%" id="03" xlink:href="img/03.jpg"/>
  <image width="100%" height="100%" id="04" xlink:href="img/04.jpg"/>
  <image width="100%" height="100%" id="05" xlink:href="img/05.jpg"/>
  <image width="100%" height="100%" id="06" xlink:href="img/06.jpg"/>
  <image width="100%" height="100%" id="07" xlink:href="img/07.jpg"/>
  <image width="100%" height="100%" id="08" xlink:href="img/08.jpg"/>
  <image width="100%" height="100%" id="09" xlink:href="img/09.jpg"/>
  <image width="100%" height="100%" id="10" xlink:href="img/10.jpg"/>
  <image width="100%" height="100%" id="11" xlink:href="img/11.jpg"/>
  <image width="100%" height="100%" id="12" xlink:href="img/12.jpg"/>
  <image width="100%" height="100%" id="13" xlink:href="img/13.jpg"/>
  <image width="100%" height="100%" id="14" xlink:href="img/14.jpg"/>
  <image width="100%" height="100%" id="15" xlink:href="img/15.jpg"/>
 </def>
 <use id="frame" xlink:href="#00"/>
 <script type="text/javascript"><![CDATA[
  (function(){
    var c = document.getElementById("frame");
    var fcnt = 0;
    setInterval(function(){
        fcnt = (fcnt + 1) % 16;
        fid = fcnt < 10 ? "#0"+fcnt : "#"+fcnt;
        c.setAttribute("xlink:href", fid);
    },125)})();
 ]]></script>
</svg>

ここでは<image>要素でxlink:hrefにファイル名を指定しているが、代わりにdata URI schemeで画像ファイルを埋め込んで外部参照をなくした方が汎用性が上がると思う。

何をやっているか

  1. <def>内で<image>によって各フレームに対応するラスタ画像を読み込んでいて、連番のidを振ってある。
  2. <use>は他の要素をコピーして表示するというもので、初期フレームをxlink:hrefに指定している。このxlink:hrefをJavascriptで更新する。
  3. 次に<script>でアニメーションをするJavascriptを定義している。setIntervalにより125ms = 1/8s (… 8fps)ごとに実行される関数の中で、setAttributeを用いて先ほどの<use>要素のxlink:hrefを次のフレームのidになるように書き換えていく。

以上、結構シンプルになったと思う。

改良可能な点

  • imageのidを2桁で0パディングしているけれども、しなくてもいいかもしれない。各フレームに表示するidの計算が楽になる。
  • また、2桁なので泥臭く0パディングしているが、0パディングするにはslice()メソッドを使うと楽らしい。桁数が多くなってくるとこっちの方がいいかも。

まとめ