JavaScriptアニメーション

9911 ワード

緩動も弾動も、そのオブジェクトが既存の位置からターゲット位置に移動する方法です.しかし、緩動とは、物体が目標点にスライドすると止まることを指す.弾動とは、物体が一定時間反発した後、最終的に目標点に止まった運動を指す.
弾動は、多くの場合、物体の加速度が目標点までの距離に比例します.
現実に弾き出す例を見てみましょう.ゴムの片方に小さなボールを結び、もう片方を固定します.小球の目標点は、その初期静止サスペンションの位置点である.ボールを少し離して離すと、手を放した瞬間、その速度は0だったが、輪ゴムは外力を加えて目標点に引っ張った.ボールをできるだけ遠くに引くと、輪ゴムが加わる外力が大きくなります.手を放すと、小さなボールが目標点を急速に飛んでいきます.しかし、目標点を飛んだ後、輪ゴムはまたそれを戻して加速度を小さくして、それが遠くに飛ぶほど、輪ゴムの力が大きくなります.結局、その速度は0に下がり、またUターンして戻ってきた.摩擦力の影響で、何度か繰り返した後、ボールの動きが徐々に遅くなり、目標点に止まった.

一.1 D座標のバウンド


1 . まず,弾性スケール係数を0~1の値で格納する変数が必要であり,大きな弾性スケールは常熟で硬いスプリング効果を示す.
var spring = 0.1,
    targetX = canvas.width / 2,
    vx = 0;

2 . 次に、小球から目標点までの距離を計算します
var dx = targetX - ball.x;

3 . 加速度を計算します.この例では,小球の加速度を距離に比例させる,すなわち加速度=小球から目標点までの距離を設定する.× 弾性比例係数
var ax = dx * spring;

4 . 加速度を速度に加算し、ボールの現在の位置に速度を加算します.
vx += ax;
ball.x += vx;

コードの書き込みを開始する前に、ballと仮定してプロセス全体をシミュレートします.x=0,初期速度vx=0,目標点の位置targetX=100,弾性スケール係数spring=0.1.次の手順を実行します.
(1)第1ラウンド、加速度ax=(100-0)*0.1=10、axをvxに負荷する速度vx=10、vxを小球の現在位置に加算ballを得る.x = 10;
(2)第2ラウンド、加速度ax=(100-ball.x)*0.1=9、これによりvx=10+9=19、ballが得る.x = 10 + 19 = 29;
(3)第3ラウンド、ax=7.1、vx=26.1、ball.x = 55.1;
(4)第4ラウンド、ax=4.49、vx=30.59、ball.x = 85.69;
(5)第5ラウンド、ax=1.431、vx=44.9、ball.x = 130.69:
(6)第6ラウンド、ax=-3.069、vx=41.831、ball.x = 88.859;
... ...
小球が1フレームずつ目標に近づくにつれて加速度はますます小さくなるが、速度はずっと増加している.
5ラウンド後、小球が目標点を越えた後、加速度は逆加速度になり、徐々に増加し、速度が徐々に減少し、最終速度が0になった後、逆加速度は極大値に達した.速度が逆速度になります.
HTMLコードは次のとおりです.
<canvas id="canvas" width="600" height="100"></canvas>

JavaScriptコードは次のとおりです.
// requestAnimationFrame      
window.requestAnimFrame = (function(){
    return  window.requestAnimationFrame       ||
            window.webkitRequestAnimationFrame ||
            window.mozRequestAnimationFrame    ||
            window.oRequestAnimationFrame      ||
            window.msRequestAnimationFrame     ||
            function( callback ){
                window.setTimeout(callback, 1000 / 60);
            };
})();

// cancelAnimationFrame      
window.cancelAnimationFrame = (function () {
    return window.cancelAnimationFrame ||
            window.webkitCancelAnimationFrame ||
            window.mozCancelAnimationFrame ||
            window.oCancelAnimationFrame ||
            function (timer) {
                window.clearTimeout(timer);
            };
})();

