22
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

【入門】mo.jsでSVGアニメーション

Last updated at Posted at 2019-09-06

SVGのアニメーションを実装する機会がありmo.jsを使用したので、備忘録も兼ねて基本的な使い方を紹介したいと思います。

公式チュートリアルが充実しているのでこちらを読めば大丈夫だと思いますが、日本語の記事がほとんどないので英語アレルギーの方は参考にしてみてください。

執筆時点でのmo.jsのバージョンは0.288.2です。

セットアップ

npm

mo.jsをインストール。

npm i @mojs/core

インストールしたらmo.jsを使用するjsファイルで読み込む。

import mojs from '@mojs/core';

CDN

以下をHTMLに記述する。

<script src="https://cdn.jsdelivr.net/npm/@mojs/core"></script>

SVGを生成する

自分でSVGを用意しなくても、簡単な形であればmo.jsを使って生成することができます。
(自分で用意したSVGを使う方法は後述。)

円を描画する

new mojs.Shape({
  parent: '#parent', // SVGの親要素を指定する
  shape: 'circle', // 描画するSVGの指定
  radius: 10, // 円のサイズ
  fill: 'deeppink', // 塗りの色
  isShowStart: true // 最初から描画しておくかどうか
});

shape'circle'を指定するとellipseタグを使ったSVGを生成してくれます。
circleタグではなくellipseタグで生成されるため、楕円にすることもできます。
楕円にしたい場合はradiusではなくradiusXradiusYで縦幅、横幅をそれぞれ指定しましょう。

また、デフォルトではisShowStartfalseになっています。
これをtrueにしておかないとアニメーションが実行されるまで非表示になってしまうため、最初から表示しておきたいものはtrue指定するのを忘れないようにしましょう。

四角形を描画する

new mojs.Shape({
  parent: '#parent',
  shape: 'rect',
  radius: 10,
  fill: 'cyan',
  isShowStart: true
});

shape'rect'を指定するとrectタグを使ったSVGを生成してくれます。
オプションの指定はcircleと同じ感じで大丈夫です。

多角形を描画する

new mojs.Shape({
  parent: '#parent',
  shape: 'polygon',
  points: 6, // 角の数
  radius: 10,
  fill: 'yellow',
  isShowStart: true
});

shapeに'polygon'を指定するとpathタグでSVGを生成してくれます。
オプションの指定は基本的に'circle''rect'と同じですが、pointsで角の数を指定することができます。
デフォルト値は3なので指定していなければ三角形が描画されます。

ジグザグ線を描画する

new mojs.Shape({
  parent: '#parent',
  shape: 'zigzag',
  points: 11,
  radius: 25,
  fill: 'none',
  stroke: 'deeppink', // アウトラインの色
  isShowStart: true
});

shapeに'zigzag'を指定するとpathタグでSVGを生成してくれます。
オプションの指定は'polygon'と同じ感じで大丈夫です。
fillnoneを指定して塗りを無効にしておかないと、三角形が横並びになっているような感じで描画されます。
合わせてstrokeに線の色を指定しておきましょう。

曲線を描画する

new mojs.Shape({
  parent: '#parent',
  shape: 'curve',
  radius: 25,
  fill: 'none',
  stroke: 'cyan',
  isShowStart: true
});

shapeに'curve'を指定するとpathタグでSVGを生成してくれます。
オプションの指定は'circle''rect'と同じ感じで大丈夫です。
fillnoneを指定して塗りを無効にしておかないと、線の内側が塗りつぶされた状態で描画されます。

交差した線を描画する

new mojs.Shape({
  parent: '#parent',
  shape: 'cross',
  radius: 25,
  stroke: 'yellow',
  isShowStart: true
});

shapeに'cross'を指定するとpathタグでSVGを生成してくれます。
オプションの指定は基本的に'circle''rect'と同じ感じですが、fillを指定しても何も変わりません。

その他の指定可能なオプションやコールバックはこちらに一覧が載っています。

実際の使用例

See the Pen mo.js - Draw Shapes Sample by yumetomo (@srbangs) on CodePen.

アニメーションを実装する前に

Bounce系のアニメーションを付けると、素の状態だとはみ出した分が見切れてしまいます。
CSSで以下の指定をしておきましょう。

svg {
  overflow: visible;
}

無限ループのアニメーション

円のサイズが20→80→20→80→20→...と無限リピートするアニメーションを作ってみます。

new mojs.Shape({
  parent: '#parent',
  shape: 'circle',
  fill: '#F64040',
  radius: { 20 : 80 }, // { start : end }
  duration: 2000, // アニメーションの秒数
  isYoyo: true, // リピートする時に再生→逆再生→再生にするかどうか
  isShowStart: true,
  easing: 'elastic.inout', // アニメーションのイージング
  repeat: 1, // リピートする回数

  // リピート分を含めてアニメーションが全て終わったら呼び出されるcallback
  onComplete() {
    // この時thisの参照先はShapeオブジェクトになる
    this.replay();
  }
}).play();

