Naoto
Naoto Naoto Hieda

p5.js でモーショングラフィックス

p5.js でモーショングラフィックス

p5.js でモーショングラフィックスをします。私はエディタには公式のものをよく使っていますが、codepen なら Qiita に埋め込みできるとのことなのでそちらを使うことにします。

p5.js で基本のスケッチ

まずは四角を動かす最低限のスケッチです。

See the Pen VwYZxxe by naoto hieda (@naotohieda) on CodePen.

ポイントはdraw関数の変数ttweenです。

1
2
3
4
5
  let t = millis() * 0.001;
  let tween = t % 1;
  let w = width / 4;
  let x = tween * (width - w);
  let y = tween * (height - w);

アニメーションをする都度millis()関数を呼んでしまいがちですが、実は同じdraw内でも呼ぶタイミングによってmillis()の結果が少しずつ変わってしまうため、drawの初めにtに格納するのがおすすめです。frameCountなら誤差は出ませんが、フレームレートに依存してしまうため処理落ちの際にアニメーションが遅くなってかっこ悪いのでここではtにこだわることにします。tの値は常に大きくなっていくので、周期的なアニメーションをするためにt % 1で小数点以下だけを残してtweenに格納しました。

イージング

上の例では速度が一定で味気ないので、イージングを使うためにこちらのコードを拝借します。

1
2
3
4
5
6
7
8
9
EasingFunctions = {
  // no easing, no acceleration
  linear: function (t) { return t },
  // accelerating from zero velocity
  easeInQuad: function (t) { return t*t },
  // decelerating to zero velocity
  easeOutQuad: function (t) { return t*(2-t) },
  ...
}

例えば先のコードで

1
  let tween = EasingFunctions.easeInOutCubic(t % 1);

とすると

See the Pen abzoGQo by naoto hieda (@naotohieda) on CodePen.

というように動きに緩急がつきました(可読性のため、EasingFunctionsは HTML のscriptタグ内で定義しました。ついでに、自前のカラー・スキーム用ライブラリを使って色を付けています。)。

周期ごとのトリガー

次に、一周期ごとに何かイベントをトリガーすることを考えます。簡単な例として背景色を変えることにしましょう。一番簡単でバグが少ないのは次の方法です。

1
2
3
4
5
6
7
8
9
10
let lastT = 0;

function draw() {
  let t = millis() * 0.001;
  if (Math.floor(t) - Math.floor(lastT) > 0) {
    // 次の周期に移った
  }
  lastT = t;
  ...
}

floor()は小数点以下を切り捨てるので、lastT(前回のdrawでの時間)から一の位が一つ増えたことがわかります(厳密には処理落ちがひどい場合などに二周期以上飛ばしてしまう可能性もありますが、ここでは無視します)。このif文の中で、例えば背景色のインデックスを変えてみましょう。

See the Pen yLyBjZG by naoto hieda (@naotohieda) on CodePen.

lerp を使う

同様に四角の動く先を周期ごとに変えてみることにしましょう。イージングを使わないで、

1
  x = lerp(x, x_destination, 0.1);

とする方法もありますが、いくらループさせても限りなく近づくだけでx_destinationと等しくなることはないので、ここでもイージングを使います。面倒ですが、前回の位置をprevPosに、目標の位置をnextPosに格納します。drawの動きに関係ある部分だけ抜き出しました。

1
2
3
4
5
6
7
8
9
10
11
12
13
function draw() {
  let t = millis() * 0.001;
  if (Math.floor(t) - Math.floor(lastT) > 0) {
    prevPos = nextPos;
    nextPos = {x: random(width - w), y: random(height - w)};
  }
  lastT = t;

  let tween = EasingFunctions.easeInOutCubic(t % 1);
  let x = lerp(prevPos.x, nextPos.x, tween);
  let y = lerp(prevPos.y, nextPos.y, tween);
  rect(x, y, w, w);
}

これで四角形が一秒ごとに違う地点に向かうようになります。

See the Pen YzPKvzZ by naoto hieda (@naotohieda) on CodePen.

シーケンス

上の例では目標位置をランダムにしましたが、シーケンスを用いたのが次の例です。

See the Pen MWYgXYd by naoto hieda (@naotohieda) on CodePen.

外部ライブラリを使う

Processing には Ani というライブラリがありますが、JavaScript にもイージングをするための便利なライブラリがありますので、ここでは TweenLite を紹介します。まず、HTML にライブラリをインクルードします。

1
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/latest/TweenLite.min.js"></script>

posオブジェクトのxyを一秒間で21にイージングする場合、

1
2
3
let pos = {x: 0, y: 0}

TweenLite.to(pos, 1 /* seconds */, {x: 2, y: 1});

で済んでしまいます(下のサンプルコードではease変数を定義してイージングの設定をしました)。内部でタイマーが動いて自動的にxyが変化するので、一度to()を呼んでしまえばこちらでは何もする必要はありません(もともとは DOM の値をアニメーションするためのライブラリですが、同じように JavaScript のオブジェクトの変数もアニメーションすることができます)。

See the Pen YzPKvWQ by naoto hieda (@naotohieda) on CodePen.

だいぶコードがすっきりしました。また、拡張性も高くなったので、最後に四角の数を増やして回転のアニメーションを加えたサンプルで締めたいと思います。

See the Pen motiongraphics test with tweenlite adv by naoto hieda (@naotohieda) on CodePen.

comments powered by Disqus