DigiPress

Highly Flexible WordPress Theme

メニュー

Snap.svgを使ったSVGのパスのモーフィングアニメーションデモ

 2018/08/18
Snap.svgを使ったSVGのパスのモーフィングアニメーションデモ

SVGを変形させたりアニメーションさせることができる、Adobeがオープンソースで提供しているJavaScriptライブラリである「Snap.svg」を使って、SVGのpathを別のpathにモーフィングさせるアニメーションサンプルをご紹介します。

SVGを操作するライブラリはSnap.svgよりもパフォーマンスが良いとされるsvg.jsなどがありますが、パスとアニメーションをより簡単に扱えるSnap.svgを利用してみました。

今回のサンプルは、WordPressのギャラリーやポートフォリオタイプのアーカイブページの記事一覧で、各記事をマウスオーバーした際に、レイヤー用に表示しているSVGを「にゅる」っと変形させるようなモーションエフェクトをイメージしています。

Snap.svgを使ったSVGのpath変形アニメーションデモ

See the Pen Hover Effect with SVG using Snap.svg.js by digistate (@digistate) on CodePen.

HTML/SVG/CSSの構成

HTMLの基本構成

HTMLは、アーカイブページをイメージして、各記事をarticleタグで括っています。
その中にあるSVGは、figureタグで括っておきます。

<section id="grid">
  <article style="background-image:url('背景用画像のURL')">
    <a href="#">
      <figure>
        <svg viewBox="0 0 200 200" preserveAspectRatio="none"></svg>
        <figcaption>
          <h2>記事のタイトル</h2>
          <p>ディスクリプションなど。</p>
        </figcaption>
        <button>ダミーボタン</button>
      </figure>
    </a>
  </article>
     :
     :
</section>

各記事(article)の背景画像をセット

ギャラリー形式のように各記事は画像を並べたデザインにするため、articleタグにインラインCSSで背景画像(アイキャッチ)をbackground-imageでセットしておきます。

<article style="background-image:url('背景用画像のURL')">

記事(article)のCSS

ギャラリーをイメージして、各記事(article)を表示したアーカイブページデザインは画像を並べた状態にするため、articleは左右の回り込みが可能なインラインのブロック要素(inline-block)にします。

3カラムにするために、幅はビューポート(ブラウザの表示サイズ)に対しておよそ33%の幅、高さは適当に500ピクセルにしています。

article {
    position:relative;
    display: inline-block;
    min-width:280px;
    width: 33.333vw;  /* ビューポートに対して33.333% : 3カラム表示 */
    height: 500px;  /* お好みで */
    background-position:center;
    background-size:cover;
}

svgタグの構成

svgタグには、viewBoxという属性でビューポートに対する位置とサイズを指定しておく必要があります。

ビューポートというのは、svgの描画エリアのサイズ(width, height)で、これに対してviewBoxは、描画エリアの左上の頂点を原点とした描画開始位置の座標(x, y)と、描画サイズ(width, height)viewBox="x y width height" として定義します。

ビューポートとviewBoxの関係は混乱しがちなので、通常は以下のように双方のサイズは同じにしておくと扱いやすくなります。

<svg width="200" height="200" viewBox="0 0 200 200">〜</svg>

SVGのviewBoxについての詳細は、以下が参考になります。

今回のデモでは、svgタグはCSSでwidth, heightを100%にしているため、ビューポート(width, height)は未指定ですが、viewBoxはとりあえず [0, 0, 200, 200] という正方形のサイズをセットし、後にJavascriptで描画するsvgの図形(実際はパス)の幅もこのviewBoxで指定した200ピクセルで作成します。

さらに、描画するsvgのアスペクト比の扱いを指定するpreserveAspectRatio属性は、svgをレスポンシブ化させて描画エリアに常に全体を表示させるため、「none」(指定しない)にしておきます。

ここまでのviewBoxpreserveAspectRatioの設定と、後述する描画図形の幅を200ピクセルで作成することによって、レスポンシブデザインに対応した、ホバー用のSVGレイヤーが出来上がります。

