JavaScript是如何工做的: CSS 和 JS 动画底层原理及如何优化它们的性能

阿里云最近在作活动,低至2折,有兴趣能够看看:
https://promotion.aliyun.com/...

为了保证的可读性,本文采用意译而非直译。javascript

这是专门探索 JavaScript 及其所构建的组件的系列文章的第 13 篇。css

若是你错过了前面的章节,能够在这里找到它们:html

  1. JavaScript 是如何工做的:引擎,运行时和调用堆栈的概述!
  2. JavaScript 是如何工做的:深刻V8引擎&编写优化代码的5个技巧!
  3. JavaScript 是如何工做的:内存管理+如何处理4个常见的内存泄漏 !
  4. JavaScript 是如何工做的:事件循环和异步编程的崛起+ 5种使用 async/await 更好地编码方式!
  5. JavaScript 是如何工做的:深刻探索 websocket 和HTTP/2与SSE +如何选择正确的路径!
  6. JavaScript 是如何工做的:与 WebAssembly比较 及其使用场景 !
  7. JavaScript 是如何工做的:Web Workers的构建块+ 5个使用他们的场景!
  8. JavaScript 是如何工做的:Service Worker 的生命周期及使用场景!
  9. JavaScript 是如何工做的:Web 推送通知的机制!
  10. JavaScript是如何工做的:使用 MutationObserver 跟踪 DOM 的变化!
  11. JavaScript是如何工做的:渲染引擎和优化其性能的技巧!
  12. JavaScript是如何工做的:深刻网络层 + 如何优化性能和安全!

想阅读更多优质文章请猛戳GitHub博客,一年百来篇优质文章等着你!前端

概述

你确定知道,动画在建立引人注目的 Web 应用程序中扮演着重要的角色。随着用户愈来愈多地将注意力转移到用户体验上,商户开始意识到完美、愉快的用户体验的重要性,结果 Web 应用程序变得愈来愈重,并具备更动态交互的 UI。这一切都须要更复杂的动画,以便用户在整个过程当中更平稳地进行状态转换。今天,这甚至不被认为是什么特别的事情。用户正变得愈来愈挑剔,默认状况下,他们指望的是具备高响应性和交互性的用户界面。java

然而,界面的动画化并不必定是简单的。什么是动画,何时该用动画,动画应该有什么样的视频效果,这些都是棘手的问题。git

JavaScript 和 CSS 动画比较

建立 Web 动画的两种主要方法是使用JavaScript和 CSS。选择哪一种没有对或错,这彻底取决于你想要达到的效果。github

CSS 动画

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

这里将从如何让元素在 X 和 Y 轴上移动 50px 简单示例开始,经过持续 1 秒的 CSS 过渡来移动元素。编程

.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 的值而后开发发生过渡效果。segmentfault

除了转换持续时间外,还有 easing 属性,这实际上就是动画的运动速度方式,该参数会在以后详细介绍。

若是像上面的代码片断同样,建立单独的 CSS 类来实现动画,固然也可使用 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 事件,但前提是放弃旧版 Internet Explorer 的支持:

图片描述

监听 transitionend 触发的事件以下所示:

var boxElement = document.querySelector('.box');
boxElement.addEventListener('transitionend', onTransitionEnd, false);

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

除了使用 CSS 过渡以外,你还可使用 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;
  }
}

效果示例: https://sessionstack.github.i...

使用CSS动画,你能够独立于目标元素定义动画自己,并使用 animation-name 属性来选择所需的动画。

CSS 动画在某种程度仍然须要加浏览器前缀的,在 Safari、Safari Mobile 和 Android 中都使用了 -webkit。 Chrome、 Opera、Internet Explorer 和 Firefox 都不须要添加前缀。许多工具能够帮助你建立所需 CSS 的前缀,这样就不须要在源文件中带样式前缀。

JavaScript 动画

和 CSS 过渡或者 CSS 动画相比,使用 JavaScript 建立动画更加复杂,但它一般为开发人员提供了更强大的功能。

