なら、GIFアニメみたいなアニメーションも可能では?と思ってやってみた。
作成したもの
アニメーションは使いまわし: 【けものフレンズ】「ネコ科のフレンズ」イラスト/にせねこ [pixiv]
はてなブログにsvgタグをコピペしようとしたら、文字数が多すぎると怒られた(data URI schemeを利用して200万文字を超えていた)。
ソース
<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"> <image width="768" height="432" xlink:href="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" xlink:href="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" xlink:href="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" xlink:href="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" xlink:href="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" xlink:href="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" xlink:href="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" xlink:href="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" xlink:href="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" xlink:href="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" xlink:href="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" xlink:href="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" xlink:href="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" xlink:href="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" xlink:href="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" xlink:href="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" xlink:href="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は対応していない(し、する予定もなさそう)ため、画像は表示されないこととなる。
最初の一フレームは常に表示される設定にしておいた方が、アニメーションに対応していないビューアで見ても何らかの画像が表示されるため、よいかもしれない。
20170331さらに追記
内の「画像の参照先を変える」にて、画像をつかってパラパラアニメーションを作る方法が紹介されている。
これでは、<def>内で画像を読み込み、<use>内で<animate>のvalues属性にidの参照をセミコロン;で区切って指定している。これが一番シンプルで分かりやすいかもしれない。