<svg viewBox="0 0 200 200" preserveAspectRatio="none"></svg>

SVG関連のCSS

figure要素のCSS

svgを包括するfigure要素は、親要素のarticleのサイズと同じにしてフィットさせるため、以下のスタイルを定義しています。

figure {
    position: relative;
    overflow: hidden;
    width:100%;
    height:100%;
}

svg要素のCSS

先述のsvgはarticle要素いっぱいに描画サイズをセットします。top:-1pxは、Mac版のFireFox 26.0にて発生する意図しない不明な線がでてしまうレンダリング不具合を補うために0pxではなく-1px上方に移動しています。

svg {
    position: absolute;
    top: -1px; /* FireFox 26.0(Mac) のレンダリング不具合補完のため */
    left:0;
    z-index: 10;
    width: 100%;
    height: 100%;
}

figcaption要素のCSS

figure要素のコンテンツのキャプションを意味するfigcaption要素もfigureに対してぴったり重ねるため、以下のようにします。

figcaption {
    position: absolute;
    top: 0;
    z-index: 11;
    padding: 10px;
    width: 100%;
    height: 100%;
    text-align: center;
}

その他、投稿タイトル(h2)やキャプション、ダミーの「Read More」ボタンの装飾についてはここでは割愛しますが、お好みで。

描画するsvgのパスの作成と塗りつぶしカラーの指定

今回のデモでは、svgパスのモーフィングアニメーションを行うことと、簡単にレイヤー用のパスとマウスオーバー時のパスを別のものに置き換えることができるよう汎用性を考え、svg要素の中に直接path要素を組み込んで描画するのではなく、記事(article)をすべて包括するsection要素に、data-path-from(初期状態のパス)、data-path-to(マウスオーバー時にモーフィングした後のパス)、data-fill-color(パスを塗りつぶすカラー)というdata属性に任意の値をセットしておき、Javascript側で指定したパスと塗りつぶしカラーを取得してSnap.svgで描画する、という仕組みにしています。

section要素の構成

<section id="grid" data-path-from="初期状態のレイヤー用パス情報" data-path-to="マウスオーバー時のパス情報" data-fill-color="パスの塗りつぶしカラーコード">

描画したいsvgのパス情報をつくる

描画したいsvgの作成と、そのパス情報を取得するは、Illustratorのような高価なソフトウェアを利用せずとも、オープンソースのベクトル画像編集ソフトの「Inkscape」を使えば簡単に作成・取得できます。

表示したいsvgの図形を初期状態のものとマウスオーバー時の状態の2つを順番に作成します。
このとき、図形の幅はsvg要素のviewBox属性に指定した幅と同じにして作成します。

パスの制御点は、初期状態とマウスオーバー時のもので必ず同じ数に合わせておきます。

作成したら、図形を選択した状態で、メニューから「パス」→「オブジェクトをパスへ」でパスに変換します。
capture-2016-10-25-11-37-40

さらに「編集」→「XMLエディタ」を開きます。
svg:pathd属性の値が求めるパス情報なので、これをコピーします。
inkscape

section要素にレイヤー用のpath情報と塗りつぶしカラーをdata属性にセット

コピーした初期状態とマウスオーバー後の2つのパス情報を、それぞれdata-path-fromdata-path-to属性にセットし、パスの塗りつぶしカラーのコードはdata-fill-color属性にセットします。

<section id="grid" data-path-from="m 0,-0.5337091 200,0 0,86.9148401 c -102.826143,-34.275781 -94.88403,33.170919 -200,0 z" data-path-to="m 0,-0.5337091 200,0 0,45.5189141 c -98.72778,19.754056 -96.52337,-23.611381 -200,0 z" data-fill-color="rgba(255,255,255,0.8)">

Javascriptでsvg要素に図形を描画

いよいよSnap.svgの力を借りて、svg要素にpath要素を組み込んでsvgレイヤーを描画します。

パラメータのセット

