JavaScript動畫詳解(二) —— 緩動動畫

吳青強 8年前發布 | 41K 次閱讀 JavaScript開發 JavaScript

最普通的動畫就是勻速的動畫,每次增加固定的值。但是生活中很多運動并不是勻速運動的,而是有加速度改變的運動。在Web動畫中,緩動動畫有時候會讓網站增色不少。

在CSS3中可以使用ease, ease-in, ease-out, ease-in-out 或者 cubic-bezier(n,n,n,n)來實現緩動動畫。而且目前也有一些jQuery封裝了緩動動畫的Move.js, Velocity.js和Tween.js等。在實際項目中使用這些庫文件或者CSS3屬性可以大大提高開發效率。但是在學習中,為了了解JS緩動動畫的 真正原理,我覺得有必要嘗試用原生的JS實現之。

總的來說,緩動動畫都是把對象從已有位置移動到目標位置的過程,在這個過程中,加速度或者速度會隨與目標位置的遠近而變化。

緩動動畫的一些具體動畫曲線可以查看這里《緩動函數》,感受一下~

一. 一般實現緩動的策略如下:

1 . 為運動確定一個比例系數,這是一個小于1且大于0的小數;

2 . 確定目標點;

3 . 計算出物體當前位置與目標點位置的距離;

4 . 計算速度,例如緩入動畫中,速度 = 距離 × 比例系數,這時比例系數為運動的加速度;

5 . 用當前位置加上速度來計算新的位置;

6 . 重復第3到第5步,知道物體到達目標;

1.1 例子:緩入動畫

來個緩入動畫例子我們分析一下,效果如下:

 

先看看這些代碼片段以及他們的含義:

1 . 確定一個小數作為比例系數,這個比例系數為加速度(標量)。當系數越接近于1,物體移動得越快;當系數越接近于0,物體移動得越慢。

var easing = 0.05;

2 . 確定目標點。這里用targetX和targetY來定義:

var targetX = canvas.width / 2,
    targetY = canvas.height / 2;

3 . 計算物體到目標點的距離。創建小球名為ball,用ball的x、y減去目標點的x、y就能得到距離。

var dx = targetX - ball.x,
    dy= targetY - ball.y;

4 . 速度 = 距離 × 比例系數。

var vx = dx * easing,
    vy= dy * easing;

5 . 用當前位置加上速度來計算新的位置。

ball.x += vx;
ball.y += vy;

6 . 因為最后幾步需要重復執行,所以會把這些代碼放在drawFrame函數里面。

完整代碼如下:

HTML代碼:

<canvas id="canvas" width="400" height="100"></canvas>

JavaScript代碼:

// 創建畫球函數
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();
  }
}

// requestAnimationFrame的兼容性寫法
window.requestAnimFrame = (function(){
  return  window.requestAnimationFrame ||
    window.webkitRequestAnimationFrame ||
    window.mozRequestAnimationFrame    ||
    window.oRequestAnimationFrame      ||
    window.msRequestAnimationFrame     ||
    function( callback ){
    window.setTimeout(callback, 1000 / 60);
  };
})();

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

var canvas = document.getElementById("canvas"),
    context = canvas.getContext("2d"),
    ball = new Ball(),
    easing = 0.05,
    targetX = canvas.width - 10,
    targetY = canvas.height / 2;
    ball.x = 5;
    ball.y = 5;

// 緩動動畫函數
var animRequest = null;
(function drawFrame() {
    animRequest = window.requestAnimationFrame(drawFrame, canvas);
    context.clearRect(0, 0, canvas.width, canvas.height);
    var vx = (targetX - ball.x) * easing;
    var vy = (targetY - ball.y) * easing;

    ball.x += vx;
    ball.y += vy;
    ball.draw(context);
})();

1.2 改進版緩入動畫:加入拖拽效果

加入了小球的鼠標移入判斷和拖拽動畫判斷,然后就有了下面這個改進版的緩動動畫了。效果如下:

 

二. 何時停止緩動動畫

