LoginSignup
5
7

More than 3 years have passed since last update.

TypeScript+p5.jsの環境でイージングアニメーションを実装する

Posted at

はじめに

Javascriptのイージングアニメーションをさせるライブラリはあるが、p5.js上でどのように適用できるかいまいち分かりませんでした。

シンプルに実装できるみたいだったので、実装してみました。

以下のページにあるJavascriptプログラムを参考にしています。

easing.js
https://gist.github.com/gre/1650294

この記事では、以下のようなことを行っています。

  • 参考プログラムをTypeScriptで実装する
  • 実際に 位置大きさ を変えるアニメーションを実装する

easeInOutElasticでのアニメーション例
anim_num.gif

p5.jsをTypeScriptで書く環境を構築する方法については、以下の記事をご参照ください。

TypeScript+webpackでProcessing(p5.js)の環境を構築する
https://qiita.com/uchiko/items/744d7559d37973a959ea

今回のサンプルプログラムは以下にあります。
https://github.com/memememomo/ts-easing-anim-sample

サンプルプログラムの概要

今回作成したプログラムは、関係性を表すとクラス図に表すと以下のようになっています。

class.png

名前 概要
Tween イージングアニメーションを制御するクラス。
イージング関数を使用してアニメーションを実行する。
Easing イージング関数の型。
イージング関数を実装する場合は、
この型に合わせるようにする。
Point 座標とサイズを持った型。
アニメーションの対象になるオブジェクトは、
この型に合わせるようにする。

イージング関数の実装

まず、イージング関数の型を以下のように実装しています。

easing.ts
type Easing = (t: number) => number;

この型に合わせてそれぞれのイージング関数を実装します。

easing.ts
export const easeIn = p => t => Math.pow(t, p);
export const easeOut = p => t => 1 - Math.abs(Math.pow(t - 1, p));
export const easeInOut = p => t =>
    t < 0.5 ? easeIn(p)(t * 2) / 2 : easeOut(p)(t * 2 - 1) / 2 + 0.5;

export const linear = easeInOut(1);
export const easeInQuad = easeIn(2);
export const easeOutQuad = easeOut(2);
export const easeInOutQuad = easeInOut(2);
export const easeInCubic = easeIn(3);
export const easeOutCubic = easeOut(3);
export const easeInOutCubic = easeInOut(3);
export const easeInQuart = easeIn(4);
export const easeOutQuart = easeOut(4);
export const easeInOutQuart = easeInOut(4);
export const easeInQuint = easeIn(5);
export const easeOutQuint = easeOut(5);
export const easeInOutQuint = easeInOut(5);
export const easeInElastic = t => (0.04 - 0.04 / t) * Math.sin(25 * t) + 1;
export const easeOutElastic = t => ((0.04 * t) / --t) * Math.sin(25 * t);
export const easeInOutElastic = t =>
    (t -= 0.5) < 0
        ? (0.02 + 0.01 / t) * Math.sin(50 * t)
        : (0.02 - 0.01 / t) * Math.sin(50 * t) + 1;

イージング関数を使用する流れは以下のようなイメージになります。

sample.ts
// アニメーション開始時刻から現在まで、どのくらいの時間が経過しているか計算
const diff = Date.now() - startTime;

// 全体のアニメーション再生時間から何割の時間が経過しているか計算
const t = diff / duration;

// イージング関数で計算
const rate = easingFunc(t);

// 線形補間で現在の値を算出。位置を変化させる場合は現在位置を算出することになる
const currentPos = startPos + (destPos - startPos) * rate;

アニメーション対象の実装について

サンプルプログラムでは、以下のような型を実装しています。

easing.ts
type Point = {
    x?: number;
    y?: number;
    sizeX?: number;
    sizeY?: number;
};

イージングアニメーション処理を扱うTweenクラスでは、この型に準じたオブジェクトに関して、アニメーション処理を行います。

サンプルプログラムでは、Ellipseクラスを定義しています。

ellipse.ts
import * as p5 from "p5";

export class Ellipse {
  constructor(
    private p: p5,
    public x: number,
    public y: number,
    public sizeX: number,
    public sizeY: number
  ) {}

  draw() {
    this.p.push();
    this.p.ellipse(this.x, this.y, this.sizeX, this.sizeY);
    this.p.pop();
  }
}

Ellipseクラスは、 x y sizeX sizeY のメンバ変数を持っているため、Point型のオブジェクトとして扱えます。

Tweenクラスの実装

以下のような流れで使用できるTweenクラスを実装しています。

main.ts
// preload()
ellipsePos = new Ellipse(p, 100, 100, 100, 100);

posEasing = new Tween(ellipsePos);
posEasing.to({ x: 600, y: 600 }, 2000);
posEasing.easing(easeInOutElastic);
posEasing.start();


// draw()
posEasing.update();
ellipsePos.draw();

Tweenクラスの実装は以下のようになっています。

easing.ts
export class Tween {
    startPoint: Point;
    destPoint: Point;
    duration: number;
    easingFunc: Easing;
    startTime: number;
    isMove: boolean;
    delayTime: number;

    constructor(private point: Point) {
        this.startPoint = {
            x: point.x,
            y: point.y,
            sizeX: point.sizeX,
            sizeY: point.sizeY
        };
        this.easingFunc = linear;
        this.isMove = false;
        this.delayTime = 0;
    }

    to(to: Point, duration: number) {
        this.destPoint = to;
        this.duration = duration;
    }

    easing(e: Easing) {
        this.easingFunc = e;
    }

    delay(d: number) {
        this.delayTime = d;
    }

    start() {
        this.startTime = Date.now();
        this.isMove = true;
    }

    update() {
        if (!this.isMove) {
            return;
        }

        const diffTime = Date.now() - this.startTime - this.delayTime;

        if (diffTime < 0) {
            return;
        }

        const rate = this.easingFunc(diffTime / this.duration);

        if (this.destPoint.x != null) {
            this.point.x = lerp(this.startPoint.x, this.destPoint.x, rate);
        }

        if (this.destPoint.y != null) {
            this.point.y = lerp(this.startPoint.y, this.destPoint.y, rate);
        }

        if (this.destPoint.sizeX != null) {
            this.point.sizeX = lerp(
                this.startPoint.sizeX,
                this.destPoint.sizeX,
                rate
            );
        }

        if (this.destPoint.sizeY != null) {
            this.point.sizeY = lerp(
                this.startPoint.sizeY,
                this.destPoint.sizeY,
                rate
            );
        }

        if (diffTime >= this.duration) {
            this.isMove = false;
        }
    }
}

まとめ

シンプルなイージングアニメーションを実装しました。いまのところ必要になる処理はこれで十分まかなえています。

他のライブラリと比べると、まだまだ実装が足りない部分があると思います。もしかしたら、p5js用のライブラリとして作り込むかもしれません。

5
7
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
5
7