Snap.svgで快適SVGアニメーション 第1回 Snap.svgとは

jQueryと同じような感覚で使え、さらにjQueryでは実現できないSVGの細かな操作が可能なJSライブラリSnap.svgを解説します。SVGの特徴、実装手段を検討し、Snap.svgの位置付けを知りましょう。

発行

著者 小山田 晃浩 フロントエンド・エンジニア
Snap.svgで快適SVGアニメーション シリーズの記事一覧

はじめに

SVG(Scalable Vector Graphics)というと、まず、「ベクター画像」という特徴を思い浮かべる人は多いでしょう。しかしSVGはただの画像ではなく、HTMLと同じくDOMを構成するのも特徴のひとつです。SVG(DOM)をJavaScriptで操ることで、インタラクティブなコンテンツを作ることができます。そのためのライブラリ「Snap.svg」について知識を深めましょう。

なお、このシリーズで紹介するサンプルは次のリポジトリにまとめてあります。併せて、参照してください。

Snap.svgで快適SVGアニメーション

SVGをJSで操作する

SVGはHTMLと同じようにDOMを構成します。そしてそのDOMをJavaScriptで操作することができます。SVGを活用すれば、SVGならではのアニメーションが可能です。

このように、SVGは、斜めや曲線の表現、あるいはマスク、フィルター、モーフィングなど、これまでHTMLでは難しかった表現を可能にしてくれます。

さて、ここからSVGのJavaScriptについて解説していきますが、途中でSVGの要素や属性などで不明点があれば、次のSVGに関する基礎を解説するシリーズにも目を通してみてください。

SVGのDOM操作の簡単な例を見てみましょう。次の例は、SVGの円の中心位置を示す、cx属性の値を少しずつ変えることでアニメーションさせています。

この例は次のようにJavaScriptを利用しています。

<svg width="500" height="100">
    <circle cx="50" cy="50" r="20"></circle>
</svg>

<script>
// SVGのcircle要素を取得
var $circle = document.querySelector( 'circle' );

( function anim () {

  window.requestAnimationFrame( anim );

  // 現在の cx 属性の値を取得
  var cx = $circle.getAttribute( 'cx' );
  // cx 属性が
  // - 500 以下なら cx+1
  // - 500より多ければ 0
  //  を newCx として保存
  var newCx = ( cx < 500 ) ? ( +cx ) + 1 : 0;

  // newCx を cx 属性に値にセット
  $circle.setAttribute( 'cx', newCx );

} )();
</script>

こうしたアニメーションを含むDOM操作を、より簡単に提供してくれるライブラリとしては、jQueryがもっともポピュラーでしょう。jQueryを使えばこんなに長いコードを書かなくても簡単にアニメーションを実現できそうに感じるのではないでしょうか?

しかし、jQueryはあくまでもHTMLを前提としたライブラリです。SVGで使おうとすると冗長になったり、実現できない機能もあります。

例えば、jQueryのanimate()で、先ほどの例と似たアニメーションを行おうとすると、次のようになります。jQueryは、要素のCSSプロパティの値を変えてアニメーションすることを前提に作られていますので、CSSプロパティではなく、HTMLの属性値主体のSVGには少し不向きな面があります。

どのように不向きかというと、次のようにコードが冗長になる傾向があります。

<svg width="500" height="100">
    <circle cx="50" cy="50" r="20"></circle>
</svg>

<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
<script>
var $circle = $( 'circle' );

( function loop () {
  // CSSではなく、属性を操作するため
  // jQueryアニメーションが冗長になる
  $circle.attr( {cx: 0} );
  $circle.animate (
    {
      cx: 500
    },
    {
      duration: 3000,
      easing: 'linear',
      step: function( now, fx ) {
        $( this ).attr( 'cx', now );
      },
      done: function() {
        loop();
      }
    }
  );
} )();
</script>

また、SVG独自のDOM APIも多数あります。例えば、次の記事でも紹介したパスの長さを取得するメソッドgetTotalLength()などです。

こういったSVG独自の仕組みに該当する機能は、jQueryには存在しません。

<svg width="512" height="256">

  <path id="myPath" d="M100,100 C125,0 225,0 250,100 S375,200 400,100" fill="none" stroke="#0066ff" />

</svg>

<div>パスの全長は<span id="log"></span></div>

<script>
var $myPath = document.querySelector( '#myPath' );
var pathLength = $myPath.getTotalLength();

var log = document.querySelector( '#log' );
log.innerHTML = pathLength;
</script>