當計算一個單目標點的簡單緩動時,物體最終會到達這個目標點,緩動也就完成了。但是,即使在前面的幾個例子里,即使該物體看起來已經停止了,計算緩 動動畫的代碼還是一直在執行(不信的可以在緩動動畫中加入打印代碼如console.log("hello world!"),打開控制臺就會看到健步如飛的"hello world!"會打印出來~)。這樣比較浪費系統資源。一旦物體到達了目標點,代碼就應該不再執行了。這個功能很簡單,只需要在動畫循環里面判斷一下物體 是否到達目標點即可。如:

if(ball.x === targetX && ball.y === targetY) {
    // 停止緩動動畫代碼
    window.cancelAnimationFrame(animRequest);
}

事實上,由于ball.x和ball.y可能是小數,隨著vx和vy越來越小越趨近于0,事實上它離目標點越來越近,但是理論上永遠不會到達目標 點,而是無窮趨于目標點的小數。一般分辨率的電腦的顯示的最小精度是1px(除了一些高分屏精度為0.1px),不能精確顯示無窮多位小數的距離。到底多 近才是足夠近?這就需要判斷物體到目標點的距離是否小于特定值了。我們可以根據實際情況使用Math.ceil()、Math.floor()或 Math.round()來對小數進行取整操作,以取接近目標點的值。

所以上面的代碼可改寫為:

if(Math.ceil(ball.x) === targetX && Math.ceil(ball.y) === targetY) {
    // 停止緩動動畫代碼
    window.cancelAnimationFrame(animRequest);
}

三. 移動的目標點

在前面的例子中,目標點只有一個,并且是固定的。

然而目標點可以是移動的。我們在每一幀都會重新計算距離,然后根據距離計算速度,代碼并不關心物體是否到否目標點或者目標點是否在移動,它只需在播放的每一幀的時候知道目標點的位置,然后計算距離和速度。

3.1 例子:小球跟隨鼠標運動

小球跟隨鼠標運動的例子中,我們把鼠標位置作為目標點,只需要把前面例子中的targetX和targetY分別替換為鼠標的位置mouse.x和mouse.y即可。

 

四. 緩動的其他應用

 

緩動不僅僅適用于運動,還可以操作很多其他屬性。只要這個屬性是可以用數字表示的,就可以操作它。例如:

4.1 Demo1. 顏色緩動動畫

嘗試在24位顏色上使用緩動,要設置紅、綠、藍的初始值和目標值,用緩動改變每一種單獨的顏色,然后再把他么合并為單個顏色。

如下:

// 初始化變量
var red = 255,
    green = 0,
    blue = 0,
    redTarget = 0,
    greenTarget = 0,
    blueTarget = 255;

// 使用緩動動畫
red += Math.ceil((redTarget - red) * easing);
green += Math.ceil((greenTarget - green) * easing);
blue += Math.ceil((blueTarget - blue) * easing);

// 最后把這三個單色值合并成一個顏色

4.2 Demo2. 透明度緩動動畫

將緩動應用在alpha上,設置alpha的初始值和目標值,然后使用緩動動畫實現淡入淡出的效果,最后把它拼接成一個RGBA字符串:

var alpha = 0,
    targetAlpha = 1;

// 使用緩動動畫
alpha += (targetAlpha - alpha) * easing;
ball.fillStyle = "rgba(" + red +"," + green + "," + blue + "," + alpha + ")";

 

五. 高級緩動

 

我們上面用到的都是簡單緩動,即物體只有一個加速度easing。而事實上我們可以完全可以通過使easing為非定值,來實現自定義物體的任意運動狀態,譬如先加速且接近物體時減速等。

一些高級緩動函數可以參考:

1 . Tween.js的源碼:https://github.com/tweenjs/tween.js/blob/master/src/Tween.js

2 . jquery.easing.js的源碼:https://github.com/gdsmith/jquery.easing/blob/master/jquery.easing.js

六. 總結

緩動動畫是比例速度,通過修改每一幀的速度來計算出當前值,通過加速度easing可以控制獨特的動畫效果。簡單緩動動畫不難,關鍵是要動手練習。 高級緩動動畫,可以自己實驗出一種特效,或者多看看Tween.js和jquery.easing.js等一些類庫的緩動動畫實現以汲取經驗。

 

下一篇想寫一下彈動動畫。就醬紙。

來自:http://www.dengzhr.com/frontend/html/494

 本文由用戶 吳青強 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!