アニメーションで変化させたいオプションはradius: { 20 : 80 }のようにオブジェクトで渡す。

isYoyoオプションは初期値falseなので、指定していなければ再生→初期値に戻る→再生の繰り返し、trueを指定すると再生→逆再生→再生の繰り返しになる。
また、逆再生はリピート扱いなのでrepeatオプションに最低でも1を指定していないと逆再生されない点に注意しましょう。

リピートする回数が決まっている場合はその回数をrepeatオプションにそのまま渡せば大丈夫ですが、repeatオプションで無限ループの指定をすることはできないので、無限ループしたい場合はonComplete()のコールバック関数内でreplay()メソッドを呼び出しもう一度最初からアニメーションをやり直します。

easingオプションに指定できる値はこちらを参照してみてください。

複数のアニメーションを繋げる

四角形が回転→内側から塗りが消えていくという流れを作ってみます。

new mojs.Shape({
  parent: '#parent',
  shape: 'rect',
  fill: 'none',
  stroke: '#FC46AD',
  radius: 10,
  strokeWidth: 20,
  angle: { '-180': 0 },
  duration: 900,
  easing: 'bounce.out',
})
.then({
  strokeWidth: 0,
  scale: 2,
  duration: 800,
  easing: 'sin.in',
  onComplete() {
    this.replay();
  }
})
.play();

thenで繋げることで一つのSVGに対して順番にアニメーションを実行することができます。
最初に指定したオプションは後から上書きしない限り維持されます(上の例だとfillstrokeは後から変わっていないのでずっと初期値のまま)。

複数のSVGを順番にアニメーションさせる

こういう時はTimelineの出番です。

const CIRCLE = new mojs.Shape({
  parent: '#parent',
  shape: 'circle',
  radius: 30,
  scale: { 0: 1 },
  fill: 'none',
  stroke: 'deeppink',
  strokeWidth: { 30: 0 },
  x: 'rand(-50, 50)', // 引数を元にランダムな値が入る - (min, max)
  y: 'rand(-50, 50)',
  duration: 300,
  // このSVGのアニメーションが完了した時点で呼び出される
  onComplete() {
    // rand()の値を再生成する
    this.generate();
  }
});

const RECT = new mojs.Shape({
  parent: '#parent',
  shape: 'rect',
  radius: 30,
  scale: { 0: 1 },
  fill: 'none',
  stroke: 'cyan',
  strokeWidth: { 30: 0 },
  x: 'rand(-50, 50)',
  y: 'rand(-50, 50)',
  delay: 150,
  duration: 300,
  onComplete() {
    this.generate();
  }
});

const POLYGON = new mojs.Shape({
  parent: '#parent',
  shape: 'polygon',
  radius: 30,
  points: 5,
  scale: { 0: 1 },
  fill: 'none',
  stroke: 'yellow',
  strokeWidth: { 30: 0 },
  x: 'rand(-50, 50)',
  y: 'rand(-50, 50)',
  delay: 300,
  duration: 300,
  onComplete() {
    this.generate();
  }
});

const TIMELINE = new mojs.Timeline({
  // Timelineに登録されている全てのアニメーションが完了したら呼び出される
  onComplete() {
    this.replay();
  }
});
TIMELINE.add(CIRCLE, RECT, POLYGON);
TIMELINE.play();

円と四角形と五角形のSVGを生成し、Timelineオブジェクトにadd()メソッドで追加して、play()メソッドを実行すると登録されているSVGのアニメーションが実行されます。

Staggerと組み合わせる

Timelineに登録するSVGがそれぞれ全く違う性質であれば良いですが、↑のようにほとんど同じようなSVGが複数ある場合1つ1つ生成していくのは非常に冗長です。
共通の部分だけ変数に持たせておくという手もありますが、Staggerを使うとより簡潔にまとめられます。

const STAGGER = mojs.stagger(mojs.Shape);
const SHAPES = new STAGGER({
  parent: '#parent',
  quantifier: 3, // 生成するSVGの数
  shape: ['circle', 'rect', 'polygon'], // 左から順番に参照される
  radius: 30,
  scale: { 0: 1 },
  fill: 'none',
  stroke: ['deeppink', 'cyan', 'yellow'], // 左から順番に参照される
  strokeWidth: { 30: 0 },
  x: 'rand(-50, 50)',
  y: 'rand(-50, 50)',
  duration: 300,
  delay: 'stagger(150)', // 0から始まり、150刻みで増えていく
  onComplete() {
    this.generate();
  }
});

