back back-top comments magnifier menu mobile right smile views

JS动画 PK CSS3动画(上)

Jean
Jean

通过这篇文章,你会了解到 为什么一些 JS 动画库(比如知名的 velocity.js)能做到比 CSS3 动画 更高效和强大,以及如何灵活的在项目中选择最适合的动画实现方式。

关于 web 动画的实现方式,如今我们有很多选择:CSS3-animation、CSS3-transition、requestAnimationFrame、jQuery.animate、setInterval / setTimeout、SVG、Canvas、Gif 等…… 这些主要分为两大阵营:基于DOM操作的 JS 动画CSS3 动画,两者 PK 其实各有千秋!

先来介绍下主要的几种动画实现方式,以及它们各自的特点和优劣:

setInterval 和 setTimeout

在 CSS3 出现前,setInterval 或 setTimeout 方法几乎是实现动画的唯一选择,例如我们常用的 jQueryanimate() 方法,它的底层就是利用 setInterval 实现,通过定时动态修改 DOM 和 CSS 实现动画。

但这种动画方式会经常出现令人心塞的卡顿。其实话说 jQuery 的设计初衷是为了用户更便利高效的操作 DOM,并不是为了动画!

setInterval 或 setTimeout 会使动画过程中不断调用js,这样会消耗大量内存,使JS引擎每隔一段时间触发垃圾回收机制(GC),导致卡顿。

下面是一个用 setInterval 实现让元素宽度变化为 200px 的动画小例子:

var box = document.getElementById('box');

var timer = setInterval(function () {
  var curWidth = box.offsetWidth; // 获取元素当前的宽度
  if (curWidth < 200) {
    box.style.width = curWidth + 5 + 'px';
  } else {
    clearInterval(timer);
  }
}, 1000/60); // 1000/60毫秒代表每秒60帧,人眼最低能捕捉每秒60帧的画面

执行

requestAnimationFrame

如果说 setInterval 或 setTimeout 是 CCS3 出现之前 实现 Web 动画的曲线救国路线~
那么 requestAnimationFrame 就成为救世主了,它是 W3C 专门为了实现高性能的帧动画而设计的一个API。

它有以下优点

  • 1. 优化 DOM 操作,比如它将每一帧中的所有 DOM 操作集中在一次重绘或回流中就完成,并且重绘或回流的时间间隔紧随浏览器的刷新频率(一般是每秒60帧),动画更加流畅
  • 2. 窗口失去焦点时,动画将停止,元素隐藏或不可见时,就不进行重绘或回流,节省很多 cpu 和内存
  • 3. 更省电,尤其对移动设备

为了深入研究 Web 动画的实现原理,先来瞧瞧这个有趣的手绘翻页动画~ 在空白笔记本每页画上不同的动作,然后不停翻页 神奇的产生了动画!

flip-animation-1

浏览器实现动画的 原理 和翻页动画相似,通过不断更新绘制 DOM 和 CSS 形成动画,大部分浏览器的显示频率是 16.7ms(约为一秒60帧,可以理解为1秒刷新60次),我们肉眼是感觉不到这个频率变化的。

setInterval 或 setTimeout 做的动画根据人为定义的时间间隔绘制,如果间隔定义小于浏览器显示频率(16.7ms)假设为10ms,就会导致第三帧丢失!如果在16.7ms之间再插入一个 setInterval 那就更糟了,动画只能断续显示,而且计数器频率越小 对电池寿命的影响越大,对 Web 性能也百害无一利。这也是为何使用 setInterval 或 setTimeout 最小时间间隔设为 1000/60(即16.7ms)的原因了。

requestAnimationFrame 的不同之处在于 它会自动根据浏览器的显示频率绘制,这样动画就不会导致丢帧,自然比较流畅了。

我们来用 requestAnimationFrame 改写下之前那个宽度变化到200px的动画小例子:

var box = document.getElementById('box');

function step() {
  var curWidth = box.offsetWidth; // 获取元素当前的宽度

  curWidth += 5;
  box.style.width = curWidth + 'px';

  // 如果元素当前的宽度小于200px 就调用函数自身 循环绘制,直到满足条件才停止
  if (curWidth < 200) {
    requestAnimationFrame(step);
  }
}

