svg要素の基本的な使い方まとめ

Written by defghi1977@xboxLIVE.この文書は全てテキストエディタで作成しています.えーと,そりゃもうゴリゴリと.

12.アニメーション

プロセッサ性能が向上した今日,アニメーション処理による動的なグラフィックは様々な場面で利用されている.svgにおいてもこのアニメーション処理を利用するための機構が定められており,ここではanimate要素を基本としたその使い方について述べる.ただ実用に耐えうるかについては現行ブラウザの対応状況を含め不透明と言わざるを得ず,利用する場合はその点を踏まえて慎重に導入したい.

アニメーションの構成要素

アニメーションを構成する要素としては次のようなものが考えられる.何れもアニメーションには欠かせない要素である.

従って,svgにおいてアニメーションを実現するにはこれらの情報を指定するための機構が必要となる.が,これらの要素はアニメーションに特有なものではなく,もっと一般のマルチメディア(動画,音声等)においても全く同様である.幸いこの用途においてマルチメディア記述言語SMIL(Synchronized Multimedia Integration Language・スマイル)が既に存在していたため,svgはこのsmilの機構を一部拡張した形で導入している.これらの機構を用いることで,svgにおいてアニメーションを実現することが可能となる.

※なお,smilから導入された要素は全てsvgに組み入れられているため,改めて名前空間の指定などの作業を行う必要はない.

svgにおけるアニメーションの実現

一般にアニメーションはグラフィックを時系列的に少しずつ変化させることで実現されている.svgもその例にもれず,図形要素のプロパティを操作することでアニメーションを構成する.その方法としてはaminate要素(smil)を用いるsvgDOM+javascriptを用いるcss3のアニメーション機構(transition,animate)を用いるが挙げられる.例を示す.

(1)animate要素を用いる (2)svgDOM+javascriptを用いる (3)css3のアニメーション機構(transition,animate)を用いる animate要素 javascript css3

これらの特徴について考えてみよう.

このようにそれぞれ得意分野を持っており,一概にこれが良いとは言えないが,個人的にはanimate要素とjavasciptとを組み合わせるのが最も効果的ではないかと思う.細かいアニメーションの部品をanimate要素で定義し,そのコントローラや足りない部分をjavascriptで実装するのだ.こうすることで面倒な低レベルの描画処理をいちいち考える必要が無くなり,スクリプトでの場面制御やイベント処理の記述に専念できるようになる.実際に時計を作る際,時計の針の動作をanimate要素で,正午からの経過時間の指定をjavascriptで行うようにしたところ,面倒なスクリプト記述が驚くほど少なくなった.

animate:属性の時系列的変化,set:属性の期限付き書き換え

animate要素によるアニメーションは,汎用的なマルチメディア記述言語smilをsvg向けに拡張したものとなっている.javascriptを用いたものほどの自由度はないが,簡潔に記述できる等のメリットも多い.

アニメーションに関わる要素としてはanimate要素set要素animateMotion要素animateColor要素animateTransform要素があり,用途に応じて使い分けることとなる.使い方はどれも同じで,アニメーションさせたい要素の子要素として配置するか,xlink:href属性で対象の要素のidを指定することで,対象の要素のプロパティを変更することができる.

animate要素は対象となる図形要素のプロパティ値の時系列的変化を表す.animateで変更可能な属性は,数値や色等の何らかの大きさを表すものである.連続的に変更できない場合はその変化は離散的(跳び跳び)になる.

set要素は期限を決めたプロパティ値の書き換えを表す.よってset要素で変更可能な属性はanimate要素のそれよりも多い.使い方はanimate要素と同じである.

xlink:href
アニメーションの対象.なければ親要素.
attributeName
アニメーション対象の属性名.座標,大きさ,角度,色等.href属性の場合は「xlink:href」を指定する.
attributeType
アニメーション対象の属性の種類
css
css属性
xml
xml属性
auto
自動(css属性→xml属性の順).

仕様上は様々な属性にanimate要素を充てがうことが出来ることとなっているが,いざ試してみると実装がそれに追いついていない印象がある.特にfilter要素に関わるアニメーション処理は動かないか,もしくはエラーとなることが多い.svg単体で動作させた場合とbackground-imageに指定した場合でも動作が異なるケースがある.また,設定可能な単位もブラウザ毎にまちまちであり,operaだと%設定が無効となる模様.確実に動作させたい場合は十分な検証を行う必要がある.

