JavaScript 工做原理之十三-CSS 和 JS 动画底层原理及如何优化其性能

原文请查阅这里,本文采用知识共享署名 4.0 国际许可协议共享,BY Trolandjavascript

本系列持续更新中,Github 地址请查阅这里css

这是 JavaScript 工做原理的第十三章。html

概述

正如你所知,动画在建立使人叹服的网络应用中扮演着一个关键角色。因为用户愈来愈注重用户体验,商户开始意识到完美,使人愉悦的用户体验的重要性,结果网络应用变得愈来愈重而且拥有更多动态交互的功能。这就要求网络应用提供更加复杂的动画来实现平滑的状态过渡贯穿于用户的使用过程中。如今,这已经司空见惯。用户变得愈来愈挑剔,他们潜意识期许能够得到快速响应和良好交互的用户界面。java

然而,让界面具备动画效果不必定是件简单的事情。动画的时机,方面及采用何种动画效果都是很模糊的概念。css3

JavaScript 和 CSS 动画比较

JavaScript 和 CSS 是建立网页动画的两条主要途径。两种不分好赖,看状况用吧。git

CSS 动画

使用 CSS 动画是让元素在屏幕上移动的最简单方法。github

咱们将会以如何让元素在 X 和 X 座标上移动元素 50 像素做为小示例开始。经过持续 1 秒的 CSS 过渡来移动元素。web

.box {
  -webkit-transform: translate(0, 0);
  -webkit-transition: -webkit-transform 1000ms;

  transform: translate(0, 0);
  transition: transform 1000ms;
}

.box.move {
  -webkit-transform: translate(50px, 50px);
  transform: translate(50px, 50px);
}
复制代码

当为元素添加 move 类的时候,改变 transform 的值而后开发发生过渡效果。浏览器

除了过渡持续时间,还有 easing 参数,它主要负责动画体验。该参数会在以后详细介绍。性能优化

若是经过以上的代码片断能够建立单独的样式类来操做动画,那么也可使用 JavaScript 来切换每一个动画。

以下元素:

<div class="box">
  Sample content.
</div>
复制代码

而后,使用 JavaScript 来切换每一个动画。

var boxElements = document.getElementsByClassName('box'),
    boxElementsLength = boxElements.length,
    i;

for (i = 0; i < boxElementsLength; i++) {
  boxElements[i].classList.add('move');
}
复制代码

以上代码片断为每一个包含 box 类的元素添加 move 类来触发动画。

这样作能够很好为你的网络应用提供很好的平衡。你就能够专一于使用 JavaScript 来操做应用状态,而后只需为目标元素设置合适的类,让浏览器来处理动画。如若你选择这么处理,就能够监听元素的 transitionend 事件,除了处理IE 老版本浏览器兼容问题以外。

以下监听 transitioned 事件,该事件会在动画结束时触发。

var boxElement = document.querySelector('.box'); // 获取第一个包含 box 类的元素
boxElement.addEventListener('transitionend', onTransitionEnd, false);

function onTransitionEnd() {
  // Handle the transition finishing.
}
复制代码

除了使用 CSS 过渡,还可使用 CSS 动画,CSS 动画可让你更好地控制单独的动画关键帧,持续时间以及循环次数。

关键帧是用来通知浏览器在规定的时间点上应有的 CSS 属性值而后填充空白。

看下例子:

/**
 * 该示例是没有包含浏览器前缀的精简版。加上之后会更加准确些。
 *
 */
.box {
  /* 选择动画名称 */
  animation-name: movingBox;

  /* 动画时长 */
  animation-duration: 2300ms;

  /* 动画循环次数 */
  animation-iteration-count: infinite;

  /* 每次奇数次循环时反转动画 */
  animation-direction: alternate;
}

@keyframes movingBox {
  0% {
    transform: translate(0, 0);
    opacity: 0.4;
  }

  25% {
    opacity: 0.9;
  }

  50% {
    transform: translate(150px, 200px);
    opacity: 0.2;
  }

  100% {
    transform: translate(40px, 30px);
    opacity: 0.8;
  }
}
复制代码

效果示例-sessionstack.github.io/blog/demos/…

经过使用 CSS 动画定义独立于目标元素的动画自己,而后设置元素的 animation-name 属性来使用想要的动画效果。

CSS 动画仍然是须要加浏览器前缀的,在 Safari, Safari 移动浏览器和 Android 端添加 -webkit- 前缀。Chrome, Opera, Internet Explorer, and Firefox 端所有不须要添加前缀。有不少工具能够用来建立包含任意前缀的样式,这样就不须要在源文件中带样式前缀。

