边学边译JS工作机制---13.CSS和JS动画以及优化建议

244 阅读9分钟

本系列其他译文请看[JS工作机制 - 小白1991的专栏 - 掘金 (juejin.cn)] (juejin.cn/column/6988… "juejin.cn/column/6988…")  >本章阅读指数:3
本章内容讲CSS 和JS动画的区别,以及优化建议。如果你的产品在交互上要求比较高,可以学习一下。

概述

在构建网络应用时,动画已经开始扮演一个很重要的角色。用户的注意力转移到了UX上,商业也逐渐认识到愉悦的用户体验带来的价值。所以网络应用逐渐变重,承载了很多动态特性。如今用户已经天然的期望网站交互友好,且响应迅速。

但是动画并不那么简单。动画的时机,对象,以及效果都是很模糊的。

JS vs CSS 动画

两种主要的动画方式是使用CSS和JS。选择哪种方案,没有对错之分,只是看你想实现哪种效果

CSS动画

CSS动画是最简单的方式,让东西在屏幕上移动。 我们看一个例子,看如何将一个元素在X,Y轴上都移动50px。这个例子是使用CSS移动完成的,耗费1000ms。

.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的值改编了,然后开始移动。

除了过渡的时间,还有一个选项 缓动,表现的是动画感觉。稍后我们讨论更多细节。 像上面那样创建单独的CSS类来管理动画,然后用户就可以使用JS切换动画的开关 如下元素

<div class="box">
  Sample content.
</div>

使用JS来切换动画开关:

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

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

通过给所有 box class的元素 添加move class来触发动画。

这么做,你可以使用JS管理状态,然后给目标元素设置合适的样式类,然后把动画交给浏览器来处理了。如果这么做,你还可以监听transitionend事件(除了兼容老版IE)

image.png

transitioned事件会在动画末尾触发,像这样

var boxElement = document.querySelector('.box'); // Get the first element which has the box class.
boxElement.addEventListener('transitionend', onTransitionEnd, false);

function onTransitionEnd() {
  // Handle the transition finishing.
}

除了使用CSS过渡,还可以使用后CSS动画,这样可以控制每一个独立的关键帧,时间和循环次数。

关键帧用来通知浏览器在规定时间点上的CSS属性,然后填充空白

举个栗子:

/**
 * This is a simplified version without
 * vendor prefixes. With them included
 * (which you will need), things get far
 * more verbose!
 */
.box {
  /* Choose the animation */
  animation-name: movingBox;

  /* The animation’s duration */
  animation-duration: 2300ms;

  /* The number of times we want
      the animation to run */
  animation-iteration-count: infinite;

  /* Causes the animation to reverse
      on every odd iteration */
  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;
  }
}

看起来像这样 (quick demo) — sessionstack.github.io/blog/demos/…

使用CSS动画,你可以定义动画本身(与元素无关),然后使用动画属性名去选择需要的动画效果。

CSS动画依然有一些厂商前缀,比如在Safari, Safari Mobile, Android上使用-webkit-。Chrome, Opera, Internet Explorer, and Firefox 都不用使用前缀。 很多工具可以帮助你创建你需要的CSS前缀版本,这样就不用在代码中写前缀了。

JavaScript动画

使用JS动画跟CSS过渡和动画相比,都复杂的多,但是却给开发者带来了更多的控制权。

JS动画需要写在代码中,作为你代码的一部分。你可以把他们封装到其他对象中。下为复现之前描述的 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)';
});

web动画默认只是修改一个元素的表现。如果想让你的对象保留在它的目标位置,当动画结束之后,你可以修改它的底层样式。这就是我们监听finish 事件的原因,然后设置 box.style.transform属性为translate(150px, 200px),该属性值和 CSS 动画执行的第二个样式转换是一样的。

使用JS动画,你可以在每一步完全控制元素的样式。你可以给动画降速,暂停,停止,翻转,操作。当你构建复杂,面向对象的应用,因为你可以适当的封装它们。

什么是缓动

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

没有事物是线性的从一个点到另一个点的。实际上,在现实世界中物体的移动是要加速或者加速的。人类的大脑熟悉这样的物体运动,所以在应用中使用动画时,利用这个知识效果更好。

先理解一些概念:

  • “缓入” — 慢慢开始运动,然后加速
  • “缓出”  — 快速开始运动,然后减速

两者可以结合, “缓出入”.缓动会让你的动画看起来更自然。

缓动关键字

CSS过渡和缓动允许你选择你想要的类型,有一些不同的关键字影响到动画的缓动效果。你也可以自自定义一些缓动效果。

常用的缓动关键字有这些:

  • linear(线性)
  • ease-in(缓入)
  • ease-out(缓出)
  • ease-in-out(缓出入)

看看他们真实的效果

Linear

 linear 是指没有任何缓动效果

它的过渡效果图长这样

image.png

