作為一只前端狗,我們的使命就是在滿足產(chǎn)品需求、實(shí)現(xiàn)交互設(shè)計(jì)的基礎(chǔ)上,將最好的體驗(yàn)呈現(xiàn)給用戶爸爸們。在保證性能的同時(shí),我們通常會(huì)給頁(yè)面加一些動(dòng)態(tài)效果,以增強(qiáng)頁(yè)面的表現(xiàn)力并提升頁(yè)面的交互體驗(yàn)。故將前端實(shí)現(xiàn)動(dòng)效的幾種常用方式整理成此篇小結(jié),以求溫故而知新,如有不當(dāng)還望多多指正。

動(dòng)畫即童年
動(dòng)畫是指由許多幀靜止的畫面,以一定的速度(如每秒16張)連續(xù)播放時(shí),肉眼因視覺(jué)殘象產(chǎn)生錯(cuò)覺(jué),而誤以為畫面活動(dòng)的作品。-- 維基百科
以上是維基百科上給出的動(dòng)畫的定義。相信每一個(gè)像我這樣有童年的孩子,應(yīng)該都玩過(guò)手翻書,或者就算你的童年稍微暗淡一點(diǎn),應(yīng)該也看過(guò)動(dòng)畫片吧...嗯嗯,并沒(méi)有跑題,其實(shí)這和我們今天提及的動(dòng)畫本質(zhì)上是一樣的,只不過(guò)就是呈現(xiàn)方式或者說(shuō)載體發(fā)生了改變。

幾個(gè)基本概念
簡(jiǎn)單介紹幾個(gè)關(guān)于動(dòng)畫的基本概念:
幀:在動(dòng)畫過(guò)程中,每一幅靜止畫面即為一“幀”;
幀率:即每秒鐘播放的靜止畫面的數(shù)量,單位是fps(Frame per second)或赫茲(Hz);
幀時(shí)長(zhǎng):即每一幅靜止畫面的停留時(shí)間,單位一般是ms(毫秒);
丟幀:在幀率固定的動(dòng)畫中,某一幀的時(shí)長(zhǎng)遠(yuǎn)高于平均幀時(shí)長(zhǎng),導(dǎo)致其后續(xù)數(shù)幀被擠壓而丟失的現(xiàn)象;
我們?cè)陲@示器上看到的動(dòng)畫,每一幀變化都是系統(tǒng)繪制出來(lái)的(GPU或者CPU)。它的最高繪制頻率受限于顯示器的刷新頻率(而非顯卡,大多數(shù)是60Hz或者75Hz)。
幀頻越高,屏幕上圖片閃爍感就越小,穩(wěn)定性也就越高。人的眼睛不容易察覺(jué)75Hz以上刷新頻率帶來(lái)的閃爍感。
實(shí)現(xiàn)方式
通常我們?cè)谇岸藢?shí)現(xiàn)動(dòng)畫效果的幾種主要實(shí)現(xiàn)方式如下:
- JavaScript:通過(guò)定時(shí)器(setTimeout 和 setIterval)來(lái)間隔來(lái)改變?cè)貥邮剑蛘呤褂胷equestAnimationFrame;
- CSS3:transition 和 animation;
- HTML5:使用HTML5提供的繪圖方式(canvas、svg、webgl);

