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