可使用 autoprefixer 或者 cssnext 来自动为样式添加前缀。

JavaScript 动画

和 CSS 过渡或者 CSS 动画相比,使用 JavaScript 来建立动画要更加复杂些,可是通常而言,它会为开发进行提供强大的功能。

通常状况下,能够内联 JavaScript 动画做为代码的一部分。也能够把它们封装在其它对象之中。如下为复现以前描述的 CSS 过渡的 JavaScript 代码:

var boxElement = document.querySelector('.box');
var animation = boxElement.animate([
  {transform: 'translate(0)'},
  {transform: 'translate(150px, 200px)'}
], 500);
animation.addEventListener('finish', function() {
  boxElement.style.transform = 'translate(150px, 200px)';
});
复制代码

默认状况下,网页动画只是修改了元素的展现效果。若是想要让元素停留在其移动到的目标位置,那么就得在动画结束的时候修改其底层样式。这也是为何在以上的示例中监听 finish 事件而后设置box.style.transform 属性为 translate(150px, 200px) 的缘由,该属性值和 CSS 动画执行的第二个样式转换是同样的。

经过使用 JavaScript 动画,能够彻底控制每一步元素的样式。这意味着能够为所欲为地减速,暂停,中止或者翻转动画进而操做目标元素。因为能够适当地封装动画行为,因此当在构建复杂面向对象的应用程序的时候会特别有用。

Easing 定义

天然平滑地移动会让网络应用拥有更好的用户交互体验。

天然条件下,没有事物能够直线地从一个点运动到另外一个点。现实生活中,在咱们周围的物理世界中物体在移动的时候会加速或减速,由于咱们并不生活在真空状态下且有不一样的因素来影响事物的运行状态。人类的大脑会指望感觉这样的移动,因此当为网络应用制做动画的时候,利用此类知识会对本身会有好处。

这是你所应该理解的术语:

  • 『ease in』-开始移动缓慢然后加速
  • 『ease out』-开始移动迅速然后减速

能够合并两个动画,好比 『ease in out』。

Easing 可使得动画更加天然平滑。

Easing 关键字

能够为 CSS 过渡和动画选择任意的 easing 方法。不一样的关键字会影响动画的 easing。你也能够彻底自定义 easing 方法。

如下为能够选择用来控制 easing 的 CSS 关键字:

  • linear
  • ease-in
  • ease-out
  • ease-in-out

让咱们深刻了解并查看他们的效果。

Linear 动画

不使用任何的 easing 方法的动画即为 linear

如下为 linear 过渡效果的图示:

值随着时间流逝,值等比增长。使用 linear 动效,会让动画不天然。通常来讲,避免使用 linear 动效。

使用以下代码实现一个简单的线性动画:

transition: transform 500ms linear;

Ease-out 动画

正如前所述,和 linear 对比,easing out 让动画快速启动,结束时会减速。如下为图示:

总之,easing out 是最适合作界面体验的,由于快速地启动会给人以快速响应的动画的感受,而结束时让人感受很平滑这得归功于不一致的移动速度。

打个比喻,好比那些跑车,首先启动速度至关的快,这就给人以愉悦的感受。这个就比较符合人类对于动画的感知。

有不少的方法来实现 ease out 动画效果,而最简单的即为 CSS 中的 ease-out 关键字。

transition: transform 500ms ease-out;

Ease-in 动画

和 ease-out 动画相反-其启动慢而后结束时变快。图示以下:

和 ease-out 动画比较,因为他们启动缓慢给人以反应卡顿的感受,因此 ease-in 让人感受动画不天然。动画结束时很快给人一种奇怪的感受,由于整个动画一直在加速,而现实世界中当事物忽然中止运动的时候会减速而不是加速。

和 ease-out 和 linear 动画相似,使用 CSS 关键字来实现 ease-in 动画:

transition: transform 500ms ease-in;

Ease-in-out 动画

该动画为 ease-in 和 ease-out 的合集。图示以下:

不要设置动画持续时间过长,不然会给人一种界面不响应的感受。

使用 ease-in-out CSS 关键字来实现 ease-in-out 动画:

transition: transform 500ms ease-in-out;

自定义 easing

你能够自定义本身的 easing 曲线,这样就更有效地控制项目中的动画。

实际上, ease-inlinearease 关键字映射到预约义贝塞尔曲线 ,能够在 CSS transitions specificationWeb Animations specification 中查找更多关于贝塞尔曲线的内容。

贝塞尔曲线