JavaScript 动画是做为代码的一部份内联编写的。你还能够将它们封装在其余对象中。如下为用 JavaScript 来实现最开始的 CSS 过渡的代码:

var boxElement = document.querySelector('.box');
var animation = boxElement.animate([
  {transform: 'translate(0)'},
  {transform: 'translate(150px, 200px)'}
])

animation.addEventListener('finish', function() {
  boxElement.style.transform = 'translate(150px, 200px)';
})

默认状况下,Web 动画仅修改元素的展现效果。 若是要将对象停留在移动后的位置,则应在动画完成时修改其基础样式。 这就是为何在上面的例子中监听 finish 事件,并将 box.style.transform 属性设置为 translate(150px, 200px),该属性值和 CSS 动画执行的第二个样式转换是同样的。

使用 JavaScript 动画,你能够在每一步彻底控制元素的样式。 这意味着你能够放慢动画速度,暂停动画,中止它们,翻转它们,并根据须要操纵元素。 若是你正在构建复杂的面向对象的应用程序,这尤为有用,由于你能够正确地封装你想要的动画行为。

Easing 定义

天然过渡效果会让你的用户对你的 Web 应用程序感受更舒服,从而带来更好的用户体验。

固然,没有任何东西从一个点到另外一个点线性移动。 实际上,当事物在咱们周围的物理世界中移动时,事物每每会加速或减速,由于咱们不是在真空中,而且有不一样的因素会影响这一点。 人类的大脑会指望感觉这样的移动,因此当为网络应用制做动画的时候,利用此类知识会对本身会有好处。

如下是一些术语须要了解一下:

  • ease in —  相对于匀速,开始的时候慢,以后快
  • ease out — 相对于匀速,开始时快,结束时候间慢
  • ease-in-out — 相对于匀速,开始和结束都慢)两头慢

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 动画

如前所述,与线性动画相比,easing out 动画开始时快,结束时候间慢,过渡效果的图示以下:

图片描述

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

有不少方法能够实现 ease-out 效果,但最简单的是 CSS 中的 ease-out 关键字:

transition: transform 500ms ease-out;

Ease-in 动画

ease-out 动画相反-开始时快,结束时候间慢,过渡效果图以下:

图片描述

ease-out 动画相比, ease-in 可能会让人感到不寻常,因为启动缓慢给人以反应卡顿的感受,所以会产生一种无反应的感受。 动画结束很快也会产生一种奇怪的感受,由于整个动画正在加速,而现实世界中的物体在忽然中止时每每会减速。

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

transition: transform 500ms ease-in;

Ease-in-out 动画

该动画为 ease-in 和 ease-out 的合集,过渡效果图以下:

图片描述

不要使用太长的动画持续时间,由于它们会让你的 UI 感受没有响应。

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

transition: transform 500ms ease-in-out;

自定义 easing

你也能够定义本身的 easing 曲线,这能够更好地建立本身想要的动画效果。

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

贝塞尔曲线 (Bézier curves)

Bézier curve(贝塞尔曲线)是应用于二维图形应用程序的数学曲线。 曲线定义:起始点、终止点(也称锚点)、控制点。经过调整控制点,贝塞尔曲线的形状会发生变化。 1962年,法国数学家Pierre Bézier第一个研究了这种矢量绘制曲线的方法,并给出了详细的计算公式,所以按照这样的公式绘制出来的曲线就用他的姓氏来命名,称为贝塞尔曲线

CSS3 transition-timing-function 属性,其语法以下:

transition-timing-function: linear|ease|ease-in|ease-out|ease-in-out|cubic-bezier(n,n,n,n);

总而言之能够用cubic-bezier(n,n,n,n)的形式来表示所有的属性值,这里就涉及到贝塞尔曲线(Bézier curve)。

