2012年8月5日日曜日

svgアニメーションを分かりやすくプログラムするには

svgにおけるアニメーションはjavascriptを用いる方法の他に,smilを用いた方法があります.smilすなわちanimate要素を用いたアニメーションは宣言型の書式を取るため,一般的なプログラムとは使い勝手が異なり,ともすると制御が非常に面倒となる可能性があります.本記事ではこのアニメーションを分かりやすくプログラムするためのコツについて示します.

※追記:chromeでは余りにも動作が怪しいため,別途対処法を考えないとダメなようです.現在調査中です.(というより発展途中ということも考えられる)

アニメーションで最も重要な要素はタイムラインです.従ってこのタイムラインに沿ってanimate要素を記述するのが最も判りやすいのではないかと思います.つまり,svgによるアニメーションを作る場合は次のような手順に則って行います.
  1. タイムラインを考え,アニメーション開始から何秒後にどの要素がどのような動作をするのかを考えます.
  2. svgに図形要素を記述します.この時アニメーションを行う要素にはidを振っておきます.これは後でanimate要素からアニメーション対象の要素を見つけ出すために設定します.
  3. アニメーションの開始の役割をもつanimate要素を定義します.この要素自体はアニメーション処理を一切行いません.これは他のanimate要素に処理の開始を伝えるためだけに用意したものです.
  4. animate要素にアニメーションの内容を記述していきます.その際,タイムラインの順にanimate要素を並べていきます.その際のbegin属性には,3で定義した要素のbeginイベントを登録し,実際のアニメーション開始時刻をそこからのオフセット値で指定します.こうすることでアニメーション開始から何秒後にどうのような動作が行われるかが分かりやすくなります.
実際に例を作って見ました.



非常に複雑な処理を行なっていますが,アニメーション開始部分を一つにまとめ,animate要素が一箇所にまとまっているため,非常に見やすくなっていると思います.

<svg width="400" height="400" viewBox="-250 -250 500 500">
    
    <!--サブルーチン-->
 <defs>
     <polygon id="hexagon" points="100,173 -100,173 -200,0 -100,-173 100,-173 200,0"/>
        <mask id="lineMask" maskUnits="userSpaceOnUse" x="-250" y="-250" width="500" height="500">
            <use xlink:href="#hexagon" stroke-width="30" stroke="white"/>
        </mask>
        <line id="line" x1="100" y1="173" x2="-100" y2="173"/>
    </defs>
    
    <!--図形要素-->
    <g stroke-width="30" stroke="red" id="main">
        <use xlink:href="#hexagon" stroke="pink" stroke-width="60" fill="hotpink"/>
        <g mask="url(#lineMask)">
            <line id="line1" x1="130" y1="173" x2="130" y2="173" transform="rotate(0)"/>
            <line id="line2" x1="130" y1="173" x2="130" y2="173" transform="rotate(120)"/>
            <line id="line3" x1="130" y1="173" x2="130" y2="173" transform="rotate(240)"/>
            
            <g transform="rotate(60)">
                <line id="line4" mask="url(#lineMask)" x1="130" y1="173" x2="130" y2="173"/>
            </g>
            <g transform="rotate(180)">
                <line id="line5" mask="url(#lineMask)" x1="130" y1="173" x2="130" y2="173"/>
            </g>
            <g transform="rotate(300)">
                <line id="line6" mask="url(#lineMask)" x1="130" y1="173" x2="130" y2="173"/>
            </g>
        </g>
    </g>
    
    <!--アニメーションプログラム-->
    <defs>
        <!--アニメーション開始イベントを発生-->
  <animate id="starter" begin="0s;starter.end" dur="14s"/>
     <!--step1-->
        <animate xlink:href="#line1" begin="starter.begin + 0s" 
         attributeName="x1" from="130" to="-130" dur="2s" fill="freeze"/>
        <animate xlink:href="#line2" begin="starter.begin + 2s" 
         attributeName="x1" from="130" to="-130" dur="2s" fill="freeze"/>
        <animate xlink:href="#line3" begin="starter.begin + 4s" 
         attributeName="x1" from="130" to="-130" dur="2s" fill="freeze"/>
        <set     xlink:href="#line1" begin="starter.begin + 6s" 
         attributeName="x1" to="130"/>
        <set     xlink:href="#line2" begin="starter.begin + 6s" 
         attributeName="x1" to="130"/>
        <set     xlink:href="#line3" begin="starter.begin + 6s" 
         attributeName="x1" to="130"/>
        <!--step2-->
        <animate xlink:href="#line4" begin="starter.begin + 6s" 
         attributeName="x1" from="130" to="-130" dur="2s" fill="freeze"/>
        <animate xlink:href="#line5" begin="starter.begin + 6s" 
         attributeName="x1" from="130" to="-130" dur="2s" fill="freeze"/>
        <animate xlink:href="#line6" begin="starter.begin + 6s" 
         attributeName="x1" from="130" to="-130" dur="2s" fill="freeze"/>
        <!--step3-->
        <animateTransform xlink:href="#main"  begin="starter.begin + 8s"  
         attributeName="transform" type="rotate" accumulate="sum" dur="2s" from="0,0,0" to="60,0,0" repeatCount="2"/>
        <!--step4-->
        <animateTransform xlink:href="#line4" begin="starter.begin + 12s" 
         attributeName="transform" type="translate" dur="2s" to="0,15"/>
        <animateTransform xlink:href="#line5" begin="starter.begin + 12s" 
         attributeName="transform" type="translate" dur="2s" to="0,15"/>
        <animateTransform xlink:href="#line6" begin="starter.begin + 12s" 
         attributeName="transform" type="translate" dur="2s" to="0,15"/>
        <animate xlink:href="#line4" begin="starter.begin + 12s" 
         attributeName="stroke-opacity" from="1" to="0" dur="2s"/>
        <animate xlink:href="#line5" begin="starter.begin + 12s" 
         attributeName="stroke-opacity" from="1" to="0" dur="2s"/>
        <animate xlink:href="#line6" begin="starter.begin + 12s" 
         attributeName="stroke-opacity" from="1" to="0" dur="2s"/>
        <set     xlink:href="#line4" begin="starter.begin + 14s" 
         attributeName="x1" to="130"/>
        <set     xlink:href="#line5" begin="starter.begin + 14s" 
         attributeName="x1" to="130"/>
        <set     xlink:href="#line6" begin="starter.begin + 14s" 
         attributeName="x1" to="130"/>
    </defs>
</svg>

0 件のコメント:

コメントを投稿