requestAnimationFrame
requestAnimationFrame是瀏覽器用于定時(shí)循環(huán)操作的一個(gè)接口,類似于setTimeout,主要用途是按幀對(duì)網(wǎng)頁(yè)進(jìn)行重繪。
設(shè)置這個(gè)API的目的是為了讓各種網(wǎng)頁(yè)動(dòng)畫效果(DOM動(dòng)畫、Canvas動(dòng)畫、SVG動(dòng)畫、WebGL動(dòng)畫)能夠有一個(gè)統(tǒng)一的刷新機(jī)制,從而節(jié)省系統(tǒng)資源,提高系統(tǒng)性能,改善視覺(jué)效果。代碼中使用這個(gè)API,就是告訴瀏覽器希望執(zhí)行一個(gè)動(dòng)畫,讓瀏覽器在下一個(gè)動(dòng)畫幀安排一次網(wǎng)頁(yè)重繪。
requestAnimationFrame使用一個(gè)回調(diào)函數(shù)作為參數(shù),這個(gè)回調(diào)函數(shù)會(huì)在瀏覽器重繪之前調(diào)用,由于功效只是一次性的,所以想實(shí)現(xiàn)連續(xù)的動(dòng)效,需要遞歸調(diào)用,示例如下:
<div id="demo" style="position:absolute; width:100px; height:100px; background:#ccc; left:0; top:0;"></div>
<script>
var demo = document.getElementById('demo');
function render(){
demo.style.left = parseInt(demo.style.left) + 1 + 'px'; //每一幀向右移動(dòng)1px
}
requestAnimationFrame(function(){
render();
//當(dāng)超過(guò)300px后才停止
if(parseInt(demo.style.left) <= 300) requestAnimationFrame(arguments.callee);
});
</script>
cancelAnimationFrame方法用于取消重繪:
var requestID = requestAnimationFrame(repeatOften);
cancelAnimationFrame(requestID);
使用requestAnimationFrameAPI的優(yōu)勢(shì)如下:
- 會(huì)把每一幀中的所有DOM操作集中起來(lái),在一次重繪或回流中就完成,并且重繪或回流的時(shí)間間隔緊緊跟隨顯示器的刷新頻率(60 Hz或者75 Hz);
- 在隱藏或不可見的元素中,將不會(huì)進(jìn)行重繪或回流,這當(dāng)然就意味著更少的的cpu,gpu和內(nèi)存使用量;
目前,主要瀏覽器Firefox 23 / IE 10 / Chrome / Safari)都支持這個(gè)方法??梢杂孟旅娴姆椒ǎ瑱z查瀏覽器是否支持這個(gè)API。如果不支持,則自行模擬部署該方法。
window.requestAnimFrame = (function(){
return window.requestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.oRequestAnimationFrame ||
window.msRequestAnimationFrame ||
function(/* function FrameRequestCallback */ callback, /* DOMElement Element */ element){
window.setTimeout(callback, 1000 / 60);
};
})();
所以,可以這么說(shuō),requestAnimationFrame就是一個(gè)性能優(yōu)化版、專為動(dòng)畫量身打造的setTimeout,不同的是requestAnimationFrame不是自己指定回調(diào)函數(shù)運(yùn)行的時(shí)間,而是跟著瀏覽器內(nèi)建的刷新頻率來(lái)執(zhí)行回調(diào),這當(dāng)然就能達(dá)到瀏覽器所能實(shí)現(xiàn)動(dòng)畫的最佳效果了。
Transition
CSS 中的 transition 屬性允許塊級(jí)元素中的屬性在指定的時(shí)間內(nèi)平滑的改變,簡(jiǎn)單看下其語(yǔ)法規(guī)則:
transition: property duration timing-function delay;
具體屬性值介紹如下:
| 值 | 描述 |
|---|---|
| transition-property | 規(guī)定設(shè)置過(guò)渡效果的 CSS 屬性的名稱。(none / all / property) |
| transition-duration | 規(guī)定完成過(guò)渡效果需要多少秒或毫秒。 |
| transition-timing-function | 規(guī)定速度效果的速度曲線。(linear、ease、ease-in、ease-out、ease-in-out、cubic-bezier(n,n,n,n)) |
| transition-delay | 定義過(guò)渡效果何時(shí)開始。 |
Animation
類似的CSS還提供了一個(gè)Animation屬性,不過(guò)區(qū)別于Transition,Animation作用于元素本身而不是樣式屬性,可以使用關(guān)鍵幀的概念,應(yīng)該說(shuō)可以實(shí)現(xiàn)更自由的動(dòng)畫效果。
語(yǔ)法
animation: name duration timing-function delay iteration-count direction;
具體屬性值介紹如下:
| 值 | 描述 |
|---|---|
| animation-name | 規(guī)定需要綁定到選擇器的 keyframe 名稱。(keyframename、none) |
| animation-duration | 規(guī)定完成動(dòng)畫所花費(fèi)的時(shí)間,以秒或毫秒計(jì)。 |
| animation-timing-function | 規(guī)定動(dòng)畫的速度曲線。(linear、ease、ease-in、ease-out、ease-in-out、cubic-bezier(n,n,n,n)) |
| animation-delay | 規(guī)定在動(dòng)畫開始之前的延遲。 |
| animation-iteration-count | 規(guī)定動(dòng)畫應(yīng)該播放的次數(shù)。 |
| animation-direction | 規(guī)定是否應(yīng)該輪流反向播放動(dòng)畫。 (normal、alternate) |
Canvas
<canvas>是HTML5新增的元素,作為頁(yè)面圖形繪制的容器,可用于通過(guò)使用JavaScript中的腳本來(lái)繪制圖形。例如,它可以用于繪制圖形,制作照片,創(chuàng)建動(dòng)畫,甚至可以進(jìn)行實(shí)時(shí)視頻處理或渲染,Canvas具有如下特點(diǎn):
- 依賴分辨率,基于位圖;
- 不支持事件處理器;
- 弱的文本渲染能力;
- 能夠以 .png 或 .jpg 格式保存結(jié)果圖像;
- 最適合圖像密集型的游戲,其中的許多對(duì)象會(huì)被頻繁重繪;
大多數(shù) Canvas 繪圖 API 都沒(méi)有定義在 <canvas> 元素本身上,而是定義在通過(guò)畫布的getContext()方法獲得的一個(gè)“繪圖環(huán)境”對(duì)象上。Canvas API也使用了路徑的表示法。但是,路徑由一系列的方法調(diào)用來(lái)定義,而不是描述為字母和數(shù)字的字符串,比如調(diào)用 beginPath() 和 arc() 方法。一旦定義了路徑,其他的方法,如 fill(),都是對(duì)此路徑操作。
SVG
SVG是英文Scalable Vector Graphics的縮寫,意為可縮放矢量圖形,用來(lái)定義用于網(wǎng)絡(luò)的基于矢量的圖形,其使用 XML 格式定義圖像,并且具有如下特點(diǎn):
- 不依賴分辨率,基于矢量圖;
- 支持事件處理器;
- 最適合帶有大型渲染區(qū)域的應(yīng)用程序(比如谷歌地圖);
- 復(fù)雜度高會(huì)減慢渲染速度(任何過(guò)度使用 DOM 的應(yīng)用都不快);
- 不適合游戲應(yīng)用;
來(lái)看一個(gè)簡(jiǎn)單的示例,用SVG畫了一個(gè)圓:
<svg xmlns="http://www.w3.org/2000/svg" version="1.1">
<rect x="50" y="20" rx="20" ry="20" width="150" height="150"
style="fill:red;stroke:black;stroke-width:5;opacity:0.5"/>
</svg>
SVG 代碼以 <svg> 元素開始,包括開啟標(biāo)簽 <svg> 和關(guān)閉標(biāo)簽 </svg> 。這是根元素。width 和 height 屬性可設(shè)置此 SVG 文檔的寬度和高度。version 屬性可定義所使用的 SVG 版本,xmlns 屬性可定義 SVG 命名空間。
SVG 的 <circle> 用來(lái)創(chuàng)建一個(gè)圓。cx 和 cy 屬性定義圓中心的 x 和 y 坐標(biāo)。如果忽略這兩個(gè)屬性,那么圓點(diǎn)會(huì)被設(shè)置為 (0, 0)。r屬性定義圓的半徑。
下面主要是介紹SVG中的幾個(gè)用于動(dòng)畫的元素,它們分別是:
<animate>:通常放置到一個(gè)SVG圖像元素里面,用來(lái)定義這個(gè)圖像元素的某個(gè)屬性的動(dòng)畫變化過(guò)程;
<animateMotion>:元素也是放置一個(gè)圖像元素里面,它可以引用一個(gè)事先定義好的動(dòng)畫路徑,讓圖像元素按路徑定義的方式運(yùn)動(dòng);
<animateTransform>:元素對(duì)圖形的運(yùn)動(dòng)和變換有更多的控制,它可以指定圖形的變換、縮放、旋轉(zhuǎn)和扭曲等;
<mpath>:元素的用法在上面的例子里出現(xiàn)過(guò),它是一個(gè)輔助元素,通過(guò)它,<animateMotion>等元素可以引用一個(gè)外部的定義的<path>。讓圖像元素按這個(gè)<path>軌跡運(yùn)動(dòng);
WebGL
WebGL使得網(wǎng)頁(yè)在支持HTML <canvas>標(biāo)簽的瀏覽器中,不需要安裝任何插件,便可以使用基于 OpenGL ES 2.0 的 API 在 canvas 中進(jìn)行3D渲染。 WebGL 程序由JavaScript的控制代碼,和在計(jì)算機(jī)的圖形處理單元(GPU)中執(zhí)行的特效代碼(shader code,渲染代碼) 組成。

