通常在前端中,實作動畫的方案主要有6種:
- javascript直接實作;
- SVG(可伸縮矢量圖形);
- CSS3 transition;
- CSS3 animation;
- Canvas動畫;
- requestAnimationFrame;
javascript 直接實作動畫
其主要思想是通過setInterval或setTimeout方法的回調函數來持續調用改變某個元素的CSS樣式以達到元素樣式變化的效果。
示例
1 <!DOCTYPE html>
2 <html lang="en">
3 <head>
4 <meta charset="UTF-8">
5 <style type="text/css">
6 #rect {
7 width: 200px;
8 height: 200px;
9 background: #ccc;
10 }
11 </style>
12 </head>
13 <body>
14 <div id="rect"></div>
15 <script>
16 let elem = document.getElementById(\'rect\');
17 let left = 0;
18 let timer = setInterval(function(){
19 if(left<window.innerWidth-200){
20 elem.style.marginLeft = left+\'px\';
21 left ++;
22 }else {
23 clearInterval(timer);
24 }
25 },16);
26 </script>
27 </body>
28 </html>
Jquery的animate()方法就是這種方式實作的。
存在的問題
javascript 實作動畫通常會導緻頁面頻繁性重排重繪,消耗性能,一般應該在桌面端浏覽器。在移動端上使用會有明顯的卡頓。
Tip:為什麼是16ms
上面例子中,我們設定的setInterval時間間隔是16ms。一般認為人眼能辨識的流暢動畫為每秒60幀,這裡16ms比(1000ms/60)幀略小一些,但是一般可仍為該動畫是流暢的。
在很多移動端動畫性能優化時,一般使用16ms來進行節流處理連續觸發的浏覽器事件。例如對touchmove、scroll事件進行節流等。通過這種方式減少持續事件的觸發頻率,可以大大提升動畫的流暢性。
SVG
SVG動畫由SVG元素内部的元素屬性控制,一般通過一下幾個元素控制:
- : 用于控制動畫延時
- :對屬性的連續改變進行控制
- :顔色變化,但用就能控制
- :控制如縮放、旋轉等幾何變化
- :控制SVG内元素的移動路徑
示例
1 <!DOCTYPE html>
2 <html lang="en">
3 <head>
4 <meta charset="UTF-8">
5 <title>Document</title>
6 <style>
7 *{
8 margin:0;
9 padding:0;
10 }
11 </style>
12 </head>
13 <body>
14 <svg id="box" width="800" height="400" xmlns="http://www.w3.org/2000/svg" version="1.1">
15 <rect x="" y="" width="100" height="100" fill="rgb(255,0,0);" stroke="" stroke-width="">
16 <set attributeName="x" attributeType="XML" to="100" begin="4s"/>
17 <animate attributeName="x" attributeType="XML" begin="0s" dur="4s" from="0" to="300"/>
18 <animate attributeName="y" attributeType="XML" begin="0s" dur="4s" from="0" to="0"/>
19 <animateTransform attributeName="transform" begin="0s" dur="4s" type="scale" from="1" to="2" repeatCount="1" />
20 <animateMotion path="M10,80 q100,120 120,20 q140,-50 160,0" begin="0s" dur="4s" repeatCount="1" />
21
22 </rect>
23 </svg>
24
25 </body>
26 </html>
這裡推薦一個在sublime text3中使用svg提示插件:svg snippet。
比較
SVG的一大優勢是含有較為豐富的動畫功能,原生繪制各種圖形、濾鏡和動畫,并且能被js調用。html是對dom的渲染,那麼svg就是對圖形的渲染。
但是,另一方面元素較多且複雜的動畫使用svg渲染會比較慢,而且SVG格式的動畫繪制方式必須讓内容嵌入到HTML中使用。CSS3的出現讓svg的應用變得相對少了。
CSS3 transition
transition是過度動畫。但是transition并不能實作獨立的動畫,隻能在某個标簽元素樣式或狀态改變時進行平滑的動畫效果過渡,而不是馬上改變。
注意
在移動端開發中,直接使用transition動畫會讓頁面變慢甚至卡頓。是以我們通常添加transform:translate3D(0,0,0)或transform:translateZ(0)來開啟移動端動畫的GPU加速,讓動畫過程更加流暢。
CSS3 animation
animation 算是真正意義上的CSS3動畫。通過對關鍵幀和循環次數的控制,頁面标簽元素會根據設定好的樣式改變進行平滑過渡。而且關鍵幀狀态的控制是通過百分比來控制的。
比較
CSS3最大的優勢是擺脫了js的控制,并且能利用硬體加速以及實作複雜動畫效果。
Canvas動畫
canvas作為H5新增元素,是借助Web API來實作動畫的。
示例
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<style>
*{
margin:0;
padding:0;
}
</style>
</head>
<body>
<canvas id="canvas" width="700" height="550"></canvas>
<script type="text/javascript">
let canvas = document.getElementById("canvas");
let ctx = canvas.getContext("2d");
let left = 0;
let timer = setInterval(function(){
ctx.clearRect(0,0,700,550);
ctx.beginPath();
ctx.fillStyle = "#ccc";
ctx.fillRect(left,0,100,100);
ctx.stroke();
if(left>700){
clearInterval(timer);
}
left += 1;
},16);
</script>
</body>
</html>
注釋:通過getContext()擷取元素的繪制對象,通過clearRect不斷清空畫布并在新的位置上使用fillStyle繪制新矩形内容實作頁面動畫效果。
比較
Canvas主要優勢是可以應對頁面中多個動畫元素渲染較慢的情況,完全通過javascript來渲染控制動畫的執行。可用于實作較複雜動畫。
requestAnimationFrame
requestAnimationFrame是另一種Web API,原理與setTimeout和setInterval類似,都是通過javascript持續循環的方法調用來觸發動畫動作。但是requestAnimationFrame是浏覽器針對動畫專門優化形成的APi,在性能上比另兩者要好。
通常,我們将執行動畫的每一步傳到requestAnimationFrame中,在每次執行完後進行異步回調來連續觸發動畫效果。
示例
1 <!DOCTYPE html>
2 <html>
3 <head>
4 <title></title>
5 <style type="text/css">
6 * {
7 margin:0;
8 padding:0;
9 }
10 div {
11 width: 200px;
12 height: 200px;
13 background-color: #ccc;
14 }
15 </style>
16 </head>
17 <body>
18 <div id="rect"></div>
19 <script type="text/javascript">
20 window.requestAnimationFrame = window.requestAnimationFrame || window.mozRequestAnimationFrame || window.webkitRequestAnimationFrame ||
21 window.msRequestAnimationFrame;
22
23 let elem = document.getElementById("rect");
24 let left = 0;
25 //自動執行持續性回調
26 requestAnimationFrame(step);
27 //持續該改變元素位置
28 function step() {
29 if(left<window.innerWidth-200){
30 left+=1;
31 elem.style.marginLeft = left+"px";
32 requestAnimationFrame(step);
33 }
34 }
35 </script>
36 </body>
37 </html>
我們注意到,requestAnimationFrame隻是将回調的方法傳入到自身的參數中執行,而不是通過setInterval調用。
總結
複雜的動畫是通過一個個簡單的動畫組合實作的。基于相容性問題,通常在項目中,一般在
- 桌面端浏覽器推薦使用javascript直接實作動畫或SVG方式;
-
移動端可以考慮使用CSS3 transition、CSS3
animation、Canvas或requestAnimationFrame方式**。