SVGで線をアニメーションさせる方法の簡単な覚え書き。
本ブログのロゴをサンプルとして。
まずはSVGを用意
今回はイラレでパスを書いてSVG書き出ししてSVGファイルを作成しました。
書きだしたSVGファイルをimgタグで読み込むだけでも普通に表示できます。
<img src="<?php echo get_template_directory_uri(); ?>/images/logo.svg" alt="" width="172">
ソースの確認
SVGファイルは実際には画像ファイルではなくHTMLソースが記述されたテキストファイルなので、テキストエディタで開くと中身は以下のようなソースになっています。
<?xml version="1.0" encoding="utf-8"?> <!-- Generator: Adobe Illustrator 19.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) --> <svg version="1.1" id="レイヤー_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 82.5 51.9" style="enable-background:new 0 0 82.5 51.9;" xml:space="preserve"> <style type="text/css"> .st0{fill:none;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;} .st1{fill:none;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-miterlimit:10;} </style> <polyline class="st0" points="1,1 12.4,34.6 24.1,1 36,34.6 47.8,1 "/> <circle class="st1" cx="64.7" cy="17.8" r="16.8"/> <path class="st1" d="M52.1,45.2c3.1,3.5,7.6,5.7,12.6,5.7c9.1,0,16.5-7.3,16.8-16.3"/> <line class="st1" x1="81.5" y1="1" x2="81.5" y2="34.6"/> <circle cx="71.8" cy="11.9" r="3.9"/> </svg>
svgで囲った中にCSSと図形描画タグが記述されています。
SVGの記述法について詳細はググればリファレンスがいくらもあるので省略。
SVGでは、polyline,circleなどの図形描画のタグにclassを振れば、CSSのプロパティとしてそれらの要素を調整することができるようになります。
例えば以下の記述では、lineタグで線の始点、終点の座標を定義し、styleで線の太さ、塗、角の丸みなどを後から定義しています。
<style type="text/css"> .st1{fill:none;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-miterlimit:10;} </style> <line class="st1" x1="81.5" y1="1" x2="81.5" y2="34.6"/>
ということで、アニメーションを行う場合はCSSやJSなどでこれらのプロパティを変化させるような記述をすればよいということです。
アニメーションの定義
今回は、CSS3のkeyframeアニメーションを使用して動きをつけてみました。
styleとタグの記述はSVGに含めてimgで読み込んでもいいし、htmlソース中にむき出しで書いてもよいです。その場合CSSは別ファイルにして読み込むなども可能。
ただし、タグだけを記述してimgで配置したSVGファイルの中に、外部ファイルからstyleを当てることはできないぽいので、SVGファイル内に含める場合は、SVGのソースにアニメーションまで全て含めて記述する必要があります。
今回は、タグ部分はhtmlソース中に生で配置し、styleはCSSファイルとして外部で読み込みました。
SVGタグの記述
まずアニメーションのために図形ごとにユニークなclassをつけました。
以下が実際に使用したソース.
<svg width="172" height="108" version="1.1" id="レイヤー_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 87 50"> <polyline class="line-w" points="1,1 12.4,34.6 24.1,1 36,34.6 47.8,1 "/> <circle class="circle-g" cx="64.7" cy="17.8" r="16.8"/> <path class="path-g" d="M52.1,45.2c3.1,3.5,7.6,5.7,12.6,5.7c9.1,0,16.5-7.3,16.8-16.3"/> <line class="line-g" x1="81.5" y1="1" x2="81.5" y2="34.6"/> <circle class="circle-g2" cx="62" cy="18" r="3.9"/> </svg>
SVGの座標について自分的に少し混乱したのでメモ。
要素として記述されてる「ViewBox」とは、SVGタグで画像の大きさとして指定された表示範囲をベクター画像として扱う場合に何✕何ピクセルにするのか定義する要素、といった感じです。図形描画タグの中で定義する座標はあくまでもViewBoxに対しての座標であって、実際の見た目は、画像として設定されたエリアに対するVieBoxの大きさを加味した相対的な位置に調整されます。
例として上記では172*108を画像サイズとしてwidth,heightを定義しており、ViewBoxを(0,0,87,50)で定義しているので、172*108の画像の中の左上の座標が(0,0)、右下の座標(※画面の見た目上の(172,108))が(87,50)となります。
図形描画のx座標として50を指定した場合、50*(画像横幅172px/ViewBox横幅87px)=画像上の99px程度の位置で表示されるということです。
要はそもそもこの例が、画像として使用したい大きさに合わせてSVGデータを作っていなかったのですごくわかりづらい例になっているということで、ベクターデータ作成の段階で単位をちゃんとピクセルベースにして、アートボードを使いたい画像サイズにしておけば混乱を避けられたのだということです。次から気をつけよう…
SVG用のCSSの記述
今回は以下のようなCSSを記述し、外部ファイルとしてcssに組み込みました。
.line-w, .path-g, .line-g{ fill:none; stroke:#000000; stroke-width:1.5; stroke-linecap:round; stroke-linejoin:round; stroke-miterlimit:0; } .line-w{ stroke-dasharray: 150; stroke-dashoffset:150; animation-name:DASH-w; animation-duration:2s; animation-delay:2s; animation-fill-mode: forwards; } @keyframes DASH-w{ 0%{stroke-dashoffset:150;} 100%{stroke-dashoffset:0;} } .path-g{ stroke-dasharray: 50; stroke-dashoffset:50; animation-name:DASH-pg; animation-duration:3s; animation-delay:2s; animation-fill-mode: forwards; } @keyframes DASH-pg{ 0%{stroke-dashoffset:50;} 100%{stroke-dashoffset:0;} } .line-g{ stroke-dasharray: 40; stroke-dashoffset:40; animation-name:DASH-lg; animation-duration:3s; animation-delay:2s; animation-fill-mode: forwards; } @keyframes DASH-lg{ 0%{stroke-dashoffset:40;} 100%{stroke-dashoffset:0;} } .circle-g{ fill:none; stroke:#000000; stroke-width:1.5; stroke-linecap:round; stroke-miterlimit:10; stroke-dasharray: 120; stroke-dashoffset:120; animation-name:DASH-circle-g; animation-duration:2s; animation-delay:2s; animation-fill-mode: forwards; } @keyframes DASH-circle-g{ 0%{stroke-dashoffset:120;} 100%{stroke-dashoffset:0;} } .circle-g2{ fill:none; cx:62; cy:18; animation-name:DASH-circle-g2; animation-duration:4s; animation-delay:4s; animation-fill-mode: forwards; } @keyframes DASH-circle-g2{ 0%{ fill:#FFF; cx:65; cy:18; } 10%{ fill:#104211; cx:65; cy:18; } 20%{ fill:#104211; cx:57; cy:18; } 30%{ fill:#104211; cx:72; cy:12; } 40%{ fill:#104211; cx:62; cy:28; r:3.9; } 50%{ fill:#104211; cx:65; cy:18; r:3.9; } 60%{ fill:#104211; cx:65; cy:18; r:7; } 80%{ fill:#104211; cx:65; cy:18; r:7; } 100%{ fill:#104211; cx:72; cy:12; r:3.9; } }
上述したとおり、図形描画タグの各要素をCSSのプロパティとして使用し、それをkeyframeアニメーションとして変化させています。
実際どう変化させているのか、という点はソースから読み取っていただくとして、出来上がったサンプルは実際にロゴとして使っているので、サイト上部をご参照ください。
線画をアニメーションさせてるからくりについては次で詳述します。
線画アニメーション
よく見るSVGによる線画アニメーションですが、これは、線のプロパティを点線にして、その点線の開始位置をkeyframeで制御することで、あたかも線が動的に描かれているかのように見せているだけで、実際には動的に描画されているわけではないです。
線を定義するプロパティのうち、以下の2つのプロパティを使用します。
stroke-dasharray
破線を引くために用いるプロパティで、値には長さやパーセンテージを指定します。この値に、アニメーションさせたいパスの長さを指定します。
例えば値10と入れた場合、10px描画して10px空白を繰り返します。
stroke-dashoffset
破線を引いた場合に、その破線をパスにそってどれくらいずらすかを定義します。
上記2つのプロパティを以下のように組み合わせて変化させ、あたかも線が引かれているように見せています。
A: stroke-dasharray: 150; stroke-dashoffset:150; ↓ B: stroke-dasharray: 150; stroke-dashoffset:0;
Aのdasharrayで、長さ、間隔150pxの破線を指定し、同時にoffsetで150px手前にずらすことで、見た目上0pxの線が引かれた状態(150pxの空白だけが表示された状態)となっています。
Bのoffsetで、手前にずらした150pxを元に戻しています。すなわち、見た目上は150pxの線がただ引かれた状態に戻ります。
間をkeyframeで設定することで、offsetからの戻しが徐々に行われるため、破線の切れ端が手前から線を埋めていくような動き=線が引かれていくような動き、となります。
線画アニメーションの基本については以上です。
これらをCSSやjsのトリガーなどと組み合わせることで、動的な演出が可能になると思います。
なお、ブラウザの対応状況はそれぞれで異なっていて、線を引くアニメーションはOSX、iOS共にsafari,chrome両方で動きますが、中のcircleの座標を動かすのはOSXとwindowsのchromeのみでしか動きませんでした。
今回はCSSでcircleのcx,cyなどのプロパティをいじってるのですが、動かないブラウザはCSS側から後でそれを変更するというのができないぽいです。最初の指定は反映されますし、fillなどは問題なくanimationが効いてるみたいなので、プロパティ単位で動作が違っているのだろうと思います。
接頭辞は今回どれにもつけていませんが、最新ブラウザだと特に問題ないみたいです。あ、でもIEは最新バージョンでももうなんというか、全く全然ダメです。なのでクライアントワークでanimationを使う場合には違うやり方を探す必要がありそうです。ブラウザ判別してpngを表示するなどの対応がいいかもしれないですね。