読者です 読者をやめる 読者になる 読者になる

The future starts today

Webとか英語とか育児とかに関する雑記

実戦で使える本格的なSVGアニメーションを作る

SVGのアニメーションって綺麗だし、カッコいいですよね。

海外のサイトだったり、dribbbleではよく見るのですが、日本のサイトでは凝ったアニメーションはあまり見かけません。

解説記事も偏っていて、モーフィングだったり、ラインアートの記事はたくさんあるのですが、オブジェクトが重なっていくつもシャシャシャッと動くようなアニメーションを解説している記事となると極端に少なくなります。

というわけで、そんなアニメーションを勉強がてら作ってみました!

まずは全体のイメージを作る

今回は試しに、スマホ上にカード形式のタイトル・ディスクリプションが並ぶようなものを作ってみようと思います。 以下のような感じです。

f:id:shibe97:20160621221322p:plain

Illustratorを使って作成しました。 Illustratorでなくても、パスが書けるようなツールで、なおかつsvg形式で書き出せるものであれば何でも良いと思います。(sketchとかでもいけるのかな、おそらく)

SVG形式で書き出してみる

Illustratorであれば「ファイル」 > 「別名で保存」からSVG形式で保存ができます。

保存したファイルをSubline Text等のエディタで開いてみると、XML形式のSVGを見ることができます。

先ほどの画像をエディタで開いて、ちょっと整形すると次のようになります。

<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
    x="0px" y="0px" viewBox="0 0 541 446" style="enable-background:new 0 0 541 446;" xml:space="preserve">
    <g class="smartphone">
        <g class="background">
            <path class="bodyShadow" fill="#ccc" d="M180,348.8v-331c0-6.6,5.5-10.5,12-8l179,70c5.7,2.6,12,9.4,12,16v331c0,6.6-7.5,14.5-16,12l-175-73
          C186.1,362.9,180,357.8,180,348.8z"/>
        <path class="headerShadow" fill="#555" d="M175,36V19.8c0-6.6,10.5-12.5,17-10l179,70c5.7,2.6,12,9.4,12,16v19.7l-5,3.8"/>
        <path class="body" fill="#eee" d="M175,352V21c0-6.6,5.5-10.5,12-8l179,70c5.7,2.6,12,9.4,12,16v331c0,6.6-3.5,11.5-12,9l-179-70
                C181.1,366.1,175,361,175,352z"/>
        <path class="header" fill="#666" d="M175,39.3V21c0-6.6,5.5-10.5,12-8l179,70c5.7,2.6,12,9.4,12,16v20.3"/>
        </g>
        <g class="parts" fill="#333">
            <circle cx="235.7" cy="46.5" r="3"/>
            <path d="M302.3,76.4l-51.5-20.8c-1.3-0.5-1.9-2-1.4-3.3v0c0.5-1.3,2-1.9,3.3-1.4l51.5,20.8c1.3,0.5,1.9,2,1.4,3.3h0
          C305,76.3,303.6,76.9,302.3,76.4z"/>
        </g>
    <g>
        <path class="card1" fill="#fff" d="M167.7,59.1c0-2.4,2-3.9,4.4-3L358.8,131c2.1,1,4.4,3.5,4.4,5.9v68.9c0,2.4-1.3,4.3-4.4,3.3l-186.6-74.8
          c-2.2-1.1-4.4-3-4.4-6.3"/>
            <g class="text1" fill="#7cccbe">
                <polygon points="183,106.5 348.5,172.1 348.5,176.8 183,111.2"/>
            <polygon points="183,77.2 237.5,98.8 237.5,113.5 183,91.8"/>
            <polygon points="183,119.5 348.5,185.1 348.5,189.8 183,124.2"/>
        </g>
        </g>
    <g>
        <path class="card2" fill="#fff" d="M167.7,147.1c0-2.4,2-3.9,4.4-3L358.8,219c2.1,1,4.4,3.5,4.4,5.9v68.9c0,2.4-1.3,4.3-4.4,3.3l-186.6-74.8
          c-2.2-1.1-4.4-3-4.4-6.3"/>
            <g class="text2" fill="#7cccbe">
            <polygon points="183,194.5 348.5,260.1 348.5,264.8 183,199.2"/>
            <polygon points="183,165.2 237.5,186.8 237.5,201.5 183,179.8"/>
            <polygon points="183,207.5 348.5,273.1 348.5,277.8 183,212.2"/>
        </g>
        </g>
    <g>
        <path class="card3" fill="#fff" d="M167.7,235.1c0-2.4,2-3.9,4.4-3L358.8,307c2.1,1,4.4,3.5,4.4,5.9v68.9c0,2.4-1.3,4.3-4.4,3.3l-186.6-74.8
          c-2.2-1.1-4.4-3-4.4-6.3"/>
            <g class="text3" fill="#7cccbe">
            <polygon points="183,282.5 348.5,348.1 348.5,352.8 183,287.2"/>
            <polygon points="183,253.2 237.5,274.8 237.5,289.5 183,267.8"/>
            <polygon points="183,295.5 348.5,361.1 348.5,365.8 183,300.2"/>
        </g>
        </g>
    </g>
</svg>

できる限り分かりやすいようにclassを振りました。 この整形作業が最も重要なポイントだと思います。

どのパスが画像でいうどの部分なのかを把握し、分かりやすく配置し直すイメージです。

<path>は文字通りパスを表し、<g>は複数のパスをグループ化したものになります。

図で示すと以下のような構成になっています。

f:id:shibe97:20160623191529p:plain

動きをつけてみる

