シェイプをある位置から別に位置に、アニメーションして移動させたい場合、そのスタート位置と終了位置を設定し、その間の位置を毎フレーム計算して、描画します。「トゥイーン」とはFlashから生まれた言葉で、あるものとあるもの間(between)をアニメーションして変化することを言います。
Flashの場合は、Flashがトゥイーンを自動的に作ってくれましたが、JavaScriptでは、TweenMaxなどライブラリの助けを借りるか自力で作成します。以下は、(20, 30)から(160, 80)まで円がアニメーションして移動する、異なる3つのアプローチの例です。
目次
stepで刻む
フレームが描画されるたびに、変数stepの値を加算して、その分だけ終了位置に近づいていく方法です。
// スタート位置
const startX = 20;
const startY = 30;
// 終了位置
const stopX = 160;
const stopY = 80;
// 1.0を刻む数値 => 大きいほどスピードが増す
const step = 0.005;
// 毎フレーム、step分だけ大きくなる
let percent = 0.0;
// 円を描く座標
let x = startX;
let y = startY;
let p1;
let p2;
let p3;
function setup() {
createCanvas(240, 120);
stroke(255);
p1 = createP();
p2 = createP();
p3 = createP();
}
function draw() {
background(0);
// 移動経路を示すための補助線
line(startX, startY, stopX, stopY);
if (percent < 1.0) {
// スタート位置 + (終了位置までの差分*割合) => percent分、少しずつ近づいていく
// 近づいていく度合いを決めるのはstep => 速度に相当する
x = startX + ((stopX - startX) * percent);
y = startY + ((stopY - startY) * percent);
percent += step;
p1.html('x :' + x);
p2.html('y :' + y);
p3.html('percent :' + percent);
}
ellipse(x, y, 20, 20);
}
ポイントはdraw()内の次のコードです。変数percentは0から始まり、毎フレームstep分だけ大きくなっていきます。xとyは、終了位置とスタート位置の差分にpercentを掛けた数値に、スタート位置を足した値になります。
if (percent < 1.0) {
x = startX + ((stopX - startX) * percent);
y = startY + ((stopY - startY) * percent);
percent += step;
}
スタート時、percentは0なので、円はスタート位置に描画されます。次のフレームでは、percentにstep分が加算されるので、xとyは終了位置とスタート位置の差分に0.005を掛けた分だけ大きくなり、円は少し右下に描画されます。そして次のフレームでもpercentにstepが加算されます。終了位置とスタート位置の差分は一定で、それに一定分だけ大きくなる数値を掛けるので、xとyは同じ比率で大きくなっていきます。これにより円はスタート位置と終了位置を結ぶ直線上に描画されることになります。
stepは単なる数値ですが、小さいほど(x,y)の移動量が減り大きいほど増えるので、円が移動するアニメーションのスピードと考えることができます。step値は一定なのでスピードは等速です。
三角関数を使う
スタート位置は(20, 30)、終了位置は(160, 80)と分かっているので、2つを結ぶ直線を斜辺とする、下図の直角三角形を想定します。すると直角の頂点の座標は(160, 30)と計算でき、底辺の長さは140、高さは50と計算できます。底辺の長さと高さが分かると、タンジェントの逆関数atanで、スタート位置から終了位置に進む角度が分かります。
角度が分かると、x位置はcos(angle)で、y位置はsin(angle)で求まります。円の描画にはこのxとyを使用します。
const startX = 20;
const startY = 30;
const stopX = 160;
const stopY = 80;
let x = startX;
let y = startY;
const speed = 3;
let p1;
let p2;
let p3;
let angle;
function setup() {
createCanvas(240, 120);
stroke(255);
p1 = createP();
p2 = createP();
p3 = createP();
// 直角の角の座標
const mX = stopX;
const mY = startY;
// 底辺の長さと高さ
const a = dist(startX, startY, mX, mY);
const b = dist(mX, mY, stopX, stopY);
print('底辺の長さ : ' + a);
print('高さ : ' + b);
// 角度を求める
angle = atan(b / a);
p1.html('角度(ラジアン単位) : ' + angle);
}
function draw() {
background(0);
line(startX, startY, stopX, stopY);
if ((x < stopX) && (y < stopY)) {
const vx = cos(angle) * speed;
const vy = sin(angle) * speed;
x += vx;
y += vy;
}
ellipse(x, y, 20, 20);
p2.html('x : ' + x);
p3.html('y : ' + y);
}
ここでは変数vxとvyを求めるときに、変数speedを掛けています。speedに小さな数値を設定するとトゥイーンは遅く、大きな数値を設定すると速くなります。
イージング関数を使う
「11-1 そもそもイージング関数は一体何を…」で述べているイージング関数を使用する方法です。
d時間かけて、bからc分だけ、定速で変化するイージング関数は次のように定義されます。
/* t:現在のタイム値(位置)
b:プロパティの初めの値
c:プロパティの初めの値と終わりの値との差(変化量)
d:アニメーションの時間
*/
const noEasing = (t, b, c, d) => {
return c * t / d + b;
}
この関数を組み込んだコードは次のように記述できます。
const startX = 20;
const startY = 30;
const stopX = 160;
const stopY = 80;
const defX = stopX - startX;
const defY = stopY - startY;
const duration = 1000; // 長さ
let x = startX;
let y = startY;
let start;
// 経過時間値
let elapsedTime = 0;
let isRunning = true;
let p1;
let p2;
function setup() {
createCanvas(240, 120);
stroke(255);
// スタート時間
start = millis();
p1 = createP();
p2 = createP();
}
function draw() {
background(0);
line(startX, startY, stopX, stopY);
// 今の時間
const now = millis();
// 経過時間値
elapsedTime = now - start;
// 経過時間が長さより大きくなったらアニメーションを止める
if (elapsedTime >= duration) {
isRunning = false;
}
if (isRunning) {
// 定速
x = noEasing(elapsedTime, startX, defX, duration);
y = noEasing(elapsedTime, startY, defY, duration);
// だんだん加速
//x = easeInQuad(elapsedTime, startX, defX, duration);
//y = easeInQuad(elapsedTime, startY, defY, duration);
}
ellipse(x, y, 20, 20);
p1.html('x : ' + x);
p2.html('y : ' + y);
}
/* t:現在のタイム値(位置)
b:プロパティの初めの値
c:プロパティの初めの値と終わりの値との差(変化量)
d:アニメーションの時間
*/
// d時間かけて、bからc分だけ、定速で変化する
const noEasing = (t, b, c, d) => {
return c * t / d + b;
}
// d時間かけて、bからc分だけ、easeInQuadで変化する
const easeInQuad = (t, b, c, d) => {
t /= d;
return c * t * t + b;
}
コードの記述は面倒ですが、この方法では、使用するイージング関数を変更することで、アニメーションの進行度合いを変えることができます。たとえば上記サンプルのeaseInQuad()関数を使用すると、円がだんだん速く移動します。
millis()
説明
プログラムがスタートしてからのミリ秒数を返す(1ミリ秒は1/1000秒)。この情報は通常、タイミングイベントやアニメーションシーケンスで使用される。
シンタックス
millis()