原文:http://javascript.info/tutori...javascript
一般,框架會爲你處理動畫。可是,你可能想知道僅僅用javascript怎麼來實現動畫,和可能出現的一些問題。理解這項技術對於建立複雜的動畫是頗有幫助的,即便在框架的幫助下。css
javascript的動畫是經過週期性的改變DOM元素的style 或者 canvas 的對象。
整個的動畫過程被分紅了很小的步驟(step),每個步驟被定時器調用。由於定時器的週期很是短,因此動畫看起來是連續的。
僞代碼:html
var id = setInterval(function(){ /*當前顯示幀*/ if(/*完成*/) clearInterval(id) }, 10)
上面代碼每一幀的間隔是 10ms ,意味着每秒鐘有100幀。
在大多數的javascript框架中 10-15ms的delay是默認的。越短的延時讓動畫看起來更加流暢,可是隻有在瀏覽器足夠快的時候,每一步的動畫纔會準時運行。
若是動畫須要許多計算,CPU可能會100%的負載,動畫就會變得遲緩。這種狀況下,delay就應該被增長。例如,delay 40ms 就是每秒25幀,接近24幀的電影標準。java
setInterval
而不是用setTimout
(其實在現代瀏覽器中大多使用requestAnimationFrame)咱們使用setInterval,而不是遞歸的使用setTimeout,是由於咱們想要一幀一個delay,而不是全部幀之間有一個固定的delay.查閱Understanding timers: setTimeout and setInterval來了解setInterval和遞歸的setTimeout之間到底有什麼不一樣。(譯者:經測試最新的chrome > 56中,setInterval的行爲跟本文中描述的不一樣。當函數執行時間超過了delay時間,下一個函數不會立刻運行,仍然會等一個delay的間隔,再執行。但本文仍有參考價值)web
例如,一個元素移動經過改變element.stye.left從0到100px。每10ms改變1px。算法
<html> <head> <link type="text/css" rel="stylesheet" href="/files/tutorial/browser/animatio/animate.css"> <script> function move(elem) { var left = 0; function frame(){ left++ // 更新參數 elem.style.left = left + 'px' // 顯示幀 if(left === 100) //檢查結束條件 clearInterval(id) } var id = setInterval(frame, 10) //沒10ms繪製一次 } </script> </head> <body> <div onclick="move(this.children[0])" class="example_path"> <div class="example_block"></div> </div> </body> </html>
在新的窗口打開chrome
爲了讓動畫更加通用,咱們介紹下面的一些參數:編程
delay
每一幀之間的間隔(ms).例如,10mscanvas
duration
整個動畫完成須要的時間(ms)。例如:1000ms瀏覽器
當動畫開始的時候,咱們也能夠用:
start 動畫開始的時間,start = new Date
動畫過程的核心,每一幀咱們須要計算:
timePassed
從動畫開始所通過的時間(ms)。
從0到動畫(duration)結束.可是偶爾可能會超過結束時間,由於瀏覽器的計時器並不許確。
progress
已通過去的動畫時間做爲分子,計算每一幀經過公式timePassed/duration
。值得範圍一般是0到1。
例如,progress的值爲0.5就是說動畫時間(duration)已通過去了一半。
delta(progress)
一個返回當前動畫進度的函數。
例如,咱們讓高度屬性從0%變化到100%。
咱們可讓動畫均勻的顯示,這樣動畫進度看起來就是線性的。
Mapping:
->progress = 0 -> height = 0%
->progress = 0.2 -> height = 20%
->progress = 0.5->height = 50%
->progress = 0.8 -> height = 80%
->progress = 1 -> height = 100%
可是咱們可能想讓動畫緩慢的開始而後再加速。這樣的話通過一半的動畫時間高度可能只有25%
,而後逐漸加速到100%。
Mapping:
->progress = 0 -> height = 0%
->progress = 0.2 -> height = 4%
->progress = 0.5->height = 25%
->progress = 0.8 -> height = 64%
->progress = 1 -> height = 100%
delta(progress) 是一個映射動畫進度增量的函數,
動畫進度一般是0到1之間的一個數字。
這篇文章會用一些例子進一步討論幾種增量函數
step(delta)
這個函數是實際上用來作這件事的函數。
它計算出增量的結果而且應用它。
對於這個高度的例子,他們多是:
function state(delta) { elem.style.height = 100*delta + "%" }
到如今爲止幾個重要的參數是:
-> delay是setInterval
的第二個參數。
-> duration是動畫完成須要的時間。
-> progress是動畫已經通過的時間,除以duration使它的值在0到1之間。
-> delta經過當前的時間,計算當前的動畫進度。
-> step作了視覺上(?)的工做。它得到當前的動畫進度,而且把它應用在元素上。
讓咱們把上面討論的簡單的寫成一個可擴展的動畫核心。
下面的動畫函數執行時間管理而且把工做分配給delta和step:
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) }
參數對象應該包含如下的一些動畫屬性:
-> delay
-> duration
-> function delta
-> function step
這個算法徹底遵循上面的描述
讓咱們基於這個來建立一個移動的動畫
function move(element, delta, duration){ var to = 500; animate({ delay: 10, duration: duration || 1000, delta: delta, step: function(delta){ element.style.left = to * delta + 'px' } }) }
它把工做指派給animate
,給animate傳入了delay,用戶提供的duration
, delta
, 和 step
。
delta = function(p) { return p}
意味着動畫進程一直是均勻的
step
用一個簡單的公式映射0..1,delta返回一個進度值 0..to。把這些結果應用到element.style.left。
用法:
<div onclick="move(this.children[0], function(p) {return p})" class="example_path"> <div class="example_block"></div> </div>
動畫就是是根據給定的規則,一直改變屬性。在javascript動畫中,這個規則就是delta函數來實現的。
不一樣的delta使動畫的速度,加速度和其餘的參數表現出各類各樣的形式。
數學公式一般被用在這裏。可是它們對於只作web編程和忘記學校裏的數學的人來講,可能很陌生。在這個章節,咱們將瀏覽不少的受歡迎的公式並看一下它們是如何工做的。
動畫運動的例子,提供不一樣的delta.
線性 delta
function linear(progress){ return progress; }
水平方向指的是時間進度,垂直方向指的是動畫進程。
咱們已經能看見了。線性的delta使動畫以固定的速度進行。
Power of n
也是一個簡單的例子。delta是progress的n次方。例如2次,3次方等。
例如2次方
function quad(progress) { return Math.pow(progress, 2) }
增長加速度的影響。例如,下面這個圖片是5次方。
Circ: 圓的一部分
function circ(progress) { return 1 - Math.sin(Math.acos(progress)) }
Back: the bow function
這個函數向弓同樣工做:首先咱們"拉開工,而後發射出去"。
不像先前的函數,它會依賴於一個附加的參數x,這就是「彈性係數」。
它定義了「拉弓」的距離。
代碼是:
function back(progress, x) { return Math.pow(progress, 2) * ((x + 1) * progress - x) }
圖像x=1.5
bounce(彈跳)
想象一下咱們釋放一個球,它掉在地上,而後彈跳幾回,最後中止。
bounce事實上作了相反的事情。屬性將會一直改變知道它達到目標點。
這個函數比以前要複雜一些,也沒有簡單的數學公式。
function bounce(progress) { for(var a = 0, b=1, result; 1; a+=b, b /= 2) { if (progress >= (7-4*a) / 11){ return -Math.pow(11 - 6 * a - 11 * progress) / 4, 2) + Math.pow(b, 2) } } }
*這段代碼曲子 MooTools.FX.Transitions.據我所知,仍然有其餘實現bounce的算法。
Elastic 彈性
這個函數也依賴於額外的參數x,x定義了初始範圍。
function elastic(progress, x){ return Math.pow(2, 10 * (progress - 1)) * Math.cos(20 * Math.PI * x/3 *progress) }
圖像 x=1.5
在這個例子中,爲了讓動畫更加平滑,時間是2s.
反向函數(Reverse functions)
一個javascript框架常常會提供的一種delta函數。
它們的直接使用被稱做 easeIn.
偶爾的時候,須要以時間倒退的方式來展現動畫。這就叫作easeout 被'time-reversing'delta來實現。
未完待續
第一次翻譯點東西,質量很差,主要是作記錄用。裏面有不少比較'術語'的,很差翻,最好看原文。後面我也會繼續修改。