【前端動畫】實現動畫的6種方式
引言
通常在前端中,實現動畫的方案主要有6種:
- javascript直接實現;
- SVG(可伸縮向量圖形);
- CSS3 transition;
- CSS3 animation;
- Canvas動畫;
- requestAnimationFrame;
javascript 直接實現動畫
其主要思想是通過setInterval或setTimeout方法的回撥函式來持續呼叫改變某個元素的CSS樣式以達到元素樣式變化的效果。
示例
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <style type="text/css"> #rect { width: 200px; height: 200px; background: #ccc; } </style> </head> <body> <div id="rect"></div> <script> let elem = document.getElementById('rect'); let left = 0; let timer = setInterval(function(){ if(left<window.innerWidth-200){ elem.style.marginLeft = left+'px'; left ++; }else { clearInterval(timer); } },16); </script> </body> </html>
Jquery的animate()方法就是這種方式實現的。
存在的問題
javascript 實現動畫通常會導致頁面頻繁性重排重繪,消耗效能,一般應該在桌面端瀏覽器。在移動端上使用會有明顯的卡頓。
Tip:為什麼是16ms
上面例子中,我們設定的setInterval時間間隔是16ms。一般認為人眼能辨識的流暢動畫為每秒60幀,這裡16ms比(1000ms/60)幀略小一些,但是一般可仍為該動畫是流暢的。
在很多移動端動畫效能優化時,一般使用16ms來進行節流處理連續觸發的瀏覽器事件。例如對touchmove、scroll事件進行節流等。通過這種方式減少持續事件的觸發頻率,可以大大提升動畫的流暢性。
SVG
SVG動畫由SVG元素內部的元素屬性控制,一般通過一下幾個元素控制:
- : 用於控制動畫延時
- :對屬性的連續改變進行控制
- :顏色變化,但用就能控制
- :控制如縮放、旋轉等幾何變化
- :控制SVG內元素的移動路徑
示例
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Document</title> <style> *{ margin:0; padding:0; } </style> </head> <body> <svg id="box" width="800" height="400" xmlns="http://www.w3.org/2000/svg" version="1.1"> <rect x="" y="" width="100" height="100" fill="rgb(255,0,0);" stroke="" stroke-width=""> <set attributeName="x" attributeType="XML" to="100" begin="4s"/> <animate attributeName="x" attributeType="XML" begin="0s" dur="4s" from="0" to="300"/> <animate attributeName="y" attributeType="XML" begin="0s" dur="4s" from="0" to="0"/> <animateTransform attributeName="transform" begin="0s" dur="4s" type="scale" from="1" to="2" repeatCount="1" /> <animateMotion path="M10,80 q100,120 120,20 q140,-50 160,0" begin="0s" dur="4s" repeatCount="1" /> </rect> </svg> </body> </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,在效能上比另兩者要好。
前面提到,大多數顯示器的重新整理頻率是60Hz,大概相當於每秒鐘重繪60次。大多數瀏覽器都會對重繪操作加以限制,不超過顯示器的重繪頻率,因為即使超過這個頻率使用者體驗也不會提升。
因此,最平滑動畫的最佳迴圈間隔是 1000ms/60 ,約16ms
。這個迴圈間隔重繪的動畫是最平滑的,因為這個速度最接近瀏覽器的最高限速。
通常,我們將執行動畫的每一步傳到requestAnimationFrame中,在每次執行完後進行非同步回撥來連續觸發動畫效果。
示例
<!DOCTYPE html>
<html>
<head>
<title></title>
<style type="text/css">
* {
margin:0;
padding:0;
}
div {
width: 200px;
height: 200px;
background-color: #ccc;
}
</style>
</head>
<body>
<div id="rect"></div>
<script type="text/javascript">
window.requestAnimationFrame = window.requestAnimationFrame || window.mozRequestAnimationFrame || window.webkitRequestAnimationFrame ||
window.msRequestAnimationFrame;
let elem = document.getElementById("rect");
let left = 0;
//自動執行持續性回撥
requestAnimationFrame(step);
//持續該改變元素位置
function step() {
if(left<window.innerWidth-200){
left+=1;
elem.style.marginLeft = left+"px";
requestAnimationFrame(step);
}
}
</script>
</body>
</html>
我們注意到,requestAnimationFrame
只是將回調的方法傳入到自身的引數中執行,而不是通過setInterval
呼叫。你要知道,無論是setInterval()
還是setTimeout()
都不十分精確。為它們傳入的第二個引數,實際上只是指定了把動畫程式碼新增到瀏覽器UI執行緒佇列中以等待執行時間。如果佇列前面已經加入其他任務,那動畫程式碼就要等前面的任務完成後再執行。
總結
複雜的動畫是通過一個個簡單的動畫組合實現的。基於相容性問題,通常在專案中,一般在
- 桌面端瀏覽器推薦使用javascript直接實現動畫或SVG方式;
- 移動端可以考慮使用CSS3 transition、CSS3
animation、Canvas或requestAnimationFrame方式**。