※safariでは環境によってはインラインsvgにおけるanimate要素が動作しないようです.この問題はスクリプトからbeginElementメソッドを実行することで回避することができます.

もうひとつ例を示す.苦労した割に報われなかったので供養も兼ねて掲載.一つ目はグラデーションにアニメーションを設定したもので,二つ目は一つ目を元に画像に波のような効果を与えるものである.二つ目はfirefoxでは動作せず,operaでのみ正しく動作する.chromeではアニメーションが停止してしまう場合がある.

firefoxで動作させる場合は1つめのsvgを外部ファイルに保存してからfeImageに参照させる.すると今度はoperaで動作が停止する不具合が発生してしまうのだ.このようにブラウザ間の微妙な違いにより動作したりしなかったりするので,一筋縄で行かない.場合によっては実現を諦める必要があるかもしれない.

アニメーションの種類と変化量

アニメーションの属性値の変化に関わる設定は次の通り.

from
値の初期値.
to
値の終了値.(set要素での設定値) by属性に優先する.
by
アニメーション終了時の増減値.(差分値)
values
セミコロン(;)区切りの値のリスト.主にkeyTimesと一緒に用いる.この値はfrom,to,byに優先する.
calcMode
値の変化の関数.
discrete
離散変化.次の値に一気に変化する.
linear
線形変化.次の値に(一定割合で)徐々に変化する.pacedとは変化の速度を折れ線に出来る点.
paced
定速(属性値がベクトル値の場合に有効).dur属性値の時間をかけてfrom値からto値に変化する.
spline
3次ベジェスプライン曲線による変化.values,keyTimes,keySplinesと一緒に用いる.
keyTimes
セミコロン(;)区切りの値の変化が起こるタイミングのリスト.アニメーション開始(0)から終了(1)までの割合で表す.
calcModeがlinear,splineの場合は先頭が0,末尾が1である必要がある.discreteの場合は先頭が0である必要がある.pacedの場合は無視される.
keySplines
keyTimesで区切られた各時区間における値の変化を3次ベジェスプライン曲線で表す.x1 y1 x2 y2の組をセミコロン(;)で区切って指定する.x1〜y2の値は0から1の間で指定する.

アニメーションの種類

この内,from,to,by,values属性はアニメーションの種類を定めるもので,属性の設定内容により次のパターンに分類することができる.

値の変化

calcMode属性には値変化のさせ方を指定する.言葉で説明しても判りにくいので実際にサンプルを書いてみた.discreteでは値がkeyTimesの前後で一気に変化しているのに対し,linearでは直線的に変化している.splineでは定義した区間ごとに滑らかなカーブを描いて値が変化している.

pacedの使い方サンプル discreteの使い方サンプル linearの使い方サンプル splineの使い方サンプル 区間1(0-0.5) 区間2(0.5-1)

keySplines属性に設定する値について

この中でsplineによるアニメーションは動きにメリハリをつけるために非常に重要であり,values属性・keySplines属性・keyTimes属性を組み合わせて利用する.属性に設定する値はセミコロン「;」区切りで指定する.属性間の関連を表すと次のようになる.

従ってkeySplines属性に指定する値の数は常にvalues属性及びkeyTimes属性に指定する値の数よりも1少なくなる.また,区間ごとの値の変化を図に表すと次のようになる.

値の変化を表すスプライン曲線は矩形[0, 1]×[0, 1](それぞれ,経過時間・値変化の割合に相当する)の範囲内で定める.なおcssにおいては特定の設定値に名称が付けられているので参考とすると良い.

ease
0.25 0.1 0.25 1.0
linear
0.0 0.0 1.0 1.0
ease-in
0.42 0 1.0 1.0
ease-out
0.0 0.0 0.58 1.0
ease-in-out
0.42 0.0 0.58 1.0
放物運動
0.33 0.0 0.67 0.33
※これは仕様で定義されていないが,憶えておくと便利なので掲載した.
ease linear ease-in ease-out ease-in-out 放物運動