まずはパス情報や塗りつぶしカラーの取得や、各種パラメータをセットします。

var duration = 400, // アニメーション時間
    easing = mina.backout,  // イージングの種類(Snap.svgで提供されているもの)
    grid = document.querySelector("#grid"),  // data属性を持つsection要素の取得
    items = document.querySelectorAll("#grid article"),  // 記事(article)要素をまとめて取得
    is_hover = false;  // マウスオーバー状態判定用

Snap.svgでサポートされるイージングの種類は以下があります。

イージングの種類

  • mina.linear
  • mina.easeout
  • mina.easein
  • mina.easeinout
  • mina.backin
  • mina.backout
  • mina.elastic
  • mina.bounce

data属性の値(svgのパス情報と塗りつぶしカラー)を取得

section要素を代入している grid オブジェクトに getAttributedata-path-from, data-path-to, data-fill-color属性の値を取得します。

var path_config = {
      from : grid.getAttribute('data-path-from'),  // 初期状態のパス
      to : grid.getAttribute('data-path-to'),  // マウスオーバー時のパス
      fill_color : grid.getAttribute('data-fill-color')  // パスの塗りつぶしカラー
  };

各記事に対して順番にpathを描画する

[].slice.call(arguments) という書式を用いて querySelectorAllで取得した 配列ライクなオブジェクト(items)のアイテムを順番に処理していきます。

[].slice.call(items).forEach(function(el) {
  // ここで順番に処理 
}

ここでいよいよ Snap.svgの出番です。
article要素内のsvg要素に対して初期状態のパスを描画するためのオブジェクトを生成します。

var snap = Snap(el.querySelector('svg')),  // svg要素を指定
      path_svg = snap.path(path_config.from).attr({  // snapオブジェクトにパスを指定
          fill: path_config.fill_color  // 塗りつぶしカラー
      }
);

あとはマウスオーバーをトリガーにしてSnapオブジェクトにアニメーションさせてパスを表示させます。

// マウスオーバー時
el.addEventListener('mouseenter', function() {
  if (!is_hover) {
    // パスをマウスオーバー時のパスにアニメーションさせる
    path_svg.animate( 
      {'path' : path_config.to},   // パスの指定
      duration,   // アニメーション時間
      easing);  // イージングの種類
    is_hover = true;  // ホバー状態判定フラグ
  }
});

// マウスオーバーが解除されたとき
el.addEventListener( 'mouseleave', function() {
  if (is_hover) {
    // パスを初期状態のパスにアニメーションさせる
    path_svg.animate(
      {'path' : path_config.from},   // 初期状態のパスを指定
      duration, 
      easing );
    is_hover = false; 
  }
});

ここまでの流れをまとめると、各記事(article)へのループ処理全体は以下のコードになります。

[].slice.call(items).forEach(function(el) {
  var snap = Snap(el.querySelector('svg')),
      path_svg = snap.path(path_config.from).attr({
        fill: path_config.fill_color
      });

  el.addEventListener('mouseenter', function() {
    if (!is_hover) {
      path_svg.animate( 
        {'path' : path_config.to}, 
        duration, 
        easing);
      is_hover = true;
    }
  });

  el.addEventListener( 'mouseleave', function() {
    if (is_hover) {
      path_svg.animate(
        {'path' : path_config.from}, 
        duration, 
        easing );
      is_hover = false; 
    }
  });
});

今回のデモでは、すべて同じパスの前提なのでarticleの親要素のsectionにパス情報をdata属性にセットしていますが、記事ごとに異なるパスや塗りつぶしカラーをセットしたい場合は、article要素ごとに異なるdata属性をセットして、Javascriptでも各article要素のdata属性の値を取得するようにすれば可能になります。

参考

codrops/Shape Hover Effect with SVG
Snap.svgを使って、一手間かけたマウスオーバーアニメーションを作成したい!

Share / Subscribe
Facebook Likes
Tweets
Hatena Bookmarks
Pinterest
Pocket
Feedly