【S2お茶会】SVGでうねうね動く円のアニメーション

s2お茶会
今回は弊社の森崎が「有機的な円のアニメーション」の実装について話しました。

SVGでうねうね動く円のアニメーション


たまたま 2 案件続いて円のアニメーションを有機的に動かしたいという要望が出たので、実装方法を考えてみました。

方法を考える


最終目標は、SVG でウネウネする円を描画することです。
ということで方法をいくつか考えて、それぞれのメリットとデメリットを書き出してみます。


gifアニメ、APNG
  • メリット
    • 実装は img で挿入するだけなので楽。
  • デメリット
    • 画像を作成するのが大変。
    • 調整/修正しづらい。
    • 決まった動きしかできないため単調になる。

canvas
  • メリット
    • パフォーマンスに優れる。
  • デメリット
    • ラスター形式のため描画が荒くなってしまう可能性がある。
    • 拡大縮小への対応が大変。

SVG
  • メリット
    • ベクター形式のため描画がキレイ。
    • パフォーマンスも悪くない。
  • デメリット
    • 実装が少し大変。

案件の内容的に、単色でぼかしなどのエフェクトも使用しないシンプルなアニメーションだったので、 今回は SVG を採用することにしました。

SVG とは


SVG は Scalable Vector Graphics の略です。直訳すると「拡大縮小可能なベクター画像」。
1999年に W3C によって開発された xml に基づくマークアップ言語です。テキストなので script などから操作が可能で、またテキストエディタで編集できます。

ベクター画像とは、

画像データの表現形式の一つで、画像を図形を表す数値情報の集合として表現したもの。サイズや解像度によらず同じ品質の出力結果を得ることができる。

ベクター画像
https://e-words.jp/w/%E3%83%99%E3%82%AF%E3%82%BF%E3%83%BC%E7%94%BB%E5%83%8F.html

いわゆる写真などはラスター画像と呼ばれておりベクター画像とは対極のもので、拡大・縮小するとピクセルの荒さが目立ったりしますが、ベクター画像は全てテキスト情報で計算されるため綺麗にレンダリングされます。

なので、レスポンシブなディスプレイが使用されるようになって以降ウェブ制作で使われることが増えました。

基本図形にはこれらがあって、


それぞれ専用のタグを持っています。
中でも path は基本図形の中で最も汎用的な要素で、それだけで全ての基本図形の作成が可能です。

<path d="M 10 10 H 90 V 90 H 10 L 10 10 Z"/>

のように、コマンド(命令)とパラメータ(座標)を指定して描画させます。

コマンド


コマンドには大きく分けて直線コマンドと曲線コマンドがあり、例えば moveTolineTo というコマンドを使いたい時には M or mL or l などの1文字で表します。というのもそのまま moveTo のように書いていくとものすごい長さになってしまうからだそうです。この時、大文字は絶対座標、小文字は相対座標を表しています。

直線コマンド
コマンド命令意味
M x y
m dx dy
moveto指定した座標に移動。
L x y
l dx dy
lineto指定した座標まで線を描画。
H x
h dx
horizontal lineto指定したx座標まで線を描画。
V y
v dy
vertical lineto指定したy座標まで線を描画。
Z
z
closepath始点に向かってパスを閉じる。
パラメータがないので大文字でも小文字でも意味は同じ。
曲線コマンド
コマンド名前意味
C x1 y1, x2 y2, x y
c dx1 dy1, dx2 dy2, dx dy
curveto三次ベジェ曲線
現在の点から、曲線の始点に対応する第一制御点 (x1, y1) と曲線の終点に対応する第二制御点 (x2, y2) を用いて点 (x, y) へ三次ベジェ曲線を描く。
S x2 y2, x y
s dx2 dy2, dx dy
shorthand/smooth
curveto
現在の点から点 (x, y) へ三次ベジェ曲線を描く。
第一制御点は前の命令の第二制御点の現在の点に対する鏡像(点対称)の地点とみなされる(もし前の命令が無いか、あるいは C, c, S, s のいずれでもない場合、第一制御点は現在の点と同一のものとみなされる)。
Q x1 y1, x y
q dx1 dy1, dx dy
quadratic Bézier curveto二次ベジェ曲線
現在の点から制御点 (x1, y1) を用いて点 (x, y) へ二次ベジェ曲線を描く。
T x y
t dx dy
Shorthand/smooth
quadratic Bézier curveto
現在の点から点 (x, y) へ二次ベジェ曲線を描く。
制御点は前の命令の制御点の現在の点に対する鏡像(点対称)の点とみなされる(もし前の命令が無いか、あるいは Q, q, T, t のいずれでもない場合、
第一制御点は現在の点と同一のものとみなされる)。
A rx ry x-axis-rotation large-arc-flag sweep-flag x y
a rx ry x-axis-rotation large-arc-flag sweep-flag dx dy
elliptical arc現在の点から点 (x, y) へ楕円形の弧を描く。
詳しくはこちら


