...

SVG画像のアニメーションを簡単に実装できるSnap.svgの使い方

SVG画像のアニメーションを簡単に実装できるSnap.svgの使い方

どうもおはようポテト(@ohayoupoteto22)です。

SVG画像をさ、動かしたいんだわ

ということで今回は

「SVG画像を使って簡単なアニメーションを実装したい」

「なんかそういうライブラリないん?」

という方に向けてSVG画像のアニメーションを簡単に実装できる「Snap.svg」の使い方をまとめました。

参考になれば幸いです⸝⸝- ̫ -⸝⸝

ブログ主

早速いってみよう!

そもそもSnap.svgとは?

Snap.svgの公式ページ

Snap.svgはSVGにアニメーションを付けることができるライブラリです。

ベクター画像のパスを指定することでお手軽に動きを付けることができる優れもの。

SVG画像を動かす手順

完成イメージ

ハンマーを振り回すアニメーション
今回は本サイトのローディング画面のアニメーションにて振り回されてるハンマーの部分を作ってみます。
上のはgif画像なので少しカクついてますが、リアルなものが見たい場合はローディング画面の方を確認してみてください。

手順

やること
step1
jsファイルを読み込む
step2
SVG画像を配置する
step3
変形後のパスを指定する

基本的にはこれだけ。簡単にできちゃいます。

実際にレッツゴーだよ!

くるみ

Snap.svgでアニメーションを実装する方法

jsファイルを読み込む

まず公式サイトからjsファイルをダウンロードします。

zipを解凍したらSnap.svg>dist>snap.svg-min.jsを設置し、

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

で読み込んであげれば準備完了です。

SVG画像を用意する

<svg id="js-svg" data-name="ハンマー" xmlns="http://www.w3.org/2000/svg" viewBox="-70 -50 400 300">
  <rect x="60.63957" y="155.39365" width="189.22245" height="5" rx="2.5" transform="translate(-105.38649 -14.71627) rotate(-21.37376)"
      fill="#c69c6d" />
  <path d="M105.05159,208.94745A28.47838,28.47838,0,0,1,80.09194,218.716Q69.30588,191.15637,58.5198,163.59674a22.33592,22.33592,0,0,1,24.95965-9.76849Q94.26553,181.38786,105.05159,208.94745Z"
      transform="translate(-58.5198 -82.15695)" fill="#3f4a57" />
  <ellipse cx="71.20314" cy="159.23249" rx="13.40157" ry="5.02559" transform="translate(-111.6555 -45.25486) rotate(-21.374)"
      fill="#303a45" />
</svg>

まず動かしたいSVG画像を配置します。

動かしたいパスにクラスを指定

<svg id="js-svg" data-name="ハンマー" xmlns="http://www.w3.org/2000/svg" viewBox="-70 -50 400 300">
  <rect id="svg-move-parts1" x="60.63957" y="155.39365" width="189.22245" height="5" rx="2.5" transform="translate(-105.38649 -14.71627) rotate(-21.37376)"
      fill="#c69c6d" />
  <path id="svg-move-parts2" d="M105.05159,208.94745A28.47838,28.47838,0,0,1,80.09194,218.716Q69.30588,191.15637,58.5198,163.59674a22.33592,22.33592,0,0,1,24.95965-9.76849Q94.26553,181.38786,105.05159,208.94745Z"
      transform="translate(-58.5198 -82.15695)" fill="#3f4a57" />
  <ellipse id="svg-move-parts3" cx="71.20314" cy="159.23249" rx="13.40157" ry="5.02559" transform="translate(-111.6555 -45.25486) rotate(-21.374)"
      fill="#303a45" />
</svg>

今回の場合は全てのパスを動かしたいのでそれぞれにidを割り当てます。

変形後のパスを用意する

//変形後
<rect id="svg-move-parts1" x="60.63957" y="155.39365" width="189.22245" height="5" rx="2.5" transform="translate(-105.38649 -14.71627) rotate(-21.37376)"
    fill="#c69c6d" />
<path id="svg-move-parts2" d="M105.05159,208.94745A28.47838,28.47838,0,0,1,80.09194,218.716Q69.30588,191.15637,58.5198,163.59674a22.33592,22.33592,0,0,1,24.95965-9.76849Q94.26553,181.38786,105.05159,208.94745Z"
    transform="translate(-58.5198 -82.15695)" fill="#3f4a57" />
<ellipse id="svg-move-parts3" cx="71.20314" cy="159.23249" rx="13.40157" ry="5.02559" transform="translate(-111.6555 -45.25486) rotate(-21.374)"
    fill="#303a45" />

変形後(動かした後)のパスを用意し、
ここから座標やら何やらを指定している属性を抜き取ります。

x="60.63957" y="155.39365" width="189.22245" height="5" rx="2.5" transform="translate(-105.38649 -14.71627) rotate(-21.37376)" fill="#c69c6d"

この部分ですね。

animateメソッドで動かす

