- SVGにはラスタ画像を埋め込める
- SVGではアニメーションが可能
なら、GIFアニメみたいなアニメーションも可能では?と思ってやってみた。
ソース
<svg xmlns="http://www.w3.org/2000/svg"
xmlnsxlink="http://www.w3.org/1999/xlink"
width="768" height="432" viewBox="0 0 764 432">
<image width="768" height="432" xlinkhref="img/00015.jpg">
<animate
attributeName="opacity"
values="0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1" dur="2s" calcMode="discrete"
begin="0s" repeatCount="indefinite" />
</image>
<image width="768" height="432" xlinkhref="img/00014.jpg">
<animate
attributeName="opacity"
values="0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;0" dur="2s" calcMode="discrete"
begin="0s" repeatCount="indefinite" />
</image>
<image width="768" height="432" xlinkhref="img/00013.jpg">
<animate
attributeName="opacity"
values="0;0;0;0;0;0;0;0;0;0;0;0;0;1;0;0" dur="2s" calcMode="discrete"
begin="0s" repeatCount="indefinite" />
</image>
<image width="768" height="432" xlinkhref="img/00012.jpg">
<animate
attributeName="opacity"
values="0;0;0;0;0;0;0;0;0;0;0;0;1;0;0;0" dur="2s" calcMode="discrete"
begin="0s" repeatCount="indefinite" />
</image>
<image width="768" height="432" xlinkhref="img/00011.jpg">
<animate
attributeName="opacity"
values="0;0;0;0;0;0;0;0;0;0;0;1;0;0;0;0" dur="2s" calcMode="discrete"
begin="0s" repeatCount="indefinite" />
</image>
<image width="768" height="432" xlinkhref="img/00010.jpg">
<animate
attributeName="opacity"
values="0;0;0;0;0;0;0;0;0;0;1;0;0;0;0;0" dur="2s" calcMode="discrete"
begin="0s" repeatCount="indefinite" />
</image>
<image width="768" height="432" xlinkhref="img/00009.jpg">
<animate
attributeName="opacity"
values="0;0;0;0;0;0;0;0;0;1;0;0;0;0;0;0" dur="2s" calcMode="discrete"
begin="0s" repeatCount="indefinite" />
</image>
<image width="768" height="432" xlinkhref="img/00008.jpg">
<animate
attributeName="opacity"
values="0;0;0;0;0;0;0;0;1;0;0;0;0;0;0;0" dur="2s" calcMode="discrete"
begin="0s" repeatCount="indefinite" />
</image>
<image width="768" height="432" xlinkhref="img/00007.jpg">
<animate
attributeName="opacity"
values="0;0;0;0;0;0;0;1;0;0;0;0;0;0;0;0" dur="2s" calcMode="discrete"
begin="0s" repeatCount="indefinite" />
</image>
<image width="768" height="432" xlinkhref="img/00006.jpg">
<animate
attributeName="opacity"
values="0;0;0;0;0;0;1;0;0;0;0;0;0;0;0;0" dur="2s" calcMode="discrete"
begin="0s" repeatCount="indefinite" />
</image>
<image width="768" height="432" xlinkhref="img/00005.jpg">
<animate
attributeName="opacity"
values="0;0;0;0;0;1;0;0;0;0;0;0;0;0;0;0" dur="2s" calcMode="discrete"
begin="0s" repeatCount="indefinite" />
</image>
<image width="768" height="432" xlinkhref="img/00004.jpg">
<animate
attributeName="opacity"
values="0;0;0;0;1;0;0;0;0;0;0;0;0;0;0;0" dur="2s" calcMode="discrete"
begin="0s" repeatCount="indefinite" />
</image>
<image width="768" height="432" xlinkhref="img/00003.jpg">
<animate
attributeName="opacity"
values="0;0;0;1;0;0;0;0;0;0;0;0;0;0;0;0" dur="2s" calcMode="discrete"
begin="0s" repeatCount="indefinite" />
</image>
<image width="768" height="432" xlinkhref="img/00002.jpg">
<animate
attributeName="opacity"
values="0;0;1;0;0;0;0;0;0;0;0;0;0;0;0;0" dur="2s" calcMode="discrete"
begin="0s" repeatCount="indefinite" />
</image>
<image width="768" height="432" xlinkhref="img/00001.jpg">
<animate
attributeName="opacity"
values="0;1;0;0;0;0;0;0;0;0;0;0;0;0;0;0" dur="2s" calcMode="discrete"
begin="0s" repeatCount="indefinite" />
</image>
<image width="768" height="432" xlinkhref="img/00000.jpg">
<animate
attributeName="opacity"
values="1;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0" dur="2s" calcMode="discrete"
begin="0s" repeatCount="indefinite" />
</image>
</svg>
もちろん<image>要素のxlink:href属性にdata URI schemeを突っ込めば画像ファイルを埋め込むことができる。
なお、HTMLから<img>タグなどで画像としてSVGを読み込む場合には、セキュリティ上の理由から外部参照が無視されるため、xlink:hrefにはdata URI schemeによって画像を埋め込まないといけない。(参考: SVG as an Image - SVG | MDN)
何をやってるのか?
<image>要素に1フレームの画像を読み込んでいる。これをアニメーションの全フレーム分用意する。
今回は8fpsで16フレームなので、全体の時間は2秒となる。
一つのフレームに注目して見ていく。
<image width="768" height="432" xlinkhref="img/00000.jpg">
<animate
attributeName="opacity"
values="1;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0" dur="2s" calcMode="discrete"
begin="0s" repeatCount="indefinite" />
</image>
これは16枚中の1番目に表示されるフレームに対応している。
- まず、アニメーションさせる対象として不透明度をattributeName="opacity"として指定している。
- アニメーションの時間は2秒なのでdur="2s"と指定している。
- valuesは、durで指定された時間を等分し、attributeNameで指定された対象の値とする。今回は16フレーム中の1番目のフレームということで、16個の数値がセミコロンで区切られ、1番目のみ1、その他が0になっている("1;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0")。1は表示、0が非表示に対応する。
- さらに、calcMode="discrete"を指定することで透明度が中間値にならずvaluesで指定した1か0、つまり表示・非表示の切り替えになる。
- begin="0s"ですぐに再生されるようにしておき、repeatCount="indefinite"で何度もループされるようにしておく。
フレームごとに異なるのは、valuesの値で、1がどの位置に来るかということである。2番目に表示されるフレームなら"0;1;0;0;0;0;0;0;0;0;0;0;0;0;0;0"、などなど。
後は同じ。
感想
- できるだけシンプルにしようとしたらこうなった。
- この実装では各画像を切り替えているだけであるため、<image>タグの順番は関係ない。
- …
- …
- GIFでいいじゃん!
20170331追記
同様のことを考えた先人が居た。
XNGファイルはSVGファイルに、データURIスキームでBase64エンコードされたJPEG画像をならべ、それをSVGアニメーションで送っているだけということがわかりました。
ついにGIFに代わる新しいアニメーション画像フォーマットが誕生か - GIGAZINE
これも同様にSVG SMIL animationを利用しているが、アニメーションの実現の方法が異なる。
フレームの若い順に幅0の<image>要素を重ねておき、<set>で各<image>要素について、前フレームのアニメーション終了時に開始するようにbegin属性を設定し(e.g. begin="A000310.end")、33msでwidthを100%にしているという実装だった。
SMILにはInternet ExplolerやEdgeは対応していない(し、する予定もなさそう)ため、画像は表示されないこととなる。
最初の一フレームは常に表示される設定にしておいた方が、アニメーションに対応していないビューアで見ても何らかの画像が表示されるため、よいかもしれない。
SVGでアニメーションをつくる利点?
時間軸方向の圧縮をしないため(Motion JPEGみたいな…)圧縮効率が悪いが、
- 複数要素を独立してアニメーションできる
- エフェクト(フィルタ)がかけられる
- クリッピングができる
など、SVGの機能をそのまま生かせるので、表現力は高いはず。簡単なアニメーション撮影もできそうである(ブラウザが対応してるか知らないが)。
20170331さらに追記
内の「画像の参照先を変える」にて、画像をつかってパラパラアニメーションを作る方法が紹介されている。
これでは、<def>内で画像を読み込み、<use>内で<animate>のvalues属性にidの参照をセミコロン;で区切って指定している。これが一番シンプルで分かりやすいかもしれない。