让咱们看一下贝塞尔曲线的运行原理。一条贝塞尔曲线包含四个点,或者准确地说是包含两组数值。每一对数值内包含表示三次贝塞尔曲线控制点的 X 和 Y 坐标。贝塞尔曲线的起点坐标为 (0, 0) ,终点坐标为 (1, 1)。能够设置两组数值对。每一个控制点的 X 轴值必须在 [0, 1] 之间,而 Y 轴值能够超过 [0, 1],虽然规范并无明确容许超过的数值。即便每一个控制点的 X 和 Y 值的微小差别都会输出彻底不一样的贝塞尔曲线。

查看维基百科关于贝塞尔曲线的说明,通俗一点讲即,如今所说的即三次贝塞尔曲线,该曲线由四个点组成,P0, P1, P2, P3 组成,那么,P0 和 P1 组成一对,P2 和 P3 组成一对,P1 和 P2 即为控制点,P0 和 P3 即为起始和结束节点。以下图所示:

看下两张拥有相近但不一样坐标的控制结点的贝塞尔曲线图。

如你所见,两张图有很大不一样。第一个控制点矢量差别为 (0.045, 0.183),而第二个控制点矢量差别为 (-0.427, -0.054)。

第二条曲线的样式为:

transition: transform 500ms cubic-bezier(0.465, 0.183, 0.153, 0.946);

第一组数值为起始控制点的 X 和 Y 坐标而第二组数值为第二个控制点的 X 和 Y 坐标。

性能优化

你得维持动画帧数为 60 帧每秒,不然会影响到用户体验。

和世界上其它事物同样,动画会有性能开销。一些属性的动画性能开销相比其它属性要小。好比,为元素的 widthheight 作动画会更改其几何结构而且可能会形成页面上的其它元素移动或者大小的改变。这一过程被称为布局。以前的文章中有详细介绍过布局和渲染。

总之,应该尽可能避免为会引发布局和绘制的属性作动画。对于大多数现代浏览器而言,即把动画局限于 opacitytransform 属性。

Will-change

可使用 will-change 来通知浏览器将会更改某个元素的属性。这会容许浏览器当更改某个元素属性的时候,事先进行最恰当的优化。但不要滥用 will-change,由于这样作会拔苗助长,使得浏览器浪费更多的资源,从而形成更多的性能问题。

为 transforms 和 opacity 添加 will-change 代码以下:

.box {
  will-change: transform, opacity;
}
复制代码

该属性在 Chrome, Firefox,Opera 获得很好的兼容。

如何选择 JavaScript 和 CSS 来执行动画

这个问题是无解的。只需谨记如下原则:

  • 基于 CSS 的动画和原生支持的网页动画通常都是由被称为『合成线程』的线程来处理的。这不一样于浏览器的主线程,主线程是用来执行计算样式,布局,绘制及 JavaScript 代码的。这即意味着若是浏览器在主线程上运行耗时的任务,不会中断动画的运行。
  • 不少时候,也能够由合成线程来处理 transformsopacity 属性值的更改。
  • 若是有任何动画触发绘制,布局或者同时触发二者,『主线程』将不得不来进行处理。事实是基于 CSS 和 JavaScript 的动画和布局或者绘制的性能开销将颇有可能会阻塞全部和 CSS 或者 JavaScript 运行相关的工做,从而使得渲染问题变得毫无心义。

正确使用动画

良好的动画为项目添加一层使人愉快和互动的用户体验。你能够随意使用动画,无论是宽度,调试,定位,颜色或背景色,但必须注意潜在的性能瓶颈。糟糕的动画选择会影响用户体验,因此动画必须是高效和适当的。尽量减小使用动画。只使用动画来让用户体验流畅天然而不是滥用。

使用动画进行交互

不要由于只是为了用而去使用动画。相反,有策略性地使用动画来增强用户交互体验。避免使用没必要要的动画来打断或者阻碍用户的使用。

避免为性能开销大的属性作动画

比糟糕的动画使用更糟的是那些会引发页面卡顿的动画。这类动画让用户感到懊丧和不快。

引用资源

疑问

能够看下网上的这篇介绍贝塞尔曲线的文章,那么能够如何使用贝塞尔曲线来作出使人惊叹的动画呢?

招贤纳士

今日头条招人啦!发送简历到 likun.liyuk@bytedance.com ,便可走快速内推通道,长期有效!国际化PGC部门的JD以下:c.xiumi.us/board/v5/2H…,也可内推其余部门!

本系列持续更新中,Github 地址请查阅这里

相关文章
相关标签/搜索