先發(fā)表一下感慨秘案,作為一個二流后臺碼農(nóng)答姥,想要做前端(面試阿里漓糙,被一個簡單問題直接問掛铣缠,問題就是只用js實現(xiàn)一個1秒內(nèi)的元素滑動效果),發(fā)現(xiàn)很多基礎(chǔ)都不會昆禽,所以我就差一些基礎(chǔ)的東西補補蝗蛙。
本文除了上面的牢騷都是翻譯 -> 原文
大部分的時候,是框架幫助我們實現(xiàn)動畫醉鳖。
但是有的時候我們會想知道用純js動畫到底怎么寫捡硅,用純js寫動畫的一些坑。
即使有框架的幫助盗棵,要構(gòu)建復(fù)雜的動畫仍然要求我們掌握這項技能壮韭。
基礎(chǔ)
js動畫的實現(xiàn)是通過漸變DOM的styles或者是創(chuàng)建canvas。
整個動畫過程被分為一個個小部分纹因,每一個小部分被叫做timer喷屋。因為timer的間隔很短所以動畫看起來是連續(xù)的。
偽代碼如下:
var id = setInterval(function(){
/*展示當前的狀態(tài)(幀)*/
if(/*完成了*/) clearInterval(id);
}, 10)
這里瞭恰,每個幀是10ms, 也就是每秒鐘100幀逼蒙。
大多數(shù)的js框架默認使用10 ~15ms。 當瀏覽器足夠快的時候,間隔越短看起來越順滑流暢是牢。 但是如果這個動畫需要很高計算量僵井,以至于使用了100%的cpu,那么動畫就看起來卡爆了驳棱,在這個時候批什,我們應(yīng)該適當?shù)奶岣唛g隔的時間,例如45ms(也就是1秒鐘25幀左右)社搅,也接近于電影24幀的效果了驻债。
用
setInterval
而不是setTimeout
由于我們希望固定秒數(shù)內(nèi)執(zhí)行完一幀,而不是每一幀之間固定秒數(shù)形葬。這里有文章講setInterval和setTimeout
例子
假如我們需要通過改變element.style.left
以每10ms 10ps 移動一個元素從0px到100px合呐。
<!DOCTYPE HTML>
<html>
<head>
<link type="text/css" rel="stylesheet"
href="/files/tutorial/browser/animation/animate.css">
<script>
function move(elem) {
var left = 0
function frame() {
left++ // update parameters
elem.style.left = left + 'px' // show frame
if (left == 100) // check finish condition
clearInterval(id)
}
var id = setInterval(frame, 10) // draw every 10ms
}
</script>
</head>
<body>
<div onclick="move(this.children[0])" class="example_path">
<div class="example_block"></div>
</div>
</body>
</html>
動畫重構(gòu)
為了使我們的動畫函數(shù)具有通用性,我們引入一些變量:
- delay:幀之間的間隔
- duration: 整個動畫的用時
- start: 動畫開始時間
- timePassed: 動畫已經(jīng)消耗的時間
- progress: 動畫的時間進度(timePassed/duration)0~1
- delta(progress): 這是一個函數(shù)笙以,代表時間進度(progress)和動畫進度的關(guān)系淌实。
例如:
但是我們也許希望我們的動畫開始的時候慢一些,然后后面加速猖腕,例如在時間0.5的時候動畫進度0.25拆祈,然后越來越快,知道100%倘感。
delta(progress)是時間進度和動畫進度的對應(yīng)關(guān)系放坏。
動畫進度不是height
而是currentHeight/height
,是一個比值老玛。
文章后面我們將通過例子研究幾種delta淤年。
- step(delta):這個函數(shù)是真正執(zhí)行動畫的函數(shù),它接受delta參數(shù)蜡豹,應(yīng)用delta來處理element.style麸粮,對于高度的例子,將是:
function step(delta){
elem.style.height = 100 * delta + '%';
}
總結(jié)一下這些主要的參數(shù):
- delay 是 setInterval 第二個參數(shù)
- duration 是這個動畫需要多長時間完成
- progress 是時間的進度(currentTime/duration)
- delta 是時間進度和動畫進度的對應(yīng)關(guān)系余素,根據(jù)當前時間來算出動畫的進度豹休。
- step 真正執(zhí)行動畫(操作
elem.style
)的函數(shù)。它接受當前動畫進度桨吊,然后應(yīng)用到element上威根。
通用的動畫函數(shù)
讓我們利用上面討論的這些參數(shù)來實現(xiàn)一個輕量的,可擴展的動畫核心视乐。
動畫函數(shù)僅僅管理動畫的時間洛搀,把其他工作留給delta和step。(這符合一般的設(shè)計原理佑淀,把固定的東西抽離出來留美,這里是時間的管理)。
function animate(opts){
var start = new Date;
var id = setInterval(function(){
var timePassed = new Date - start;
var progress = timePassed / opts.duration;
if(progress > 1) progress = 1;
var delta = opts.delta(progress);
opts.step(delta);
if(progress == 1) clearInterval(id);
}, opts.delay || 10);
}
opts應(yīng)該包括:
- delay
- duration
- step
- delta
例子
function move(element, delta, duration) {
var to = 500
animate({
delay: 10,
duration: duration || 1000, // 1 sec by default
delta: delta,
step: function(delta) {
element.style.left = to*delta + "px"
}
})
}
用戶提供duration, duration, delta 然后把工作托付給animate。
delta = function(p){ return p; }
:代表動畫是線性的谎砾。
step
:利用delta
的結(jié)果(0~1)來處理動畫逢倍。
用法如下:
<div onclick="move(this.children[0], function(p) {return p})" class="example_path">
<div class="example_block"></div>
</div>