プロセッサ性能が向上した今日,アニメーション処理による動的なグラフィックは様々な場面で利用されている.svgにおいてもこのアニメーション処理を利用するための機構が定められており,ここではanimate要素を基本としたその使い方について述べる.ただ実用に耐えうるかについては現行ブラウザの対応状況を含め不透明と言わざるを得ず,利用する場合はその点を踏まえて慎重に導入したい.
プロセッサ性能が向上した今日,アニメーション処理による動的なグラフィックは様々な場面で利用されている.svgにおいてもこのアニメーション処理を利用するための機構が定められており,ここではanimate要素を基本としたその使い方について述べる.ただ実用に耐えうるかについては現行ブラウザの対応状況を含め不透明と言わざるを得ず,利用する場合はその点を踏まえて慎重に導入したい.
アニメーションを構成する要素としては次のようなものが考えられる.何れもアニメーションには欠かせない要素である.
従って,svgにおいてアニメーションを実現するにはこれらの情報を指定するための機構が必要となる.が,これらの要素はアニメーションに特有なものではなく,もっと一般のマルチメディア(動画,音声等)においても全く同様である.幸いこの用途においてマルチメディア記述言語SMIL(Synchronized Multimedia Integration Language・スマイル)が既に存在していたため,svgはこのsmilの機構を一部拡張した形で導入している.これらの機構を用いることで,svgにおいてアニメーションを実現することが可能となる.
※なお,smilから導入された要素は全てsvgに組み入れられているため,改めて名前空間の指定などの作業を行う必要はない.
一般にアニメーションはグラフィックを時系列的に少しずつ変化させることで実現されている.svgもその例にもれず,図形要素のプロパティを操作することでアニメーションを構成する.その方法としてはaminate要素(smil)を用いるsvgDOM+javascriptを用いるcss3のアニメーション機構(transition,animate)を用いるが挙げられる.例を示す.
これらの特徴について考えてみよう.
このようにそれぞれ得意分野を持っており,一概にこれが良いとは言えないが,個人的にはanimate要素とjavasciptとを組み合わせるのが最も効果的ではないかと思う.細かいアニメーションの部品をanimate要素で定義し,そのコントローラや足りない部分をjavascriptで実装するのだ.こうすることで面倒な低レベルの描画処理をいちいち考える必要が無くなり,スクリプトでの場面制御やイベント処理の記述に専念できるようになる.実際に時計を作る際,時計の針の動作をanimate要素で,正午からの経過時間の指定をjavascriptで行うようにしたところ,面倒なスクリプト記述が驚くほど少なくなった.
animate要素によるアニメーションは,汎用的なマルチメディア記述言語smilをsvg向けに拡張したものとなっている.javascriptを用いたものほどの自由度はないが,簡潔に記述できる等のメリットも多い.
アニメーションに関わる要素としてはanimate要素set要素animateMotion要素animateColor要素animateTransform要素があり,用途に応じて使い分けることとなる.使い方はどれも同じで,アニメーションさせたい要素の子要素として配置するか,xlink:href属性で対象の要素のidを指定することで,対象の要素のプロパティを変更することができる.
animate要素は対象となる図形要素のプロパティ値の時系列的変化を表す.animateで変更可能な属性は,数値や色等の何らかの大きさを表すものである.連続的に変更できない場合はその変化は離散的(跳び跳び)になる.
set要素は期限を決めたプロパティ値の書き換えを表す.よってset要素で変更可能な属性はanimate要素のそれよりも多い.使い方はanimate要素と同じである.
仕様上は様々な属性にanimate要素を充てがうことが出来ることとなっているが,いざ試してみると実装がそれに追いついていない印象がある.特にfilter要素に関わるアニメーション処理は動かないか,もしくはエラーとなることが多い.svg単体で動作させた場合とbackground-imageに指定した場合でも動作が異なるケースがある.また,設定可能な単位もブラウザ毎にまちまちであり,operaだと%設定が無効となる模様.確実に動作させたい場合は十分な検証を行う必要がある.
※safariでは環境によってはインラインsvgにおけるanimate要素が動作しないようです.この問題はスクリプトからbeginElementメソッドを実行することで回避することができます.
もうひとつ例を示す.苦労した割に報われなかったので供養も兼ねて掲載.一つ目はグラデーションにアニメーションを設定したもので,二つ目は一つ目を元に画像に波のような効果を与えるものである.二つ目はfirefoxでは動作せず,operaでのみ正しく動作する.chromeではアニメーションが停止してしまう場合がある.
firefoxで動作させる場合は1つめのsvgを外部ファイルに保存してからfeImageに参照させる.すると今度はoperaで動作が停止する不具合が発生してしまうのだ.このようにブラウザ間の微妙な違いにより動作したりしなかったりするので,一筋縄で行かない.場合によっては実現を諦める必要があるかもしれない.
アニメーションの属性値の変化に関わる設定は次の通り.
この内,from,to,by,values属性はアニメーションの種類を定めるもので,属性の設定内容により次のパターンに分類することができる.
calcMode属性には値変化のさせ方を指定する.言葉で説明しても判りにくいので実際にサンプルを書いてみた.discreteでは値がkeyTimesの前後で一気に変化しているのに対し,linearでは直線的に変化している.splineでは定義した区間ごとに滑らかなカーブを描いて値が変化している.
この中でsplineによるアニメーションは動きにメリハリをつけるために非常に重要であり,values属性・keySplines属性・keyTimes属性を組み合わせて利用する.属性に設定する値はセミコロン「;」区切りで指定する.属性間の関連を表すと次のようになる.
従ってkeySplines属性に指定する値の数は常にvalues属性及びkeyTimes属性に指定する値の数よりも1少なくなる.また,区間ごとの値の変化を図に表すと次のようになる.
値の変化を表すスプライン曲線は矩形[0, 1]×[0, 1](それぞれ,経過時間・値変化の割合に相当する)の範囲内で定める.なおcssにおいては特定の設定値に名称が付けられているので参考とすると良い.
from値とto値の形式を合わせれば,複雑な引数の属性もアニメーションできる.ただし,全てのケースでアニメーション可能というわけではない.どの属性値がアニメーション可能かは仕様書に定義されているものの,ブラウザ毎に実装度合いが異なる.もしくはエラーで途中停止してしまう.
アニメーションを実行するタイミング,終了するタイミングに関わる属性は次の通り.これらを組み合わせて,アニメーションの開始,継続,終了を制御していく.
begin,end属性に設定できる値は次の通り.自動実行の他,各種イベントに対応しているものの,ブラウザごとに実装状況が異なる. また,指定した値に「+1s」「-1s」と言った記述を追記することでアニメーションの実行タイミングをずらすことができる.プラス値を指定した場合はその分アニメーションの実行が遅延され,マイナス値を指定した場合はその分アニメーションが済んだものとして開始される.この仕様のため,他の要素を参照する場合,参照先の要素のid属性に「-」が含まれていると正しく動作しない.
offset-value オフセット値 |
画像ロード時からの経過時間で指定する.0sで即時実行.+1sで1秒遅れて実行.-1sで1秒前から実行(されたことにして実行).なお,時間のオフセット値は任意の設定値に付け加えることが出来る. | |
syncbase-value 他要素を参照 |
他の要素のアニメーションの開始・終了イベントをトリガーにしてアニメーションを開始する.なお,書式にマイナスの記述があるため,参照先要素のIDに「-」が含まれていると正しく動作しない.「[要素のid].begin」,「[要素のid].end」で指定する. | |
event-value イベント |
何らかの要素のイベントをトリガとしてアニメーションを実行する.例ではclickイベントを使ってアニメーションを開始している.「[イベントを起こした要素のid].[イベント名]」で指定する.アニメーションの対象が自分自身であれば要素名を省略できる. | |
repeat-value リピート値 |
リピート回数によって見た目を変えたい場合に用いる.イベントはbegin→repeat(1)→repeat(2)→…→endの順に発生する. | |
accessKey-value アクセスキー |
キー入力をトリガーとしてアニメーションを実行する. 現状firefoxのみ先行実装.大文字と小文字は区別される.「<」;やenterキー等の制御文字についてはエスケープすることで指定する事ができる(例:enter…「
」).その他の矢印キー等の特殊キーについては不明.javascriptの機能を併用することとなるか.なお,svg自体にキー入力に関わるイベントの規定は現状存在しない. この例ではキー入力の1秒前と指定しているので,キーを入力した時点で既に1秒分のアニメーションが完了したものとして動作している. |
|
wallclock-sync-value 時刻指定 |
日付時刻を指定するもの.記述の形式としてISO8601を用いる.動作するブラウザが存在しない模様. | |
indefinite | そのままではアニメーションは実行されず,アニメーション要素へのハイパーリンク呼び出し,もしくはdomインターフェースからbeginElementメソッドを実行することでアニメーションが開始される.animate_indefiniteを起動. |
begin,dur,end属性その他に設定する時間の記述方法は次の通り.
※本項目は使うべきでない機能について解説しています.
ハイパーリンクでのジャンプ先がanimate要素だった場合,対象のアニメーションが実行されるが,その際のa要素はアニメーション時間軸に対するハイパーリンクの意味合いとなる.つまりsvgグラフィックがレンダリングされ,リンク先のanimate要素の実行開始時刻が確定している場合,リンクを実行することでアニメーション全体がその時刻までスキップされる.仕様はこちら
最も単純な例を示す.この例では4つのanimate要素が数珠つなぎで実行されるが,ここでanim_4はsvgの読み込みの6秒後に実行されることがわかる.従ってanim_4へのリンクが実行されるとアニメーション全体が6秒後のタイミングにスキップされる.するとanim_4が実行される前に既に開始されているanim_1〜3も6秒後の状態に再設定される.
但し,現実的にはbegin属性によるアニメーションの開始タイミングは複雑な形をとることが多い上,この機能を正しく実装したブラウザは存在しない.従って思い通りのアニメーションシーク機構を実装するには不確定要素が多すぎるため,利用しない方が良い.
※firefoxはこの機能を一部実装しているため,他のブラウザとは異なる結果となるケースがある.
fill属性ではアニメーション終了時の状態を制御することができる.
実際に試してみよう.「click me!」をクリックするとアニメーションが開始する.fill=removeではアニメーション終了時に円の半径が0に戻っている.
アニメーションで変化する属性の値の取扱いをadditive,accumulate属性で変更することが出来る.
基本的なアニメーションはanimate要素で可能だが,animateMotion要素を用いると図形を任意の曲線に沿って移動させることが出来る.主な方法はfrom,to,by,valuesで座標のリストを指定するパスを指定するの2つ.前者は描画位置の移動といった点でanimate要素とほぼ同じ感覚で利用できる.後者では図形を任意のパスに沿って動作させることが出来る.path属性を使って定義するかmpath要素を使って参照する.
valuesを指定した場合とpathを指定した場合の違いについて示す.valuesの場合はその頂点とkeyTimesの値が対応しているため,辺毎の速度が変化している.
rotateを指定した例を示す.autoではpathを順方向に進んだ際に自然に見える.auto-reverseではその逆向き(180°回転)となる.
animateColor要素は図形の色のアニメーションを定義する.が,w3cの仕様にもある通り将来的に削除の方向にある.現状でもfirefoxにおいて無効であるため,色のアニメーションについてはanimate要素を用いると良い.
animateTransform要素では図形の変形アニメーションを定義する.transform属性・gradientTransform・patternTransform属性専用の要素であり,これらの属性は複数の変形関数を組み合わせることができるが,その関数毎に異なる値の変化を定義したい場合ケースがあり,そのための機能を提供する.
transform効果を複数掛けたい場合は,additive="sum"を指定する.位置を固定して拡大・縮小する場合に利用する.
単に変形の基準を変えたいだけなら,g要素と組み合わせたほうが判りやすい.なお,g要素のtransform属性とanimateTransform要素との適用順がブラウザ毎に異なるので,若干面倒だがg要素を複数重ねて変換の適用順を明確化すると良い.
animateTransform要素はlinearGradient要素,radialGradient要素,patternにも適用可能だ.この場合attributeNameにgradientTransform/patternTransformを指定することで,塗り潰しの変形をアニメーション化することができる.(chromeでは動作しない)
text要素などの文字列にアニメーションを施した際,動きがぎこちなくなるケースがある.これはフォントによる文字の描画の背後においてブラウザによる微調整が行われることにより発生している.この問題は後述するtext-rendering属性にgeometricPrecision(幾何精度優先)を指定することで回避できる場合がある.
smilを使ってパラパラアニメーションを作る場合などにおいて,image要素のxlink:href属性を切り替えたい場合がある.例えば次の画像群をパラパラアニメーション化する場合,素朴に考えると次のようなコードとなるだろう.
<svg xmlns:xlink="http://www.w3.org/1999/xlink" >
<image width="100%" height="100%">
<animate attributeName="xlink:href" begin="0s" dur="0.5s" repeatCount="indefinite"
values="imageAnim/f1.png;imageAnim/f2.png;imageAnim/f3.png;imageAnim/f4.png;imageAnim/f5.png"
/>
</image>
</svg>
しかしこの方法では,アニメーション開始時にはまだ画像が読み込まれておらず,ブラウザによっては動作が非常に不安定となってしまう.またchromeではこのコードは動作しない.この問題は一旦全ての画像をimage要素で読み込んだ後,use要素を使って描画内容を変更することで対処できる.
この例ではアニメーション開始前にimage要素を使って画像を読み込むこととなるため,非常に動作が安定する.
※なおchromeではインラインsvgにおいてxlink:href属性のアニメーションを行う場合,この例のようにxlinkの名前空間文字列の指定が必要となる.
本項ではsvgアニメーションを実装する際のテクニック,注意事項について述べる.
単純なアニメーションを使いまわす方法としては,予め図形を定義しておき,use要素で複製するといった方法が挙げられる.アニメーションのタイミングを変更することは出来なくなるが,面倒なanimate要素を記述する必要が無くなるためコードを短く保つことができる.
この方法を取る場合は必ずdef要素で定義したテンプレート図形要素の配下にanimate要素を記述するようにする.xlink:href属性により外部からアニメーションを施した場合,困ったことにブラウザ毎に動作が異なるのだ.これはuse要素がどこまでツリー構造を複製するかについての実装の違いによるものと考えられる.例を示す.firefoxでは図形が回転しないことが確認できるだろう.
ここまでは単一の要素に対するアニメーションを見てきたが,実際には複数の要素を同時にアニメーションさせて豊かな表現をしたくなるところだろう.しかし,smilによるアニメーションは宣言的に指定するため,javascriptを始めとした手続き型の言語と比べ自由度がありすぎる.よって闇雲にanimate要素を文書に挿入してしまうと非常にメンテナンスが難しいものとなってしまう.例を示す.
ソースコードからどのようなアニメーションがなされるか読み取れただろうか?どの要素がどのタイミングで動き出すのかがコード全体に分散してしまっていて,解りにくかったのではないだろうか.このようにanimate要素が入り乱れてくると途端にコードの可読性が損なわれてしまう.また,子要素としてanimate要素を配置する方法は作業分担の面でも面倒である.というのも,一般的なドローイングツールにおいてはまだanimate要素の出力に対応していないため,いちいち手作業で修正しなければならない.従って何らかの手順を踏んだアニメーション設計を行うことが望ましいと考えられる.
そこで筆者はanimete要素を使う際に次のような規約を用いることを提案する.
つまり,図形の構造とアニメーション動作とを分離するのだ.このことによりアニメーション対象やアニメーションの時系列的な変化をコード上から容易に追うことが可能となる.先程の例をもう一度この規約に則って書き換えてみよう.
ずっとコードが見やすくなったかと思う.なおこの手法はあくまで原則論であり,実装したいアニメーションの内容により,より良い方法を模索して欲しい.
chromeやsafari等のレンダリングエンジンにwebkitを採用しているブラウザではsmilの動作が非常に不安定で(特にインラインsvgで顕著),実装の方法によっては次第にcpuリソースを食いつぶしてしまうなど深刻な問題を孕んでいる.この問題を根本的に解決するには本来webkit側での対応が望ましいのだが,いつまで経っても修正される気配が無い.
従って利用者側で何らかの対策を練る必要があるのだが,筆者の経験上クロスブラウザでの動作を目指すのであれば,アニメーション設計を行う前に次の点に注意すると良い.
†イベント周りのリソースの開放が上手く動作しておらず,cpuリソースを食いつぶすと言った現象を引き起こす.
もちろんこれ以外にもwebkit特有の問題は多く,バージョンが上がる毎に新たに動作しないパターンが発生するなどの課題もある.よってieがsmilをサポートしないことから,そもそもsmilを利用しないと言った選択も視野に入れておく必要があろう.
このようにwebkit系の環境ではsmileにおける制約が余りに多く,凝ったアニメーションを実現することが非常に困難である.従って敢えてsmilを選択するのであれば不足した機能をスクリプトで補わねばならない.
これらの点に注意して先ほどのアニメーションを書き換えると次のようになる.これで一応ieを除いてクロスブラウザ動作となるが,アニメーションが安定するまで暫く時間が掛かるなど課題も多く,挙動を統一するのは一筋縄では行かない.
webkit系ではgradientTransform,patternTransform属性に対するanimateTransform要素の適用が出来ない.この問題はpattern要素を挿入することで回避できる.
先ほどの例をchromeでも動作するように書き換えると次のようになる.
グラデーション・パターンを直接参照するのではなく,中間pattern要素を用意し間接的に参照するようにする.中間pattern要素内部の図形要素におけるtransform属性はアニメーション化可能であるため,この図形を変形することで擬似的にグラデーション・パターンが変形されているように見せかけるのだ.
この方法では座標系の選択(userSpaceOnUse/objectBoundingBox)の選択がややこしい他,変形に伴う空白部が出来ないように予め細工をしておく必要があるなど,後々のメンテナンスが非常に困難である点に注意しよう.
webkitではurlを伴う内容をアニメーション化することが出来ないが,先ほどと同様の手法で対処可能である.use要素のxlink:href属性がアニメーション化可能であることから,一旦pattern要素を挟んでその中のuse要素の参照先を変更することで対処する.(なおその際,xlinkの名前空間が指定されていないと正しく動作しない.)