いよいよ本題です。 こいつに動きをつけてみます。

最初は素のSVGだけで何とかならないものかと頑張っていたのですが、かなり辛かったので、TweenMaxというライブラリを使用しました。 TweenMaxを用いると、jQueryライクにメソッドチェーンで直感的に動きをつけることができます。

greensock.com

まずはセレクタで要素を取得する

JSでSVGタグで作成した各要素を取得してきます。

var xmlns="http://www.w3.org/2000/svg",
    select = function(s) {
        return document.querySelector(s);
    },
    selectAll = function(s) {
        return document.querySelectorAll(s);
    },
    smartphone = select('.smartphone'),        
    background = select('.background'),
    parts = select('.parts'),
    card1 = select('.card1'),
    card2 = select('.card2'),
    card3 = select('.card3'),
    text1 = selectAll('.text1 polygon'),
    text2 = selectAll('.text2 polygon'),
    text3 = selectAll('.text3 polygon');

transformの基準点を中心にする

これから要素を拡大したり動かしたりするのですが、普通にtransformを行うと要素の左上が基準となってしまい、微妙なアニメーションが出来上がってしまいます。

なので、物にもよりますが、拡大させたい要素にはtransformOriginを設定してあげると良いでしょう。

TweenMax.set([background, card1, card2, card3, text1, text2, text3], {
    transformOrigin : '50% 50%'
});

第一引数に要素を配列で渡して、第二引数にCSSを指定するイメージでオブジェクトを渡します。

タイムラインを作成する

実際に動かしてみます。

ここではTimelineMaxというclassを用います。

TimelineMaxのインスタンスを生成時には様々なパラメータが指定できます。

  • repeat
    • 何回リピートさせるか
    • 例えば1を指定すると、計2回再生される
    • 無限にリピートさせたい場合は-1を指定する
  • delay
    • 開始前に何秒遅延させるか
  • yoyo
    • アニメーションが最後までいったら逆回転で再生させるかどうか
    • Boolean値で指定
  • paused
    • デフォルトはfalseとなっており、アニメーションは自動的に開始されてしまう
    • trueを指定した場合は、JSで好きなタイミングでplay()を呼べば開始できる

今回は以下のように指定しました。

var timeline1 = new TimelineMax({repeat:0, delay:1, yoyo:false, paused:false});

それ以降はメソッドチェーンでアニメーションを記述していきます。

timeline1.from(background, 0.5, {
    scale: 0,
    ease: Power1.easeOut
})
.from(parts, 0.5, {
    opacity: 0,
    ease: Power1.easeOut
})
.from(card1, 0.5, {
    scale: 0
})
.staggerFrom(text1, 0.5, {
    scale: 0
}, 0.1)
.from(card2, 0.5, {
    scale: 0
})
.staggerFrom(text2, 0.5, {
    scale: 0
}, 0.1)
.from(card3, 0.5, {
    scale: 0
})
.staggerFrom(text3, 0.5, {
    scale: 0
}, 0.1);

ここまでで各要素が順番に出現していくアニメーションを作ることができました。

各メソッドの説明

from()

  • 第一引数:対象の要素
  • 第二引数:何秒間でアニメーションさせるか
  • 第三引数:ここで指定した状態から、SVGタグが示す元の状態までアニメーションを行う

to()

  • 第一引数:対象の要素
  • 第二引数:何秒間でアニメーションさせるか
  • 第三引数:SVGタグが示す元の状態から、ここで指定した状態からまでアニメーションを行う

staggerFrom()

これがかなり便利。 複数要素を指定して、少しずつタイミングをズラしながらアニメーションをさせることができる。

  • 第一引数:対象の要素
  • 第二引数:何秒間でアニメーションさせるか
  • 第三引数:ここで指定した状態から、SVGタグが示す元の状態までアニメーションを行う
  • 第四引数:複数指定した要素たちの開始タイミング何秒ごと遅延させるか

staggerTo()

staggerFromの逆バージョン。こちらも便利。

  • 第一引数:対象の要素
  • 第二引数:何秒間でアニメーションさせるか
  • 第三引数:SVGタグが示す元の状態から、ここで指定した状態からまでアニメーションを行う
  • 第四引数:複数指定した要素たちの開始タイミング何秒ごと遅延させるか

Power1.easeOut

これはメソッドではないですが、一緒に説明してしまいます。

TweenMax側で用意されているeasingです。

どんなものがあるかは以下のease-visualizerを見ると分かりやすいです。(超便利!)

greensock.com

さらに並行してタイムラインを追加する

ついでにもうひとつタイムラインを加えてみます。

上下にふわふわと浮いているようなアニメーションをつけてみます。

var timeline2 = new TimelineMax({repeat:-1, delay:1, yoyo:true, paused:false});
timeline2.from(smartphone, 2, {
    y: 10,
    ease: Power1.easeInOut
});

yoyo = trueにすることで上下に行ったり来たりさせています。

結果、こんなものができました。

f:id:shibe97:20160622215028g:plain

最終的なコードはgistに置いておきます。

card type animation

まとめ

いかがだったでしょうか。

慣れてしまえば、このレベルのアニメーションであればサクサク作ることができそうです。

TweenMaxには他にもまだまだ機能があったので、おいおい試してみたいと思います。

おまけ

せっかくなので、私が運営しているサービスであるMeyasubacoのトップページに今回の成果物を組み込んでリニューアルしてみました。

meyasuba.co

ユーザーがある一定位置までスクロールしてきたらアニメーションがスタートするようにしてあります。

こちらももし良ければチェックしてみてください。