SVGのDOM APIが備えているさまざまなメソッドは、jQueryでは使えません。それでは、jQueryを使わずに、ピュアなJavaScriptを使わないといけないかといえば、そうではありません。jQueryと同じ感覚で使え、しかもSVGのDOM APIを利用することができる、SVGためのJSライブラリSnap.svgが存在します。

私はsvg.jsなど、いくつかのSVGのラッパーライブラリを試してみたことがありますが、Snap.svgの機能がもっとも使いやすく、また導入事例もあるためCodeGridでは、Snap.svgを取り上げます。

Snap.svgとは

HTMLのためにjQueryがあるように、SVGのためにSnap.svgあります。Snap.svgはSVGのDOM取得から、属性変更、アニメーション、パスモーフィング、行列計算といったSVGに特化したさまざまな機能をjQuery感覚で利用することができます。

Snap.svgはRaphaël.jsの後継ライブラリであり、どちらもDmitry Baranovskiy氏により開発されています。

Raphaël.jsは2008年にリリースされ、IE6、7、8もサポートしているWebのためのベクターグラフィックス ライブラリでした。一方で、Snap.svgはIE8以下は未サポートですが、SVGのより高度な機能を提供してくれます。また、そのAPIはRaphaël.jsに似ており、Raphaël.jsを使った経験があれば、より理解しやすいでしょう。

Snap.svgはAdobeのGitHubリポジトリ内で管理されています。ここ半年(本稿執筆は2015年5月)ほど、メンテナンスが止まっていたため、私は心配でしたが、つい最近はたまったPR(プルリクエスト)が取り込まれています。

Snap.svgの基本

それでは、さっそくSnap.svgを触ってみましょう。

冒頭でご紹介した、円が水平方向に移動するアニメーションをSnap.svgを使うと、どこまで単純なコードにできるかを通して、基本を理解しましょう。

<svg width="500" height="100">
    <circle cx="0" cy="50" r="20" class="circle1"></circle>
</svg>

<script src="snap.svg-min.js"></script>
<script>

var $circle = Snap( '.circle1' );

function anim () {

  // cx の値を 0 にリセット
  $circle.attr( { cx: 0 } );

  // cx の値を 3 秒かけて 500 にアニメーションする
  $circle.animate( {
    cx: 500
  }, 3000, function () {
    anim();
  } );

};

anim();
</script>

まずは、DOMをSnap.svg用のオブジェクト(circle要素*)を取得します。jQueryの$( 'セレクター' )と同じですね。Snap.svgではSnap()を使います。

*注:circle要素

circle要素の各属性については、次のリファレンスなどを参照してください。

var $circle = Snap( '.circle1' );

SVGでは基本的にCSSではなく、属性により位置や大きさを決定します。まずは、属性をセットしましょう。属性をセットするにはattr()を使います。これもjQueryと同じですね。

オブジェクト形式で引数を渡すと属性値をセットすることができます。次のコードは円の中心のx座標を定義するcx属性値をセットしています。

$circle.attr( { cx: 0 } );

また、文字列形式で引数を渡すと、現在の属性値を取得することができます。

$circle.attr( 'cx' );

アニメーションの方法もjQueryとよく似ています。

var $circle = Snap( '.circle1' );

$circle.animate( {
  cx: 500
}, 3000, function () {
  console.log( 'callback' );
} );

どの属性を、何秒間かけて変化させるのか、そして、必要に応じてアニメーションが完了したあとに実行されるコールバック関数も指定することができます。

次のコードがアニメーションを行う関数anim()です。まずcx属性値を0にセットします。そして、cx属性の値を3000ms(3秒)かけて、500にアニメーションします。コールバック関数で、自分自身を呼び出し、アニメーションが継続するのです。

function anim () {

  // cx の値を 0 にリセット
  $circle.attr( { cx: 0 } );

  // cx の値を 3 秒かけて 500 にアニメーションする
  $circle.animate( {
    cx: 500
  }, 3000, function () {
    anim();
 } );

};

このように、表面上はSnap.svgはjQueryととてもよく似ています。一方で、その奥にはSVGならではの機能も数多く搭載されているライブラリです。

まとめ

いかがでしょうか? Snap.svgはjQueryを使った経験があれば、それをそのまま活かすことができます。APIの設計はRaphaël.jsに近いため、Raphaël.jsを使った経験がある人にもわかりやすいでしょう。また、SVG DOMをこれまでピュアなJavaScriptで操作した経験がある方にとっても、Snap.svgを通して何が起こっているのかがわかるはずです。

SVGはただの画像ではありません。そして、Snap.svgはSVGをDOMとして利用する際にとても便利なライブラリなのです。そのことがおわかりいただけたかと思います。

次回はアニメーションのもう少し細かな設定をみていきます。