SVGと戯れる 第3話「大量のSVGイラストに個別でアニメーションをつける with Snap.svg 後編」

Author: tsukasa

こんにちは、ヤムチャーtsukasaです。
前回はSVGのマウス移動による視差効果まで作成しました。後編はパーツのアニメーションを作成します!

アニメーションのクラスを確認する

前回でsvg要素の準備は全て終了しましたが、
アニメーションのためにつけたクラスの再確認をしましょう。

サンプルでつけたクラスは以下の4つです。

  • moveX…X軸移動を往復
  • moveY…X軸移動を往復
  • shake…一定間隔でガタッと揺れる
  • wink…まばたきをするように一瞬でscaleYを縮め、ゆっくり戻す

DOMを指定

中編でIDの重複を防ぐためにIDは全てクラスに置き換えましたね。
DOMの指定では複数同じアニメーションをつけるパーツが存在する可能性を想定してselectAllを使用します。

アニメーションの開始

中編と同じようにnode.setAttributeを使用して要素を変形させます。
アニメーションには用意したTweenエンジンを使いましょう。今回はTweenLiteを使います。

winkだけ新たな味方、getBBoxが登場しています。getBBoxを使用すると、指定されたsvgDOMのx座標、y座標、幅、高さが返ってきます。数値はスクリーン上での位置座標・大きさではなく、svg上での座標・大きさになります。ということは…?そう、リサイズの度にgetBBoxを走らせて位置座標を取得する必要がありません。これはcanvasにはないsvgの特権ですね。リサイズ周りが楽です。中編で気付いた方がいらっしゃると思いますが、svgDOMのtransformで指定する数値もsvg上での大きさになります。

getBBoxで取得した座標は指定したDOMのアニメーションの基点を指定する際に使用します。基点とは、アニメーションの中心点、つまりcssで言うtransform-originですね。単純な回転アニメーションや振り子アニメーションでもtransformの基点をどこに置くかで見た目は大きく変わります。基点を自在に指定できるようにしておくと、イラストレーターやデザイナーの方が想像するアニメーションにより近付けやすくなります。

アニメーション処理

TweenLIteの処理手順は以下の通りです。

TweenLiteの使い方の詳細はDocを参照してください。
TweenLite

  • progress(進行度)として0の値を持つオブジェクトを作成し、TweenLiteで値を0から1へTweenさせる
  • onUpdateでprogressに最終値を掛けた現在の値を算出し、node.setAttributeで変形
  • nCompleteで再び走らせる。間隔はdelayで調整する。setIntervalでアニメーションを繰り返してもいいです。(繰り返したくない場合はいらない)

moveX,moveY

自然な往復運動に見せるため、moveXには余弦波(cos)moveYには正弦波(sin)を使用します。余弦波は正弦波に置き換えても全然問題ありません。余弦波を使う場合はスタートが0からではないので、スタートの角度を90にしてあげるのを忘れないでください。

shake

shakeは単純に上下運動させているだけです。もはやTweenLiteは必要ありませんが、統一させるためにこの記述にしました。始まった途端に1回目の振動をさせるため、第5引数をフラグにしています。繰り返す時はこのフラグをfalseにしてます。

wink

winkだけちょっと違います。getBBoxで取得した座標を使用してる点ですね。
なぜ座標が必要なのか、簡単に説明しましょう。

それはscaleとrotateは変形の基点を指定する必要があるからです。先ほど書いた通り、基点はcss-transform-originと同じ扱いです。Snap.svgは簡単に位置座標と大きさを取得できるので、基点の計算がとても楽なのです。winkは基点を中心にしたいので、(x座標+(幅/2),y座標+(高さ/2))ですね。

あれ?何でscaleはnode.setAttributeじゃないの??と思った方。鋭い!
理由はnode.setAttributeでscaleの基点は設定できないからです。なのでSnap.svgで拡張されたtransformを使用しています。ここは機能性重視ということですね。

「何も描画されてないレイヤーは絶対に消す」と書いた理由

前編で何も描画されてないレイヤーは絶対に消してくださいと書いた理由は、実はこの基点と関係があります。アニメーションを指定したグループに空レイヤーが存在した場合、恐らくグループの位置座標が左上と判定されるようです。以外とレイヤーが整理されていないと残ってたりするので、シェイプの整理・結合作業の際にチェックしておくといいかもしれません。

完成!!

こうして出来上がったデモがこちらになります。
視差効果+アニメーション付きのデモ

どうでしょう、あれ?やっぱイラストが下手すぎますね!それっぽくなりましたね!
TOYOTA DREAM CAR COLLECTIONに素敵なイラストがたくさん掲載されているので、そちらを見た方がいいですね!リロードすると3種類のイラストが切り替わるので、93種類全部見つけてください。

改良するなら

ここでひとまず完成ではありますが、まだまだ改良の余地は残ってます。

  • 色のアニメーションを追加する(余談ですが、このキャラクターはヒットポイントを消費する無敵の迎撃技「アイアストロン」を持っていて、ピカピカピカ〜って色が銀色に光ります)
  • パスのモーフィングで目に表情をつける(パスのモーフィングについてはlettersさんの記事がとてもわかりやすいです!)
  • キー入力でパンチ・キック等のアニメーションを作る(入力時にtransformを全部クリアしてパーツをデフォルトのポジションに戻してから、別のアニメーションを開始する)
  • そもそもイラストを何とかする

注意点とまとめ

レスポンシブにも対応できて、アニメーションも色々できて、万々歳じゃん!!というわけにはいきません。SVGはそもそも描画コストが高いので、複雑すぎるイラストを使ったり、同時に大量のSVGを使うとパフォーマンスに多大なる影響を与えてしまいます。特にスマホは描画処理能力が低いので注意が必要です。(svgスプライトの使い過ぎで泣きを見ました)

必ず使用する際はパフォーマンスを頭の隅に置きつつ、やれる範囲で最適化しながら作りましょう。この手法はスピード重視なので最適化は後回しですが、あまりにも軽視しすぎると後で痛い目を見るので…。最後にポイントをおさらいしましょう。

  • イラストのシェイプ・パスはできるだけ結合させる
  • 透明度、レイヤー効果は使わない、空レイヤーは削除する
  • アニメーションをつけるパーツの数はほどほどに
  • mousemove等のイベントは間引いてあげる

それでは良きSVGライフを!!