linear 效果是匀速的,这使得效果看起开并不真实。因此避免使用它

一个简单的例子实现它: transition: transform 500ms linear;

Ease-out

动画快速启动,在最后减速。过渡效果图长这样

image.png

UI中使用缓出是最好的,因为它快速启动,让人感觉响应很快。而结束时让人感觉很平滑这得归功于不一致的移动速度。

有很多办法实现这样的效果,最简单是使用 css 中ease-out

transition: transform 500ms ease-out;

Ease-in

缓入的效果跟缓出是相反的---启动很慢,结束时很快。它的过渡效果长这样:

image.png

跟缓出相比,缓入非常反直觉。它启动很慢,让人感觉响应很慢。而结束很快,让人感觉也很奇怪,因为整个动画一直在加速,而现实世界中当事物突然停止运动的时候会减速而不是加速。 跟缓出相似,你可以使用CSS关键字

transition: transform 500ms ease-in;

Ease-in-out

这个动画合并了缓入和缓出,它的动效图长这样

image.png

不要让动效持续时间太久,会让感觉你的UI没有响应。 同样可以使用CSS关键字实现:

transition: transform 500ms ease-in-out;

定制缓动效果

你可以自定义自己的 easing 曲线,这样就更有效地控制项目中的动画。

实际上, ease-inlinear 及 ease 关键字映射到预定义贝塞尔曲线 ,可以在 CSS transitions specification 和 Web Animations specification 中查找更多关于贝塞尔曲线的内容。

贝塞尔曲线

看看贝塞尔曲线是如何工作的 。贝塞尔曲线有两对值。每一对数值内包含表示三次贝塞尔曲线控制点的 X 和 Y 坐标。贝塞尔曲线的起点是(0, 0),终点的左边是(1, 1)。你可以设置每一对的值,每个控制点的 X 轴值必须在 [0, 1] 之间,而 Y 轴值可以超过 [0, 1],虽然规范并没有明确允许超过的数值。 查看维基百科关于贝塞尔曲线的说明,通俗一点讲即,现在所说的即三次贝塞尔曲线,该曲线由四个点组成,P0, P1, P2, P3 组成,那么,P0 和 P1 组成一对,P2 和 P3 组成一对,P1 和 P2 即为控制点,P0 和 P3 即为起始和结束节点。如下图所示:

X和Y的微小差异,也会有很大的区别。看下两张拥有相近但不同坐标的控制结点的贝塞尔曲线图。

image.png and

image.png 这两个图差异很大,但是差别很小。第一个控制点矢量差异为 (0.045, 0.183),而第二个控制点矢量差异为 (-0.427, -0.054)。

第二个线的CSS是这样:

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

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

性能优化

使用动画时,需要始终维持60fps ,否则影响用户体验。

但是动画也不是免费的。一些属性的动画性能开销会小一点。但是,为元素的 width 和 height 做动画会更改其几何结构并且可能会造成页面上的其它元素移动或者大小的改变。这一过程被称为布局。我们在第11章讨论过。

总之,应该尽量避免为会引起布局和绘制的属性做动画。对于大多数现代浏览器而言,即把动画局限于 opacity 和 transform 属性。

Will-change

你可以使用will-change 告知浏览器,你将会改变元素属性,这让浏览器提前开始选择合适的优化策略。但是不要过渡使用will-change,因为会引起浏览器浪费资源,最后反而影响了性能。

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

.box {
  will-change: transform, opacity;
}

看一下浏览器支持的情况

image.png

在JS和CSS中选择

选择JS和CSS不是对错的问题,选谁都可以。在选择时,可以遵循这些原则:

  • 基于CSS的动画,原生支持的Web动画,往往被“合成线程”来处理的.这个线程跟主线程不同,主线程处理样式,布局,绘制,执行JS。这意味着,如果浏览器在主线程中执行繁重的任务,这些动画可以持续运行而不会被打断。
  • 很多时候,也可以由合成线程来处理 transforms 和 opacity 属性值的更改。
  • 如果触发了绘制或者布局,就会要求主线程来处理。不管是基于CSS还是JS的动画,绘制和布局的开销都会让阻塞CSS和JS的执行。

选择合适的动画

良好的动画为项目增添了令人愉快和互动的用户体验。你可以随意使用动画,不管是宽度,调试,定位,颜色或背景色,但必须注意潜在的性能瓶颈。糟糕的动画选择会影响用户体验,所以动画必须是高效和适当的。尽可能减少使用动画。只使用动画来让用户体验流畅自然而不是滥用。

使用动画支持交互

不要因为你可以,就去使用动画。相反的,战略性的使用动画去加强用户交互体验。如非必要,不要使用动画打断或者阻塞用户行为。

拒绝开销昂贵的动画属性

唯一比动画更糟的事情是放置不当导致页面卡段。这种动画让用户感觉更不舒服。