p5.js でモーショングラフィックス
p5.js でモーショングラフィックスをします。私はエディタには公式のものをよく使っていますが、codepen なら Qiita に埋め込みできるとのことなのでそちらを使うことにします。
p5.js で基本のスケッチ
まずは四角を動かす最低限のスケッチです。
See the Pen VwYZxxe by naoto hieda (@naotohieda) on CodePen.
ポイントはdraw
関数の変数t
とtween
です。
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
オブジェクトのx
とy
を一秒間で2
と1
にイージングする場合、
1
2
3
let pos = {x: 0, y: 0}
TweenLite.to(pos, 1 /* seconds */, {x: 2, y: 1});
で済んでしまいます(下のサンプルコードではease
変数を定義してイージングの設定をしました)。内部でタイマーが動いて自動的にx
とy
が変化するので、一度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.