requestAnimationFrame(step);

执行

requestAnimationFrame(callback) 方法接收一个回调函数作为参数,每调用一次 requestAnimationFrame 就绘制一次,循环调用实现了连续的动画。

requestAnimationFrame 浏览器兼容性 还是不错的,桌面端除了 IE9 及之前的版本,移动端除了 Opera Mini 和 Android Browser4.3 以及之前的版本,几乎所有现代浏览器都支持。下图是在 caniuse 上测试的结果:

requestanimationframe-support

在 Canvas 和 SVG 里做动画就可以用 requestAnimationFrame,他们是完美的搭档,这里先不细说了,下次会详细做一个专题来聊聊 Canvas 和 SVG。

有个好消息,近期发布的 jQuery3 中,动画相关的方法已经使用了 requestAnimationFrame,如果你感兴趣,也可以来围观  jQuery3.0 带来哪些新特性

CSS3 transition 或 animation

CSS3 有两种实现动画的方式:transition(过渡动画)和 animation(帧动画),使用和 requestAnimationFrame 类似的机制。

transition 改写之前的例子,只需要在 CSS 里加个过渡属性(transition):

#box {
  width: 50px;
  height: 50px;
  transition: all .6s ease-out;
}

在需要执行动画时,用 js 动态设置元素的最终属性,会自动形成过渡动画

var box = document.getElementById('box');
box.style.width = '200px';

执行

但更好的选择是把动画的最终状态单独放在一个 class 中,由 JS 控制:

.active { width: 200px }
btn.addEventListener('click', function () {
  box.classList.add = 'active';
});

animation 帧动画,顾名思义 需要定义一些关键帧,玩过 flash 的 一定感到很熟悉,改写之前的例子:

@keyframes changeWide {
  0% {
    width: 50px;
  }

  100% {
    width: 200px;
  }
}

@-webkit-keyframes changeWide {
  0% {
    width: 50px;
  }

  100% {
    width: 200px;
  }
}

#box {
  -webkit-animation-name: changeWide;
  animation-name: changeWide;    /* 执行动画的名称(@keyframes 所定义的)*/
  -webkit-animation-duration: .8s;
  animation-duration: .8s;       /* 动画执行时间 */
  -webkit-animation-fill-mode: forwards;
  animation-fill-mode: forwards; /* 动画完成后,保持最后一个关键帧的属性值 */
}

执行

既然用 CSS3 做动画方便又高效,那是不是就用不着 JS 了呢?

当然不~ 如果你希望动画特效能向下兼容 PC 端的 IE8-9,就不得不使用 setInterval 或 setTimeout 了,下图数据表明国内市场仍有一部分同学在使用万恶的 IE8!不过,微软早在年初就宣布停止对IE 8/9/10的技术支持,老版的 IE 浏览器将会慢慢淡出人们的视线。

browser-market-share-9-30

CSS3 好写好用,但也并不能解决所有需求,例如实现一个回顶的滚动动画 会涉及 scrollTop 属性,CSS 就无法控制了,还得由 JS 出马。PS:如果想实现一个链式动画,比如先变宽 再向右平移 接着翻转…… 用纯 CSS3 实现?一脸懵逼了吧,还是 jQuery.animate 来的方便。

CSS3 动画的优点也很明显:

  • 1. 通过优化 DOM 操作,0GC 避免内存消耗来减少卡顿
  • 2. 使用与 requestAnimationFrame 类似的机制
  • 3. 可以强制使用硬件加速 (通过 GPU 来提高动画性能)

为了避免文章篇幅过长而影响阅读体验,特意分成了上下两部分。

JS动画 PK CSS3动画(下) 将告诉你如何在项目中选择和组合 最优的动画实现方案。

本文由 前端先生 原创,欢迎转载分享,但请注明出处。

0条评论

发表评论

电子邮件地址不会被公开。 必填项已用*标注

扫描二维码分享到微信