天天看點

【前端動畫】實作動畫的6種方式

通常在前端中,實作動畫的方案主要有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方式**。