让咱们看看贝塞尔曲线的工做原理。 贝塞尔曲线须要四个值,或者更准确地说它须要两对数字。 每对描述立方贝塞尔曲线控制点的 XY 坐标。贝塞尔曲线的起点有一个坐标 (0, 0) ,结束坐标是 (1, 1)。 你能够设置两个对号,两个控制点的 X 值必须在 [0,1] 范围内,而且每一个控制点的 Y 值能够超过 [0,1] 限制,尽管规定不清楚多少。

即便每一个控制点的 XY 值稍有变化,也会获得彻底不一样的曲线。让咱们看两张贝塞尔曲线的图,两张图相近但坐标的控制结点却不一样。

图片描述

图片描述

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

第二条曲线的样式为:

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

前两个数字是第一个控制点的 XY 坐标,后两个数字是第二个控制点的 XY 坐标。

性能优化

当你在使用动画的时候,你应该维持 60 帧每秒,不然会影响用户体验。

和世界上的其余事物同样,动画也会有性能的开销。一些属性的动画性能开销相比其它属性要小。例如,为元素的 widthheight 作动画会更改其几何结构而且可能会形成页面上的其它元素移动或者大小的改变,这个过程称为布局。咱们在以前的一篇文章 中更详细地讨论了布局和渲染。

一般,你应该避免动画触发布局或重绘的属性。 对于大多数现代浏览器,这意味着把动画局限于 opacitytransform 属性。

Will-change

你可使用 will-change 知浏览器你打算更改元素的属性,这容许浏览器在进行更改以前进行最适当的优化。可是,不要过分使用 will-change,由于这样作会致使浏览器浪费资源,从而致使更多的性能问题。

will-change 用法以下:

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

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

图片描述

JavaScript 动画和 CSS 动画该若是抉择

  • 根据 Google Developer,渲染线程分为 主线程 (main thread)合成线程 (compositor thread)。若是 CSS 动画只是改变 transformsopacity,这时整个 CSS 动画得以在 合成线程 完成(而JS动画则会在 主线程 执行,而后触发合成线程进行下一步操做),在 JS 执行一些昂贵的任务时,主线程繁忙,CSS 动画因为使用了合成线程能够保持流畅
  • 在许多状况下,也能够由合成线程来处理 transformsopacity 属性值的更改。
  • 对于帧速表现很差的低版本浏览器,CSS3能够作到天然降级,而JS则须要撰写额外代码。
  • CSS动画有自然事件支持(TransitionEnd、AnimationEnd,可是它们都须要针对浏览器加前缀),JS则须要本身写事件。
  • 若是有任何动画触发绘画,布局或二者,则须要 “主线程” 才能完成工做。 这对于基于 CSS 和 JavaScript 的动画都是如此,布局或绘制的开销可能会使与 CSS 或 JavaScript 执行相关的任何工做相形见绌,这使得问题没有实际意义。
  • CSS3有兼容性问题,而JS大多时候没有兼容性问题。

总结

若是动画只是简单的状态切换,不须要中间过程控制,在这种状况下,css 动画是优选方案。它可让你将动画逻辑放在样式文件里面,而不会让你的页面充斥 Javascript 库。然而若是你在设计很复杂的富客户端界面或者在开发一个有着复杂 UI 状态的 APP。那么你应该使用 js 动画,这样你的动画能够保持高效,而且你的工做流也更可控。因此,在实现一些小的交互动效的时候,就多考虑考虑 CSS 动画。对于一些复杂控制的动画,使用 javascript 比较可靠。


原文:

https://blog.sessionstack.com...

代码部署后可能存在的BUG无法实时知道,过后为了解决这些BUG,花了大量的时间进行log 调试,这边顺便给你们推荐一个好用的BUG监控工具 Fundebug

你的点赞是我持续分享好东西的动力,欢迎点赞!

交流

干货系列文章汇总以下,以为不错点个Star,欢迎 加群 互相学习。

https://github.com/qq44924588...

我是小智,公众号「大迁世界」做者,对前端技术保持学习爱好者。我会常常分享本身所学所看的干货,在进阶的路上,共勉!

关注公众号,后台回复福利,便可看到福利,你懂的。

clipboard.png