複雑な形式のアニメーション

from値とto値の形式を合わせれば,複雑な引数の属性もアニメーションできる.ただし,全てのケースでアニメーション可能というわけではない.どの属性値がアニメーション可能かは仕様書に定義されているものの,ブラウザ毎に実装度合いが異なる.もしくはエラーで途中停止してしまう.

複雑な形式のアニメーション 複雑な形式のアニメーション

アニメーションのタイミングに関わる属性

アニメーションを実行するタイミング,終了するタイミングに関わる属性は次の通り.これらを組み合わせて,アニメーションの開始,継続,終了を制御していく.

begin
アニメーションを開始するタイミング.複数の値をセミコロンで設定可能.詳細な値は後述.
dur
アニメーションの持続時間
end
アニメーションを終了するタイミング.複数の値をセミコロンで設定可能.詳細な値は後述.
min
アニメーションの最短持続時間
max
アニメーションの最長持続時間
restart
アニメーションのリスタート可能条件.イベント処理時にアニメーションを中断して最初から動作させるかといった用途に用いる.
always
いつでも
whenNotActive
止まってたら
never
できない
repeatCount
アニメーションの繰り返し回数.回数かindefinite.repeatイベントが発生.
indefinite
未指定.(ずっと繰り返す)
repeatDur
アニメーションを繰り返す時間.時間指定かindefiniteを指定.
indefinite
未指定.(ずっと繰り返す)

begin,end属性に設定できる値は次の通り.自動実行の他,各種イベントに対応しているものの,ブラウザごとに実装状況が異なる. また,指定した値に「+1s」「-1s」と言った記述を追記することでアニメーションの実行タイミングをずらすことができる.プラス値を指定した場合はその分アニメーションの実行が遅延され,マイナス値を指定した場合はその分アニメーションが済んだものとして開始される.この仕様のため,他の要素を参照する場合,参照先の要素のid属性に「-」が含まれていると正しく動作しない.

offset-value
オフセット値
画像ロード時からの経過時間で指定する.0sで即時実行.+1sで1秒遅れて実行.-1sで1秒前から実行(されたことにして実行).なお,時間のオフセット値は任意の設定値に付け加えることが出来る.
syncbase-value
他要素を参照
他の要素のアニメーションの開始・終了イベントをトリガーにしてアニメーションを開始する.なお,書式にマイナスの記述があるため,参照先要素のIDに「-」が含まれていると正しく動作しない.「[要素のid].begin」,「[要素のid].end」で指定する.
event-value
イベント
何らかの要素のイベントをトリガとしてアニメーションを実行する.例ではclickイベントを使ってアニメーションを開始している.「[イベントを起こした要素のid].[イベント名]」で指定する.アニメーションの対象が自分自身であれば要素名を省略できる. click me!
repeat-value
リピート値
リピート回数によって見た目を変えたい場合に用いる.イベントはbegin→repeat(1)→repeat(2)→…→endの順に発生する.
accessKey-value
アクセスキー
キー入力をトリガーとしてアニメーションを実行する.
現状firefoxのみ先行実装.大文字と小文字は区別される.「&lt」;やenterキー等の制御文字についてはエスケープすることで指定する事ができる(例:enter…「
」).その他の矢印キー等の特殊キーについては不明.javascriptの機能を併用することとなるか.なお,svg自体にキー入力に関わるイベントの規定は現状存在しない.
この例ではキー入力の1秒前と指定しているので,キーを入力した時点で既に1秒分のアニメーションが完了したものとして動作している.
press a key!
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秒後の状態に再設定される.

anim_1を開始 anim_2の開始までスキップ anim_3の開始までスキップ anim_4の開始までスキップ

但し,現実的にはbegin属性によるアニメーションの開始タイミングは複雑な形をとることが多い上,この機能を正しく実装したブラウザは存在しない.従って思い通りのアニメーションシーク機構を実装するには不確定要素が多すぎるため,利用しない方が良い

※firefoxはこの機能を一部実装しているため,他のブラウザとは異なる結果となるケースがある.

アニメーション設定値の固定

fill属性ではアニメーション終了時の状態を制御することができる.

fill
アニメーション終了時に効果を初期値に戻すかどうか
freeze
戻さない
remove
戻す

