SVG アニメーションの練習結果

Category : SVG

SVG アニメーションの練習をしていたので、後から見直してなぜこうなったのか分かるように記録しておきます。

<!--
  アニメーションしたときに消えてしまわないように
  overflow="visible" を指定している
-->
<svg viewBox="0 0 512 512" overflow="visible" class="hex">

  <!-- 同じ図形を使いまわすので defs 要素内で定義しておく -->
  <defs>

    <!--
      アニメーションに width を使いたいので
      symbol 要素で新しい viewBox を作成する
    -->
    <symbol id="symbolTrapezoid" fill="#ff7a7a">
      <!-- 各辺にある台形 -->
      <polygon id="trapezoid"
               points="140.8125,56.46875 128.03125,78.625 383.96875,78.625 371.1875,56.46875"/>
    </symbol>

    <!-- 六角形 -->
    <polygon id="hexagon"
             points="0,256 128,34.297497 384,34.297497 512,256 384,477.7025 128,477.7025"/>
  </defs>

  <!--
    図形全体を入れるグループ
    図形自体を回転させるアニメーションはここに指定する
  -->
  <g id="animateHexagon">

    <!-- アニメーションが1周したら初期状態に戻す -->
    <set id="resetRotate"
         attributeName="transform"
         attributeType="rotate"
         to="0, 256, 256"
         begin="moveUpTop.end"/>

    <!-- 最初の回転(120度) -->
    <animateTransform id="rotate120"
                      attributeName="transform" type="rotate"
                      from="0, 256, 256" to="120, 256, 256"
                      begin="showTop.end + 0.5s" dur="0.3s"
                      repeatCount="1" fill="freeze"
                      calcMode="spline" keyTimes="0;1"
                      keySplines="0.6 0 0 0.4"/>

    <!-- 2回目の回転(120度から240度へ) -->
    <animateTransform id="rotate240"
                      attributeName="transform" type="rotate"
                      from="120, 256, 256" to="240, 256, 256"
                      begin="rotate120.end + 0.5s" dur="0.3s"
                      repeatCount="1" fill="freeze"
                      calcMode="spline" keyTimes="0;1"
                      keySplines="0.6 0 0 0.4"/>

    <!-- 内側と外側の六角形のグループ -->
    <g id="hexagonGroup">
      <use xlink:href="#hexagon" fill="#ffcccc"/>
      <!-- 内側は縮小する -->
      <use xlink:href="#hexagon" fill="#ffa3a3"
           transform="translate(256, 256) scale(0.7) translate(-256, -256)"/>
    </g>

    <!-- 各辺にある台形のグループ -->
    <g id="trapezoidGroup" class="trapezoid">

      <!-- 左上辺(アニメーションの起点) -->
      <use xlink:href="#symbolTrapezoid" width="0"
           transform="rotate(300, 256, 256)">

        <!--
          アニメーションスタート
          表示直後とアニメーションが1周してから1秒後にセット
        -->
        <animate id="showLeftTop"
                 attributeName="width"
                 from="128.03125" to="383.96875"
                 begin="0s;moveUpTop.end + 1s" dur="0.5s"
                 repeatCount="1" calcMode="paced"/>

        <!-- 下辺の表示アニメーションが終了したらフェードアウト -->
        <animate id="fadeOutLeftTop"
                 attributeName="fill-opacity"
                 from="1" to="0"
                 begin="showBottom.end" dur="0.1s"
                 repeatCount="1" calcMode="paced"/>

        <!-- 下辺の表示アニメーションが終了するまで width をキープ -->
        <set id="keepWidthLeftTop"
             attributeName="width"
             to="383.96875"
             begin="showLeftTop.end" end="fadeOutLeftTop.end"/>

        <!-- フェードアウトしてから2周目に入るまで透明状態をキープ -->
        <set id="keepOpacityLeftTop"
             attributeName="fill-opacity"
             to="0"
             begin="fadeOutLeftTop.end" end="showLeftTop.begin"/>
      </use>

      <!--
        上辺
        左上辺・右上辺・下辺のフェードアウトが終わったら表示する
      -->
      <use xlink:href="#symbolTrapezoid" width="0">

        <!-- 下辺のフェードアウトが終了したらアニメーション開始 -->
        <animate id="showTop"
                 attributeName="width"
                 from="128.03125" to="383.96875"
                 begin="fadeOutBottom.end" dur="0.5s"
                 repeatCount="1" calcMode="paced"/>

        <!-- 図形全体が2度回転し終わったらフェードアウト -->
        <animate id="fadeOutTop"
                 attributeName="fill-opacity"
                 from="1" to="0"
                 begin="rotate240.end + 0.5s" dur="0.1s"
                 repeatCount="1" calcMode="paced"/>

        <!-- フェードアウトと同時に上方向(図形の外方向)に向かって移動 -->
        <animateTransform id="moveUpTop"
                          attributeName="transform" type="translate"
                          additive="sum"
                          from="0, 0" to="0, -50"
                          begin="rotate240.end + 0.5s" dur="0.1s"
                          repeatCount="1" calcMode="paced"/>

        <!-- 2回目の上辺フェードアウトが終了するまで width をキープ -->
        <set id="keepWidthTop"
             attributeName="width"
             to="383.96875"
             begin="showTop.end" end="fadeOutTop.end"/>

        <!-- 2回目の上辺フェードアウトが終了するまで透明状態をキープ -->
        <set id="keepOpacityTop"
             attributeName="fill-opacity"
             to="0"
             begin="fadeOutTop.end" end="showTop.begin"/>

        <!-- 2回目の上辺フェードアウトが終了したら位置を元に戻す -->
        <set id="resetTranslateTop"
             attributeName="transform"
             attributeType="translate"
             to="0, 0"
             begin="fadeOutTop.end"
             end="showTop.begin"/>
      </use>

      <!-- 右上辺(基本的には左上辺と同じ) -->
      <use xlink:href="#symbolTrapezoid" width="0"
           transform="rotate(60, 256, 256)">
        <animate id="showRightTop"
                 attributeName="width"
                 from="128.03125" to="383.96875"
                 begin="showLeftTop.end" dur="0.5s"
                 repeatCount="1" calcMode="paced"/>
        <animate id="fadeOutRightTop"
                 attributeName="fill-opacity"
                 from="1" to="0"
                 begin="showBottom.end" dur="0.1s"
                 repeatCount="1" calcMode="paced"/>
        <set id="keepWidthRightTop"
             attributeName="width"
             to="383.96875"
             begin="showRightTop.end" end="fadeOutRightTop.end"/>
        <set id="keepOpacityRightTop"
             attributeName="fill-opacity"
             to="0"
             begin="fadeOutRightTop.end" end="showRightTop.begin"/>
      </use>

      <!-- 右下辺(基本的には上辺と同じ) -->
      <use xlink:href="#symbolTrapezoid" width="0"
           transform="rotate(120, 256, 256)">
        <animate id="showRightBottom"
                 attributeName="width"
                 from="128.03125" to="383.96875"
                 begin="fadeOutBottom.end" dur="0.5s"
                 repeatCount="1" calcMode="paced"/>
        <animate id="fadeOutRightBottom"
                 attributeName="fill-opacity"
                 from="1" to="0"
                 begin="rotate240.end + 0.5s" dur="0.1s"
                 repeatCount="1" calcMode="paced"/>
        <animateTransform id="moveRightBottom"
                          attributeName="transform" type="translate"
                          additive="sum"
                          from="0, 0" to="0, -50"
                          begin="rotate240.end + 0.5s" dur="0.1s"
                          repeatCount="1" calcMode="paced"/>
        <set id="keepWidthRightBottom"
             attributeName="width"
             to="383.96875"
             begin="showRightBottom.end" end="fadeOutRightBottom.end"/>
        <set id="keepOpacityRightBottom"
             attributeName="fill-opacity"
             to="0"
             begin="fadeOutRightBottom.end" end="showRightBottom.begin"/>
      </use>

      <!-- 下辺(基本的には左上辺と同じ) -->
      <use xlink:href="#symbolTrapezoid" width="0"
           transform="rotate(180, 256, 256)">
        <animate id="showBottom"
                 attributeName="width"
                 from="128.03125" to="383.96875"
                 begin="showRightTop.end" dur="0.5s"
                 repeatCount="1" calcMode="paced"/>
        <animate id="fadeOutBottom"
                 attributeName="fill-opacity"
                 from="1" to="0"
                 begin="showBottom.end" dur="0.1s"
                 repeatCount="1" calcMode="paced"/>
        <set id="keepWidthBottom"
             attributeName="width"
             to="383.96875"
             begin="showBottom.end" end="fadeOutBottom.end"/>
        <set id="keepOpacityBottom"
             attributeName="fill-opacity"
             to="0"
             begin="fadeOutBottom.end" end="showBottom.begin"/>
      </use>

      <!-- 左下辺(基本的には上辺と同じ) -->
      <use xlink:href="#symbolTrapezoid" width="0"
           transform="rotate(240, 256, 256)">
        <animate id="showLeftBottom"
                 attributeName="width"
                 from="128.03125" to="383.96875"
                 begin="fadeOutBottom.end" dur="0.5s"
                 repeatCount="1" calcMode="paced"/>
        <animate id="fadeOutLeftBottom"
                 attributeName="fill-opacity"
                 from="1" to="0"
                 begin="rotate240.end + 0.5s" dur="0.1s"
                 repeatCount="1" calcMode="paced"/>
        <animateTransform id="moveLeftBottom"
                          attributeName="transform" type="translate"
                          additive="sum"
                          from="0, 0" to="0, -50"
                          begin="rotate240.end + 0.5s" dur="0.1s"
                          repeatCount="1" calcMode="paced"/>
        <set id="keepWidthLeftBottom"
             attributeName="width"
             to="383.96875"
             begin="showLeftBottom.end" end="fadeOutLeftBottom.end"/>
        <set id="keepOpacityLeftBottom"
             attributeName="fill-opacity"
             to="0"
             begin="fadeOutLeftBottom.end" end="showLeftBottom.begin"/>
      </use>

    </g><!-- 台形グループ -->
  </g><!-- 図形全体のグループ -->

</svg>

アニメーションの開始・完了イベントを animate 要素の begin, end 属性に指定することでアニメーション持続時間が一部変更されても修正する場所があまり増えないようにしたかったんですが、ややこしくなってしまいました。
あと、全体的にコードの見通しが悪くて、メンテしやすいものを目指したはずが全く逆のものになってしまいました。

下記の記事で、このアニメーションの動きと似たサンプルが使われていますので、併せて読むとよいかと思います。
勉強になりました。

アニメーション自体はやりたかった動きを実現できたので概ね満足ですが、見やすい・管理しやすいコードとという点ではいまいちでした。
もっと色々やってみないとですね。


当初「アサシンクリード リベレーション」というゲームで使われているアブスターゴ社(ゲーム内企業)のロゴを使ったローディングアニメーションを目指していたんですが、座標の計算が面倒くさくて今回のようなものになりました……。
誰か作ってください。

Leave a Reply