//       
function Ball() {
    this.x = 0;
    this.y = 0;
    this.radius = 10;
    this.fillStyle = "#f85455";
    this.draw = function(cxt) {
        cxt.fillStyle = this.fillStyle;
        cxt.beginPath();
        cxt.arc(this.x, this.y, this.radius,  0, 2 * Math.PI, true);
        cxt.closePath();
        cxt.fill();
    }
}

var canvas = document.getElementById("canvas"),
        context = canvas.getContext("2d"),
        ball = new Ball(),
        spring = 0.1,
        targetX = canvas.width / 2,
        vx = 0;
ball.x = 20;
ball.y = 20;

//       
var animRequest = null;
(function drawFrame() {
    animRequest = window.requestAnimationFrame(drawFrame, canvas);
    context.clearRect(0, 0, canvas.width, canvas.height);
    //              
    var dx = targetX - ball.x;
    //       
    var ax = dx * spring;
    //      
    vx += ax;
    //           
    ball.x += vx;
    ball.draw(context);
})();

次のような効果が得られます.
See the Pen EVNWqL by dengzhirong (@dengzhirong) on CodePen.
でも!しかし、問題は小球がいつまでも止まらないことだ.小球の揺れ幅が変わらないからだ.私たちが実現したい例では、ボールの弾きが止まるまで遅くなることを望んでいます.実際の生活では,小球の弾性ポテンシャルエネルギーの多くは摩擦力の存在によって内エネルギーに転化し,最後に小球を停止させる.したがって,ここでも摩擦力をシミュレートし,摩擦力係数frictionを0~1の範囲で作成する.
var friction = 0.95;

そしてvx*frictionを,現在の速度vxを得る.
vx * = friction;

最終的な効果は次のとおりです.
See the Pen GpNmRR by dengzhirong (@dengzhirong) on CodePen.
【備考:F 5を押してリフレッシュするか、「Result」パネルに浮かぶ「Return」ボタンをクリックして効果を確認してください】

二.2 D座標のバウンド


前の例は、ボールをx軸上で動かすことです.小球をx軸とy軸で同時に動かすには,2次元座標上の弾性を導入する必要がある.実際には簡単で、ターゲットポイント、速度、加速度を2 D座標系に拡張するだけでいいです.
コードは上記の例と同じように重複せず、直接効果を上げます.
See the Pen ojYWgm by dengzhirong (@dengzhirong) on CodePen.
【備考:F 5を押してリフレッシュするか、「Result」パネルに浮かぶ「Return」ボタンをクリックして効果を確認してください】
前の例と唯一異なるのは、y軸を1本追加することである.しかし、今でも小さなボールは1次元の動きのように見えます.小さなボールはx軸とy軸で同時に動いていますが、直線です.なぜなら、その初期速度は0であり、目標点に引っ張る外力も1つしか受けないため、直線に沿って運動する.アニメーションをより豊かにするには、vx、vy、または異なるx、y軸のfriction値を変更してみます.自分でやってみましょう.

三.ターゲットポイント移動のバウンド


ターゲットポイントが移動すると、マウスをターゲットポイントとして考えやすくなります.前回のスローモーションアニメーションでは、マウスのスローモーションアニメーションに従う小さなボールがありました.ボールをマウスに追従させるのも簡単で、targetXとtargetYを現在の座標に置き換えるだけです.効果はかっこいいですが、コードはほとんど変わっていません.前の例では、次の2行を変更します.
var dx = targetX - ball.x;
var dy = targetY - ball.y;

次のように変更します.
var dx = mouse.x - ball.x;
var dy = mouse.y - ball.y;

もちろん、現在のマウスの位置を取得する関数も書く必要があります.私が書いたブログ「JavaScriptアニメーションの詳細(一)--ループとイベントの傍受」を参照してください.
完全なコードは次のとおりです.
HTMLコード:
<canvas id="canvas" width="400" height="400"></canvas>

