实现动画的方案主要有6种:Javascript直接实现动画,可伸缩矢量图形(SVG)动画,CSS transition,CSS3 animation、Canvas动画、requestAnimationFrame。
javascript实现
<!DOCTYPE html>
<html>
<head>
<style>
.content{width: 100px;height: 100px;background-color: red;}
</style>
</head>
<body>
<div class="content"></div>
<script>
var ele=document.getElementsByClassName("content")[0];
var left=0;
let timer=setInterval(function () {
if(left<window.innerWidth-100){
ele.style.marginLeft=left+'px';
left++;
} else {
clearInterval(timer);
}
},16);
</script>
</body>
</html>
缺点:通过Javascript实现动画通常会导致页面频繁重排重绘,很消耗性能。
按照常理来说,改变元素位置会产生重排,为什么上面图中显示的全是重绘呢?原因是绝对定位会建立一个新的图层,而此图层上只有当前一个元素,所以只会重绘,而不会重排。这也告诉我们,在同一层中,元素数量少的情况下,重排性能对更好,速度会更快。
CSS3 transition
<!DOCTYPE html>
<html>
<head>
<style>
.content{
width: 100px;
height: 100px;
background-color: red;
transition: all 10s ease-in-out 0s;
-webkit-transition: all 10s ease-in-out 0s;
margin-left: 0;
}
.right{
width: 100px;
height: 100px;
margin-left: 400px;
background-color: blue;
}
</style>
</head>
<body>
<div class="content"></div>
<script>
var timer=setTimeout(function () {
var content=document.getElementsByClassName("content")[0];
content.setAttribute('class','right');
},500);
</script>
</body>
</html>
为什么 transform 没有触发 repaint 呢?简而言之,transform 动画由GPU控制,支持硬件加速,并不需要软件方面的渲染。
硬件加速原理
浏览器接收到页面文档后,会将文档中的标记语言解析为DOM树。DOM树和CSS结合后形成浏览器构建页面的渲染树。渲染树中包含了大量的渲染元素,每一个渲染元素会被分到一个图层中,每个图层又会被加载到GPU形成渲染纹理,而图层在GPU中transform 是不会触发 repaint 的,最终这些使用 transform 的图层都会由独立的合成器进程进行处理。
CSS3 animation
<!DOCTYPE html>
<html>
<head>
<style>
.content{
width: 100px;
height: 100px;
background-color: red;
transition: all 10s ease-in-out 0s;
-webkit-transition: all 10s ease-in-out 0s;
animation: move 4s infinite;
margin-left: 0;
}
@keyframes move {
from{
margin-left: 0;
}
50%{
margin-left: 400px;
}
to{
margin-left: 0;
}
}
</style>
</head>
<body>
<div class="content"></div>
</body>
</html>
并不是所有的CSS属性都能触发GPU的硬件加速(图层在GPU中属性改变不会触发 repaint ),实际上只有少数属性可以,比如下面的这些:
transform
opacity
filter
Canvas动画
<!DOCTYPE html>
<html>
<head>
</head>
<body>
<canvas id="canvas" width="700" height="500"></canvas>
<script>
var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
var left=0;
var timer=setInterval(function () {
ctx.clearRect(0,0,700,550);
ctx.beginPath();
ctx.fillStyle="#f00";
ctx.fillRect(left,0,100,100);
ctx.stroke();
if(left>700){
clearInterval(timer);
}
left+=1;
},16);
</script>
</body>
</html>
requestAnimationFrame
<!DOCTYPE html>
<html>
<head>
<style>
div{width: 100px;height: 100px;background-color: red;}
</style>
</head>
<body>
<div id="box" width="700" height="500"></div>
<script>
window.requestAnimationFrame=window.requestAnimationFrame||
window.mozRequestAnimationFrame || window.webkitRequestAnimationFrame ||
window.msRequestAnimationFrame;
let element=document.getElementById("box");
let left=0;
requestAnimationFrame(step);
function step() {
if(left<window.innerWidth-200){
left+=1;
element.style.marginLeft=left+"px";
requestAnimationFrame(step);
}
}
</script>
</body>
</html>