let $svgParts1 = Snap("#svg-move-parts1");
let $svgParts2 = Snap("#svg-move-parts2");
let $svgParts3 = Snap("#svg-move-parts3");
let SPEED = 200; //アニメーションの速度,
$svgParts1.animate({ x: "75", y: "34", width: "189.22245", height: "5", rx: "2.5", transform: "translate(137.27166 -126.41038) rotate(77.23189)" }, SPEED, mina.easein);
$svgParts2.animate({ d: "M156.4867,71.15524a28.47835,28.47835,0,0,1-5.92376-26.14034l57.72658-13.08162a22.33593,22.33593,0,0,1,5.92373,26.14033Z" }, SPEED, mina.easein);
$svgParts3.animate({ cx: "246", cy: "15", rx: "5.02559", ry: "13.40157", transform: "translate(-148.11801 15.75089) rotate(-12.76835)" }, SPEED,mina.easein);

まずSnapクラスのインスタンスを作成します。
動かしたいパスそれぞれのidを指定してあげればおけ。

第二引数ではアニメーションの速度(ミリ秒)を、第三引数ではイージングを指定してあげます。

そしてそのインスタンスそれぞれで、先ほど抜き取ったパスを引数としてanimateメソッドを呼んであげれば…

animateメソッドで動かす

動いちゃいます。

色々ツッコミどころのある動きですが、これで簡単な2点間のアニメーションは実装できました。
今回のアニメーションは3点間を行き来することによってハンマーを振り回してるように見せるものなので、3つのパスを行ったり来たり&繰り返す必要があります。

アニメーションを繰り返す

$svgParts3.animate({ cx: "71.20314", cy: "159.23249", rx: "13.40157", ry: "5.02559", transform: "translate(-111.6555 -45.25486) rotate(-21.374)" }, SPEED, EASEIN, humerAnimation1);

このように関数を引数で指定してあげると、そのアニメーションが終了した後にその関数を呼んでくれるようになります。

これを利用して3点間のアニメーションを繰り返します。

let direction = true;
let EASEIN = mina.easein;
function humerAnimation0() {
  direction = true;
  $svgParts1.animate({ x: "60.63957", y: "155.39365", width: "189.22245", height: "5", rx: "2.5", transform: "translate(-105.38649 -14.71627) rotate(-21.37376)" }, SPEED, EASEIN);
  $svgParts2.animate({ d: "M105.05159,208.94745A28.47838,28.47838,0,0,1,80.09194,218.716Q69.30588,191.15637,58.5198,163.59674a22.33592,22.33592,0,0,1,24.95965-9.76849Q94.26553,181.38786,105.05159,208.94745Z" }, SPEED, EASEIN);
  $svgParts3.animate({ cx: "71.20314", cy: "159.23249", rx: "13.40157", ry: "5.02559", transform: "translate(-111.6555 -45.25486) rotate(-21.374)" }, SPEED, EASEIN, humerAnimation1);
}

function humerAnimation1() {
  if (direction) {
    $svgParts1.animate({ x: "75", y: "34", width: "189.22245", height: "5", rx: "2.5", transform: "translate(137.27166 -126.41038) rotate(77.23189)" }, SPEED, EASEIN);
    $svgParts2.animate({ d: "M156.4867,71.15524a28.47835,28.47835,0,0,1-5.92376-26.14034l57.72658-13.08162a22.33593,22.33593,0,0,1,5.92373,26.14033Z" }, SPEED, EASEIN);
    $svgParts3.animate({ cx: "246", cy: "15", rx: "5.02559", ry: "13.40157", transform: "translate(-148.11801 15.75089) rotate(-12.76835)" }, SPEED, EASEIN, humerAnimation2);
  }
  else {
    $svgParts1.animate({ x: "75", y: "34", width: "189.22245", height: "5", rx: "2.5", transform: "translate(137.27166 -126.41038) rotate(77.23189)" }, SPEED, EASEIN);
    $svgParts3.animate({ cx: "246", cy: "15", rx: "5.02559", ry: "13.40157", transform: "translate(-148.11801 15.75089) rotate(-12.76835)" }, SPEED, EASEIN);
    $svgParts2.animate({ d: "M156.4867,71.15524a28.47835,28.47835,0,0,1-5.92376-26.14034l57.72658-13.08162a22.33593,22.33593,0,0,1,5.92373,26.14033Z" }, SPEED, EASEIN, humerAnimation0);
  }
}
function humerAnimation2() {
  direction = false;
  $svgParts1.animate({ width: "189.22245", height: "5", rx: "2.5", transform: "translate(385 145) rotate(174.6146)" }, SPEED, EASEIN);
  $svgParts2.animate({ d: "M329.75652,173.71985a28.47841,28.47841,0,0,1,26.68481-2.51572q2.77778,29.46448,5.55553,58.929a22.336,22.336,0,0,1-26.68481,2.51569Q332.53429,203.18431,329.75652,173.71985Z" }, SPEED, EASEIN);
  $svgParts3.animate({ cx: "433", cy: "240", rx: "13.40157", ry: "5.02559", transform: "translate(-163.48199 -48.41855) rotate(-5.38564)" }, SPEED, EASEIN, humerAnimation1);
}
humerAnimation1();

