7_3:トゥイーン p5.js JavaScript

シェイプをある位置から別に位置に、アニメーションして移動させたい場合、そのスタート位置と終了位置を設定し、その間の位置を毎フレーム計算して、描画します。「トゥイーン」とは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()

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

CAPTCHA