WebGL 本質(zhì)上是基于光柵化的 API,而不是基于 3D 的 API。WebGL 只關(guān)注兩個(gè)方面,即投影矩陣的坐標(biāo)和投影矩陣的顏色。使用 WebGL 程序的任務(wù)就是實(shí)現(xiàn)具有投影矩陣坐標(biāo)和顏色的 WebGL 對(duì)象即可??梢允褂谩爸鳌眮?lái)完成上述任務(wù)。頂點(diǎn)著色器可以提供投影矩陣的坐標(biāo),片段著色器可以提供投影矩陣的顏色。
由于WebGL的體系比較龐大,三言兩語(yǔ)說(shuō)不完,所以以下僅提供各種傳送門了(不許說(shuō)我懶?。。?br>
WebGL 參考資料
WebGL API
幾個(gè)常用的動(dòng)畫庫(kù)
Ani.js -- 基于CSS動(dòng)畫的生命處理庫(kù)
Dynamics.js -- 創(chuàng)建具有物理運(yùn)動(dòng)效果動(dòng)畫的js庫(kù)
Animate.css -- 齊全的CSS3動(dòng)畫庫(kù)
Three.js -- 讓用戶通過(guò)javascript入手進(jìn)入搭建webgl項(xiàng)目的類庫(kù)