const TIMELINE = new mojs.Timeline({
  onComplete() {
    this.replay();
  }
});
TIMELINE.add(SHAPES);
TIMELINE.play();

quantifier

コメントで書いてある通り、SVGを何個生成するか指定します。
整数を渡せばその数だけ生成されますが、例えば'shape'という文字列を渡すとshapeオプションに指定されている配列の中身の数が渡されます。

quantifier: 'shape', // shapeオプションの配列の中身の数(3)が渡される
shape: ['circle', 'rect', 'polygon']

quantifierの値 > 配列の中身の数な場合

例えば以下のような場合。

quantifier: 5,
shape: ['circle', 'rect', 'polygon']

この場合はcirclerectpolygoncirclerectという順番で値が渡される。
つまりquantifierの値の方が大きい場合、溢れた分はもう1度配列の先頭から参照するようになっている。

stagger()

渡された値刻みで増えていく。
第1引数が初期値、第2引数が増加量。
引数を1つだけにした場合は第2引数の増加量として扱われ、初期値は0になる。

quantifier: 3,
duration: 'stagger(150)', // 0→150→300
delay: 'stagger(100, 50)', // 100→150→200
x: 'stagger(1, .5)', // 1→1.5→2
y: 'stagger(-100, rand(100, 200))' // マイナスの値やrand()も使える

自分で用意したSVGを使用する

mo.jsの機能を使わず、自分で用意したSVGを使う場合は以下のように

class Thunder extends mojs.CustomShape {
  getShape() { return '<path d="M47.31 54.73L25.81 54.73L74.19 0L52.26 40.75L74.19 40.75L25.81 100L47.31 54.73Z" />' }
  getLength() { return 289.36328125; } // optional
}

mojs.CustomShapeクラスを継承したクラスを作成し、その中にSVGのパスを返すgetShapeメソッドを書きます。
また、アニメーションさせる際にstrokeDasharraystrokeDashoffsetに対して%の値を指定する場合、SVGのアウトラインの長さを返すgetLengthメソッドも追加で書く必要があります。

クラスを作成したら、以下のようにmojs.addShapeメソッドに作成したクラスを渡すことで使えるようになります。

mojs.addShape('thunder', Thunder);

new mojs.Shape({
  shape: 'thunder',
  // 略
});

実際の使用例

See the Pen mo.js - CustomShape Sample by yumetomo (@srbangs) on CodePen.

ShapeSwirlモジュール

語彙力が足りずうまく言葉で説明できないので、ShapeSwirlモジュールを使うとどういうアニメーションができるかは以下のCodepenをご覧ください。
使い方は今まで紹介してきたShapeとほとんど同じです。

See the Pen mo.js - ShapeSwirl Sample by yumetomo (@srbangs) on CodePen.

基本的なオプションはShapeと同じですが、ShapeSwirl用のものがいくつか増えています。
swirlSize:揺れの大きさ(初期値は10)。
swirlFrequency:揺れる回数(初期値は3)。
direction:最初の揺れが左右どちらの向きから始まるか(初期値は1)。1なら右、-1なら左。
pathScale:揺れを横の動きとすると、縦の移動量を調節できる(たぶんCodepenで軽く触ってみるとわかりやすいです)(初期値は1)。
degreeShift:アニメーションの角度(初期値は0)。

Burstモジュール

複数のShapeが飛び散るようなアニメーションを実装する時に便利なモジュールです。
オプションの指定方法は以下を参考にしてみてください。

new mojs.Burst({
  parent: '#parent',
  left: 0,
  top: 0,
  radius: { 8: 64 }, // 個々のShapeの大きさではなく、飛び散る範囲に反映される
  angle: { 0: 180 },
  count: 7, // 飛び散らせるShapeの数
  degree: 180, // 飛び散らせる範囲(この場合は0°〜180°の範囲に絞られる)

  // 個々のShapeの設定はchildrenオブジェクトの中に書く
  children: {
    radius: 5,
    fill: ['deeppink', 'cyan', 'yellow'], // 配列が使える
    scale: { 1: 0 },
    duration: 1000,
    delay: 'stagger(200)', // staggerも使える
    easing: 'quint.out'
  }
});

実際の使用例

See the Pen mo.js - Burst Sample by yumetomo (@srbangs) on CodePen.

tuneメソッド

上記Codepenのこの部分で使っているtuneメソッドについて

$WRAPPER.addEventListener('click', event => {
  BURST
    .tune({ // ←
      x: event.layerX,
      y: event.layerY
    })
    .replay();
});

tuneメソッドを使用すると、指定のオプションの値を上書きすることができます。
今回の場合だとreplayメソッドの実行前に、xyにクリックした位置を渡して上書きしているので、クリックした位置を中心にアニメーションが実行されています。

22
6
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
22
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?