フロントエンドでの SVG の扱い方


フロントエンドでは img タグや svg タグを使って描画させます。

img タグ
  • メリット
    • src に パスを指定するだけなので簡単。
  • デメリット
    • 基本的に扱いは画像となるので、細かな操作は出来ない。
svg タグ
  • メリット
    • テキストデータとして扱えるので、script で細かな操作が可能。
    • CSS による装飾が可能。
  • デメリット
    • 書くことが増えるためコードの見通しが悪くなる。

object タグを使っても実装できますが、使うメリットはなさそうな印象。

ライブラリが充実しているのでそれを使った方が実装も簡単になるし、表現の自由度も上がりそうでした。さらにレガシーブラウザへの対応も可能になるため使っておいて損はないように思います。

代表的なところだと、

  • Raphaël.js
    • 昔からあるパイオニア的ライブラリ。
    • IE 6+/Safari 3.0+/Firefox 3.0+ に対応。
  • Snap.svg
    • Adobe 製。
    • Raphaël.js と同じ人が作っている。
    • 機能豊富。
    • チェーンメソッド採用で、JQuery と相性がいい。
    • Typescript対応。
    • IE 9+/Chrome/Safari/Firefox に対応。
  • SVG.js
    • 軽量かつ高速。
    • チェーンメソッド採用。
    • プラグインで機能追加可能。
    • Typescript対応。
    • IE 9+/Chrome 4.0+/Safari 3.2+/Firefox 3.0+ に対応。
  • GraphicsJS
    • 比較的新しい。
    • AnyChart というデータビジュアライゼーションが得意な会社が作成。
    • グラフやチャートなどを作成する機能が豊富。
    • 仮想DOMに対応。
    • IE 6+/Chrome 1.0+/Safari 4.0+/Firefox 2.0+ に対応。

などがあります。

実際にやってみる


目標円周を動かしたい

SVGの circle タグでは円の描画はできても、円弧のアニメーションは作れないので path タグを使う必要があります。
また、path を使うなら二次ベジェ曲線か三次ベジェ曲線どちらを使うのか決めないといけませんが、三次ベジェの方がちょっと複雑になりそうだったので、今回は二次ベジェを使うことにしました。

STEP-1


まずポイントを作成します。
大体8等分ぐらいでほぼ真円と区別がつかないぐらいの近似曲線が描画できるそうです。

ポイントは、

  • コントロールポイント(制御点)
  • アンカーポイント(始点、終点)

の 2 パターンを作ります。
それらを結びつけながら円弧を描画していきます。
ライブラリは SVG.js 使用しました。

See the Pen STEP-1 by Kenji Morisaki (@kenz0626) on CodePen.


右上のパネル 1 つ目のチェックボックスはコントロールポイントです。
2 つ目をクリックするとそれらが線で結ばれます。
3 つ目がアンカーポイント。線と線の中点を取っています。アンカーポイント 2 つと、その間にあるコントロールポイントで円弧を描いています。

STEP-2


準備ができたので、いよいよアニメーションを実装します。
やり方としては、

  1. コントロールポイント(青い点)を円周に対して直角に上下動させる
  2. 隣り合うコントロールポイントの中点を取得して、アンカーポイント(赤い点)の位置を計算し直す
  3. コントロールポイントとアンカーポイントを元に円弧を再描画する

こうすることで、一つずつの線が動いているようなアニメーションを実装しました。
パネルを操作することで楕円形にしたり、座標を変更して部分的に円が見えるようにしたり、色々な設定が可能です。

See the Pen STEP-2 by Kenji Morisaki (@kenz0626) on CodePen.


ポイントを増やすことで複雑な動きをさせることも可能ですが、円のアニメーションに見えなくなってしまうので 8~16 ぐらいがちょうど良さそうに思いました。

今回は SVG と SVG.js を使って実装しましたが、他に何か良い方法があればぜひ教えてください。