Unformed Building

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

公開:

パーマリンク

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属性に指定することでアニメーション持続時間が一部変更されても修正する場所があまり増えないようにしたかったんですが、ややこしくなってしまいました。
あと、全体的にコードの見通しが悪くて、メンテしやすいものを目指したはずが全く逆のものになってしまいました。

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

ゲームミュージックと生存確認をかねた画期的な: svgアニメーションを分かりやすくプログラムするには

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


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