1. 程式人生 > >【前端動畫】實現動畫的6種方式

【前端動畫】實現動畫的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方式**。