🔺

SVGアニメーションを書いてみよう

2022/10/21に公開

SVG と仲良くなろう

背景

普段 HTML/CSS で書けなさそうな図形は canvas(konvajs/node-canvas)に頼っているのですが、SVG について理解を深めたかったのと、canvas だとうまくアニメーション制御が書けず挫折したのでいい方法を模索するためです。

SVG とは

SVG とは「Scalable Vector Graphics(スケーラブル・ベクター・グラフィックス)」の略。
SVG は画像ファイルの一種で、画像や文字などをベクトルで描画するための指示を保存しており、拡大縮小でデータが劣化することがありません。

パスの記述方法

今回主に扱うのはpathです。
pathでは色々な直線や曲線を組み合わせて自由に書けますが、図形を書くために特化している書き方がいくつか用意されています。(rect, circleなど)

コマンド

大文字の場合は絶対座標、小文字にすると現在の位置からの相対座標になります。

  • M = 座標に移動する(move to)
  • L = 指定された座標まで直線を引く(line to)
  • H = 指定された座標まで水平線を引く(horizontal)
  • V = 指定された座標まで垂線を引く(vertical)
  • C = 曲線(curve to)
  • S = 滑らかな曲線(smooth curve)
  • Q = 2 次ベジェ曲線
  • T = 滑らかな 2 次ベジェ曲線
  • A = 楕円弧(Arc)
  • Z = パスを閉じる

目指すゴール

SVG を使って 1 球速報の変化球の軌跡をアニメーションで描画できるようにする
(赤線は補助線)

path を書いてみる

<path
  stroke="#984ea3"
  fill="none"
  strokeWidth="5"
  d="M 299.5,50 s 84.53,122.72727272727272,-25.939999999999998,140"
/>

このコマンドは、

  • 始点(299, 50)...絶対座標
  • ベジェ制御点(84,122)...相対座標
  • 終点(-25, 140) ...相対座標

のpathを書きます。

ベジェ曲線の書き方についてはこちらを参照。
https://developer.mozilla.org/ja/docs/Web/SVG/Tutorial/Paths#ベジェ曲線

SVG でアニメーション

CSSでアニメーションの定義を書きます。

svg path {
  stroke-dasharray: 300px; /** 破線の間隔 */
  stroke-dashoffset: 300px; /** 破線の開始位置 */
  /** delayでアニメーションの開始を軌跡ごとにずらす */
  animation: line_animation 3s both {delay}s;
}

/** 破線の開始位置をアニメーションでずらす */
@keyframes line_animation {
  0% {
    stroke-dashoffset: 300px;
  }
  100% {
    stroke-dashoffset: 0px;
  }
}

/** 球種表示のアニメーション */
@keyframes fade {
  0% {
    opacity: 0;
  }
  100% {
    opacity: 1;
  }
}

ハマったところ

ベジェ曲線の書き方

制御点は通過点ではないので、軌道を動的に作るときに直感的に考えにくかった。

途中までしか描画されない!

SVG は描画領域が決まっているので、領域をはみ出てしまうと描画されない
例: M 10,10 L -10,10 (座標 10,10 に移動して、-10,10 まで線を引く)
x=0 のところで線が途切れてしまう。
考え方は canvas と同じ。

回転の中心点はどこだ

ストレート以外は三角形を描画して回転させているが、pathの領域が指定したサイズにならないのでtranslateするにも中心点が指定できず、positionで調整するはめになった。
最適解が欲しいところ。

実装した例

https://cap-scorebook.com/game/5c08354d-f8cc-4276-8c91-cb7c2de963a2?first_team=チームB(紅白)&last_team=チームA(紅白)&first_run=0&last_run=0&inning=1&isTop=1&out=0&is1st=10&is2nd=0&is3rd=0&pitcher=若林&batter=菊池(和)&result=1BH

参考

https://fuuno.net/ani/ani16/ani16.html
https://qiita.com/hiroyuki-n/items/9f7667f249bd73905fbc

Discussion