実際に試してみよう.「click me!」をクリックするとアニメーションが開始する.fill=removeではアニメーション終了時に円の半径が0に戻っている.

fill=freeze fill=remove click me!

アニメーション設定値の取扱い

アニメーションで変化する属性の値の取扱いをadditive,accumulate属性で変更することが出来る.

additive
animateで設定する値の算出法を設定する.
sum
animateで設定する値(from,to値等)は図形の値に差分値として加算される.この設定を用いると単一の属性に複数のアニメーションを定義し,その値を足し合わせると言ったことが可能となる.
replace
絶対値として扱う.
accumulate
リピートした際に値を蓄積するかどうかを設定する.
sum
前回の結果に足しこんでいく.(累積)
none
リピートする都度値をリセットする.

animateMotion:モーションパスによる移動,mpath:パスの参照

基本的なアニメーションはanimate要素で可能だが,animateMotion要素を用いると図形を任意の曲線に沿って移動させることが出来る.主な方法はfrom,to,by,valuesで座標のリストを指定するパスを指定するの2つ.前者は描画位置の移動といった点でanimate要素とほぼ同じ感覚で利用できる.後者では図形を任意のパスに沿って動作させることが出来る.path属性を使って定義するかmpath要素を使って参照する.

calcMode
animate要素に同じ.だが,アニメーション対象が属性値ではなくpath上の位置である点が異なる.初期値はpaced.
keyTimes
animate要素に同じ.だが,アニメーション対象が属性値ではなくpath上の位置である点が異なる.
keySplines
animate要素に同じ.だが,アニメーション対象が属性値ではなくpath上の位置である点が異なる.
from,to,by,values
animate要素に同じ.x,yの組みをセミコロン区切りで指定する.
path
モーションパスを記述する.path要素のd属性と同じ記法を用いる.
keyPoints
曲線全体の長さにおける位置を0から1の間の割合で設定する.path属性を指定した場合に利用される.
xlink:href
モーションパスを参照する.(mpath専用)
rotate
図形の回転角を指定する.初期値は0°.
auto
自動
auto-reverse
自動(逆)

valuesを指定した場合とpathを指定した場合の違いについて示す.valuesの場合はその頂点とkeyTimesの値が対応しているため,辺毎の速度が変化している.

rotateを指定した例を示す.autoではpathを順方向に進んだ際に自然に見える.auto-reverseではその逆向き(180°回転)となる.

animateColor:色の時系列的変化

animateColor要素は図形の色のアニメーションを定義する.が,w3cの仕様にもある通り将来的に削除の方向にある.現状でもfirefoxにおいて無効であるため,色のアニメーションについてはanimate要素を用いると良い.

animateTransform:変形アニメーション

animateTransform要素では図形の変形アニメーションを定義する.transform属性・gradientTransform・patternTransform属性専用の要素であり,これらの属性は複数の変形関数を組み合わせることができるが,その関数毎に異なる値の変化を定義したい場合ケースがあり,そのための機能を提供する.

type
変形の種類.→transform.種類によって設定する値が変化する.
rotate,translate,skewX,skewY,scaleの何れかを設定する.※matrixは使えない.
種類により,valuesに指定する値の形式が変化するので注意.
回転

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要素を使う際に次のような規約を用いることを提案する.

  1. アニメーションの起点は1箇所とする.アニメーションの開始を表すanimate要素を1つ定義し,その他のanimate要素はこの要素のbeginイベントからのオフセット値で開始時刻を指定する.
  2. アニメーション対象の要素はxlink:href属性で指定する.
  3. アニメーション専用のdefs要素を定義し,その中にanimate要素をbegin属性の値の順に並べる.

つまり,図形の構造とアニメーション動作とを分離するのだ.このことによりアニメーション対象やアニメーションの時系列的な変化をコード上から容易に追うことが可能となる.先程の例をもう一度この規約に則って書き換えてみよう.

ずっとコードが見やすくなったかと思う.なおこの手法はあくまで原則論であり,実装したいアニメーションの内容により,より良い方法を模索して欲しい.

webkitにおけるsmilアニメーション

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の名前空間が指定されていないと正しく動作しない.)