JavaScriptコード:
// requestAnimationFrame      
window.requestAnimFrame = (function(){
    return  window.requestAnimationFrame       ||
            window.webkitRequestAnimationFrame ||
            window.mozRequestAnimationFrame    ||
            window.oRequestAnimationFrame      ||
            window.msRequestAnimationFrame     ||
            function( callback ){
                window.setTimeout(callback, 1000 / 60);
            };
})();

// cancelAnimationFrame      
window.cancelAnimationFrame = (function () {
    return window.cancelAnimationFrame ||
            window.webkitCancelAnimationFrame ||
            window.mozCancelAnimationFrame ||
            window.oCancelAnimationFrame ||
            function (timer) {
                window.clearTimeout(timer);
            };
})();

//         
function getMouse(ev) {
    var mouse = {
        x: 0,
        y: 0
    };
    var event = ev || window.event;
    if(event.pageX || event.pageY) {
        x = event.x;
        y = event.y;
    }else {
        var scrollLeft = document.documentElement.scrollLeft || document.body.scrollLeft;
        var scrollTop = document.documentElement.scrollTop || document.body.scrollTop;
        x = event.clientX + scrollLeft;
        y = event.clientY + scrollTop;
    }
    mouse.x = x;
    mouse.y = y;

    return mouse;
}

//       
function Ball() {
    this.x = 0;
    this.y = 0;
    this.radius = 10;
    this.fillStyle = "#f85455";
    this.draw = function(cxt) {
        cxt.fillStyle = this.fillStyle;
        cxt.beginPath();
        cxt.arc(this.x, this.y, this.radius,  0, 2 * Math.PI, true);
        cxt.closePath();
        cxt.fill();
    }
}

var canvas = document.getElementById("canvas"),
        context = canvas.getContext("2d"),
        ball = new Ball(),
        spring = 0.05,
        vx = 0,
        vy = 0,
        targetX = 0,
        targetY = 0,
        friction = 0.95;
ball.x = 20;
ball.y = 20;
var mouse = {x: 0, y: 0};

canvas.addEventListener("mousemove", function(ev) {
    mouse = getMouse(ev);
    targetX = mouse.x;
    targetY = mouse.y;
    console.log(targetX + " , " + targetY);
}, false);
//       
var animRequest = null;
(function drawFrame() {
    animRequest = window.requestAnimationFrame(drawFrame, canvas);
    context.clearRect(0, 0, canvas.width, canvas.height);
    //              
    var dx = targetX - ball.x;
    var dy = targetY - ball.y;
    //       
    var ax = dx * spring;
    var ay = dy * spring;
    //      
    vx += ax;
    vy += ay;
    vx *= friction;
    vy *= friction;
    //           
    ball.x += vx;
    ball.y += vy;
    ball.draw(context);
})();

効果は次のとおりです.
See the Pen LpbyGq by dengzhirong (@dengzhirong) on CodePen.
【備考:マウスを動かしてみる~】
はい、上記の例は力が足りないので、ボールを輪ゴムに栓しているように見せたいのですが、このときは上にボールの中心を現在のマウスの位置に線を引くだけでいいです.
context.beginPath();
context.strokeStyle = "#71A4AD";
context.moveTo(ball.x, ball.y);
context.lineTo(mouse.x, mouse.y);
context.stroke();

効果は次のとおりです.
See the Pen NGbjRd by dengzhirong (@dengzhirong) on CodePen.

四.まとめ


[フレックス](Flip)と[ジット](Ease)は非常に似ています.これは、ループ関数を使用して現在の位置からターゲットの位置までのモーション効果をフレームごとに描画します.異なるのは緩動とは速度と距離に比例することであり、弾動は加速度と距離に比例する関係である.しかし、よりリアルな弾性をシミュレートするには、摩擦力係数のような因子を加えて、運動が停止するまで速度を徐々に低下させる必要がある可能性があります.
関連記事:
1 . 『JavaScriptアニメーション詳細(二)——緩動アニメーション』
2 . 『JavaScriptアニメーション詳細(一)——ループとイベントリスニング』