このように関数を呼び合うことでアニメーションを繰り返します。

ブログ主

directionでアニメーションの方向を保存してるんだね

速度などを調整して完成

ハンマーが振り回されているように見せるため、2つ目のパスの透明度を下げて残像のようにします。

let $svgParts1 = Snap("#svg-move-parts1");
let $svgParts2 = Snap("#svg-move-parts2");
let $svgParts3 = Snap("#svg-move-parts3");
let svgBody = $("#js-svg");
let svgParts3 = $("#svg-move-parts3");
let SPEED = 320;
let EASEIN = mina.easein;
let direction = true; //アニメーションの向き(trueが振り下ろす時)
let animationCount = 0;

function humerAnimation0() {
  direction = true;
  svgBody.css("opacity", "1");
  $svgParts1.animate({ x: "60.63957", y: "155.39365", width: "189.22245", height: "5", rx: "2.5", transform: "translate(-105.38649 -14.71627) rotate(-21.37376)" }, SPEED, EASEIN);
  $svgParts2.animate({ d: "M105.05159,208.94745A28.47838,28.47838,0,0,1,80.09194,218.716Q69.30588,191.15637,58.5198,163.59674a22.33592,22.33592,0,0,1,24.95965-9.76849Q94.26553,181.38786,105.05159,208.94745Z" }, SPEED, EASEIN);
  $svgParts3.animate({ cx: "71.20314", cy: "159.23249", rx: "13.40157", ry: "5.02559", transform: "translate(-111.6555 -45.25486) rotate(-21.374)" }, SPEED, EASEIN, humerAnimation1);
}

function humerAnimation1() {
  let waitSecond = 200;//最初の待つ時間
  if (animationCount == 0) {
    waitSecond = 0;
  }
  if (direction) {
    setTimeout(function () {
      svgBody.css("opacity", ".1");
      svgParts3.css("display", "none");
      $svgParts1.animate({ x: "75", y: "34", width: "189.22245", height: "5", rx: "2.5", transform: "translate(137.27166 -126.41038) rotate(77.23189)" }, SPEED - 200, EASEIN);
      $svgParts2.animate({ d: "M156.4867,71.15524a28.47835,28.47835,0,0,1-5.92376-26.14034l57.72658-13.08162a22.33593,22.33593,0,0,1,5.92373,26.14033Z" }, SPEED - 200, EASEIN);
      $svgParts3.animate({ cx: "246", cy: "15", rx: "5.02559", ry: "13.40157", transform: "translate(-148.11801 15.75089) rotate(-12.76835)" }, SPEED - 200, EASEIN, humerAnimation2);
    }, waitSecond);
  }
  else {
    setTimeout(function () {
      svgBody.css("opacity", ".1");
      svgParts3.css("display", "none");
      $svgParts1.animate({ x: "75", y: "34", width: "189.22245", height: "5", rx: "2.5", transform: "translate(137.27166 -126.41038) rotate(77.23189)" }, SPEED - 200, EASEIN);
      $svgParts2.animate({ d: "M156.4867,71.15524a28.47835,28.47835,0,0,1-5.92376-26.14034l57.72658-13.08162a22.33593,22.33593,0,0,1,5.92373,26.14033Z" }, SPEED - 200, EASEIN);
      $svgParts3.animate({ cx: "246", cy: "15", rx: "5.02559", ry: "13.40157", transform: "translate(-148.11801 15.75089) rotate(-12.76835)" }, SPEED - 200, EASEIN, humerAnimation0);
    }, 100);
  }
}
function humerAnimation2() {
  direction = false;
  svgBody.css("opacity", "1");
  $svgParts1.animate({ width: "189.22245", height: "5", rx: "2.5", transform: "translate(385 145) rotate(174.6146)" }, SPEED, EASEIN);
  $svgParts2.animate({ d: "M329.75652,173.71985a28.47841,28.47841,0,0,1,26.68481-2.51572q2.77778,29.46448,5.55553,58.929a22.336,22.336,0,0,1-26.68481,2.51569Q332.53429,203.18431,329.75652,173.71985Z" }, SPEED, EASEIN, humerAnimation1, );
  $svgParts3.animate({ cx: "433", cy: "240", rx: "13.40157", ry: "5.02559", transform: "translate(-163.48199 -48.41855) rotate(-5.38564)" }, SPEED, EASEIN, svgParts3UnVanish);
  animationCount++;
}

function svgParts3UnVanish() {
  svgParts3.css("display", "initial");
}

//アニメーションスタート
humerAnimation1();

その他アニメーションのタイミングや速度などを変更してあげれば…

ハンマーを振り回すアニメーション

完成です。

お疲れ様!

くるみ

まとめ

以上、SVG画像のアニメーションを簡単に実装できるSnap.svgの使い方でした。

今回はほんの一部のしか利用してなく、このライブラリではまだまだ色んなことができます。詳しくは公式のドキュメントからどうぞ。

参考になれば幸いです!では⸝⸝- ̫ -⸝⸝

コメントを残す

CAPTCHA