アニメーショントレーニング 三角関数でなめらかな反復 -『Animation』

table of contents

    三角関数のメソッドを利用して、滑らかな JavaScript アニメーションを実装します。難しく考える必要はなく、そういうものなんだなあと認識して、アニメーションを楽しく実装しましょう。

    オブジェクトの操作には TweenMax を利用します。

    仕組み

    Math.sin(θ) というメソッドは-1から1の値を返します。θ の値を連続変化させることで、-1から1の範囲の値を得ることができます。

    この-1から1の値に、任意の値を掛けたりして、-1から1を拡張します。例えば、円の半径の値を掛けると、その円の範囲内を動くような値を得られます。

    θにはラジアンという値を与える必要があります。ラジアンを求める式はangle * Math.PI / 180 です。angle は角度です。このangleの変化量がオブジェクトが動くスピードです。

    横移動とカラーアニメーション

    単純な横移動と、TweenMax はカラーのアニメーションを簡単に実装することができるので、サンプルとしてこれも盛り込んでみます。

    demo



    class smoothMove { /** * * @param target {Object} * @param speed {Number} * @param wrapWidth {Number} デフォルトはウィンドウサイズ * @param wrapHeight {Number} デフォルトはウィンドウサイズ */ constructor(target, speed = 2, wrapWidth = window.innerWidth, wrapHeight = window.innerHeight) { this.target = target; // 角度 this.angle = 0; // スピード this.speed = speed; // 中心の座標 this.center = { x: wrapWidth / 2 - this.target.width() / 2, y: wrapHeight / 2 - this.target.height() / 2 }; // 動く範囲を円の半径で表現 // この倍の値(直径)が移動範囲になる this.radius = window.innerHeight / 4; return this; } /** * ラジアンに変換する * @param angle * @returns {number} */ getRadian(angle) { return angle * Math.PI / 180; } /** * 新しい範囲における現在の値を、現在の範囲を元に変換して返す * map(a, b, c, d, e) aを範囲b-cから別の範囲d-eへ変換する * @param value {Number} * @param fromMin {Number} 変換前の最小 * @param fromMax {Number} 変換前の最大 * @param toMin {Number} 変換後の最小 * @param toMax {Number} 変換後の最大 */ map(value, fromMin, fromMax, toMin, toMax) { let result = 0; result = (value <= fromMin) ? toMin : (value >= fromMax) ? toMax : (() => { let ratio = (toMax - toMin) / (fromMax - fromMin); return (value - fromMin) * ratio + toMin; })(); return result; } /** * アニメーションを始める */ play() { let radian = this.getRadian(this.angle); // Math.sin(radian)は-1から1までを正弦波で返す // radiusをかけることでそれを半径とする円の範囲でアニメーション // Math.sin(radian) * 範囲 let offsetX = Math.sin(radian) * this.radius; let color = `hsla(${this.map(Math.sin(radian), -1, 1, 0, 360) / 2}, 70%, 70%, 1)` TweenMax.set(this.target, { x: this.center.x + offsetX, y: this.center.y, backgroundColor: color }); // アニメーションで使用する角度を進める量 // つまり、速度 this.angle += this.speed; requestAnimationFrame(() => { this.play(); }); } } let anim = new smoothMove($('.target')); anim.play();

    詳細はコメントに書き込みましたが、背景色を変化させるための値は、map() という関数を作成して、-1から1の範囲を0から360に拡張しています。こういうヘルパー関数を用意しておくと便利ですね。ちょっとテクニカルな書き方をしてますが、ここは便利な関数として流しましょう。

    クラスとして定義したので、多少オプションを与えられるようにしました。

    円運動のアニメーション

    さっきはMath.sin(θ)を利用して、一定の範囲を取り出しました。Math.sin(θ)Math.cos(θ)を利用すると、同じ半径の円の円周上の座様を求めることができます。Math.cos(θ) が x 座標、Math.sin(θ) が y 座標を示します。

    この値を使って、オブジェクトを円運動させてみます。

    demo

    smooth animation – JSFiddle


    class smoothMove { /** * * @param target {Object} * @param speed {Number} * @param wrapWidth {Number} * @param wrapHeight {Number} */ constructor(target, speed = 2, wrapWidth = window.innerWidth, wrapHeight = window.innerHeight) { this.target = target; // 角度 this.angle = 0; // スピード this.speed = speed; // 中心の座標 this.center = { x: wrapWidth / 2 - this.target.width() / 2, y: wrapHeight / 2 - this.target.height() / 2 }; // 動く範囲を円の半径で表現 // この倍の値(直径)が移動範囲になる this.radius = window.innerHeight / 4; return this; } /** * ラジアンに変換する * @param angle * @returns {number} */ getRadian(angle) { return angle * Math.PI / 180; } /** * アニメーションを始める */ play() { let radian = this.getRadian(this.angle); // Math.sin(radian)は-1から1までを正弦波で返す // radiusをかけることでそれを半径とする円の範囲でアニメーション let offsetX = Math.sin(radian) * this.radius; let offsetY = Math.cos(radian) * this.radius; TweenMax.set(this.target, { x: this.center.x + offsetX, y: this.center.y + offsetY, }); // アニメーションで使用する角度を進める量 // つまり、速度 this.angle += this.speed; requestAnimationFrame(() => { this.play(); }); } } let anim = new smoothMove($('.target')); anim.play();

    横移動のアニメーションとほとんど変わりありませんね。

    他にも、


    let scale = Math.sin(radian) + 1; TweenMax.set(this.target, { scale: scale, });

    とすると、拡大縮小するアニメーションを実装できたりだとか、様々な動きに応用できます。変化させたい値を用意する際は、Math.sin(θ)が返す-1から1の値をmap()関数で範囲を変えて加工してあげると柔軟に対応できます。

    応用例

    おわります。

    アニメーショントレーニング 三角関数でなめらかな反復 -『Animation』のアイキャッチ画像

    share

    related