Chinaunix首页 | 论坛 | 博客
  • 博客访问: 817600
  • 博文数量: 62
  • 博客积分: 526
  • 博客等级: 二等列兵
  • 技术积分: 2078
  • 用 户 组: 普通用户
  • 注册时间: 2012-02-04 20:41
个人简介

博客迁移至 freefe.cc

文章分类

全部博文(62)

分类: JavaScript

2015-09-30 15:47:50

   

    目前我们关于实现动画的方式,无非就是类似 jQuery的动画, CSS 的 animation,transition,tranform 或者 requestAnimationFrame 以及触及GPU的硬件加速动画。

    
    jQuery的动画不用说,就普通的 CSS 动画而言使用浏览器自身的渲染引擎来处理动画的渲染,而触发 CSS 硬件加速则是通过 GPU 来提升 CSS 动画的性能,将有关于动画的运算从 CPU 移植到 GPU 上,大大提高渲染动画的效率。GPU 在对于渲染图形方便的优势,所以能在渲染动画上更容易达到每秒60帧的顺滑效果,使得视觉上顺畅无比。
    
    简单来讲,当一个页面初次加载完毕的时候,浏览器的渲染引擎便开始了它的工作:
    1. 浏览器解析HTML, 构建一棵 DOM 树,其每个节点对应页面的每个元素。
    2. 渲染引擎对 DOM树,以浏览器解析后的 CSS ,会对应构建出一棵 Render 树,其每个节点 RenderObject包含了所对应 DOM 的最终样式信息。
    3. 基于 Render 树,渲染引擎会将其中的某些特定节点同时创建新的 RenderLayer,从而还会生成一棵 RenderLayer 树。
    4. 当 Render 树和  RenderLayer 树构建完毕之后,便依据两者,遍历各个节点开始渲染绘制出页面。

    
 
    再简单介绍下 chrome 的 timeline 的几个名词:

    Timer Fire: 定时器触发,很简单,比如 jquery 的动画均是通过 setTimeout 来处理生产动画效果(一般性的动画),所以每次展现完毕后都会触发下一个定时器事件 JS 的过程。
    Recalculate Style:将 CSS 构建与 DOM 对应的的树,一般称之为 Render 树的过程。
    Layout:依据 Render 树,以及 RenderLayer 树进行页面布局的过程。
    Print:更具布局,绘制出页面的过程
    Composite Layers:渲染展现到屏幕的过程

    实例页面:
    />
    最古老的动画形式:
    HTML:

  1. <div class="square"></div>

    CSS:

  1. .square{
  2.     position:relative;
  3.     top:50px;
  4.     left: 50px;
  5.     margin-bottom: 10px;
  6.     height:80px;
  7.     width:80px;
  8.     background-color:#999;
  9. }

    JS:

  1. $('.jquery').click(function(){
  2.     $('.square').animate({
  3.         height: 300,
  4.         width: 300,
  5.         left:200,
  6.         top:200,
  7.         opacity: 0.5},
  8.         3000, 'linear', function() {
  9.     });
  10. });

    以上是一个很简单 jq 动画效果,3秒钟从原有图形样式转变到指定的图形样式,但是,浏览器所处理的却是一段 jq 分割的复杂的过程,

    

    每一次浏览器都要处理包括 Timer Fire,Recalculate Style,Layout,Paint,Composite Layers,一次又一次的不断重复又循环,内存占用率也是随之增大。

    


    如今的时代,css的动画帮我们解决了很多难题,
    普通的2D CSS动画:
    CSS:

  1. .css2d{
  2.     width:300px;
  3.     height:300px;
  4.     left:200px;
  5.     top:200px;
  6.     transition: width 3s linear, height 3s linear,top 3s linear,left 3s linear;
  7.     -webkit-transition: width 3s linear, height 3s linear,top 3s linear,left 3s linear,;
  8. }

    JS:

  1. $('.css-2d').click(function(){
  2.     $('.square').addClass('css2d');
  3. });

    直接上图,计算样式以及重新布局均有浏览器自身完成,并且少了定时器触发事件这一块,使得动画运行方式更为自然,

    

    还有内存图:
    

    或许感觉还不错了,但是由于涉及到了一些或导致重排的属性,使得动画的时候一直重排重绘,那么来看看硬件加速的样式动画吧,

    吊炸天的硬件加速CSS动画:
    CSS:

  1. .hardware{
  2.     -webkit-animation: hardware 2s infinite linear;
  3.     animation: hardware 2s infinite linear;
  4. }

  5. @-webkit-keyframes hardware {
  6.     0% {
  7.         transform: translate3d(0,0,0) scale3d(1,1,1);
  8.         -webkit-transform: translate3d(0,0,0) scale3d(1,1,1);
  9.     }


  10.     50% {
  11.         transform: translate3d(150px,150px,0) scale3d(3,3,1);
  12.         -webkit-transform: translate3d(150px,150px,0) scale3d(3,3,1);
  13.     }
  14. }


  15. @keyframes hardware {
  16.     0% {
  17.         transform: translate3d(0,0,0) scale3d(1,1,1);
  18.         -webkit-transform: translate3d(0,0,0) scale3d(1,1,1);
  19.     }


  20.     50% {
  21.         transform: translate3d(150px,150px,0) scale3d(3,3,1);
  22.         -webkit-transform: translate3d(150px,150px,0) scale3d(3,3,1);
  23.     }
  24. }

    JS:

  1. $('.css-hardware').click(function(){
  2.     $('.square').addClass('hardware');
  3. });

    对于硬件加速的动画,浏览器渲染的过程:
    

    屌不屌其实都一样啦,只不过没有使用处理元素的一些,如height, width,left,top 等元素值的动画,而使用了一些3D CSS属性达到了我们所需的动画效果,但是浏览器处理动画却已经完全不一样。动画过程优化到了仅剩Recalculate Style,Composite Layers 两个过程。
    

    这边为什么会这么吊呢,这就全依仗与 对应所生成的第三种树 RenderLayer 树,该元素节点被渲染引擎根据 CSS 创建了对应的 RenderLayer 树节点,并将其绑定到 GPU,当该 RenderLayer 的 transform 等变化时候,跳过浏览器的 Layout,以及 Print,直接有 GPU 对其做变换。这个也是直接提升动画性能,内存突然降低(计算被移植到了GPU)的原因。


    那么如何来处理 CSS,将我们所需要进行动画的元素构建时拥有新建的 RenderLayer 节点呢?

    目前查询到的资料来看,chrome 以下:
    1. 3D或透视变换(perspective transform)CSS属性
    2. 使用加速视频解码的<video>节点
    3. 拥有3D(WebGL)上下文或加速的2D上下文的<canvas>节点
    4. 混合插件(如Flash)
    5. 对自己的opacity做CSS动画或使用一个动画webkit变换的元素
    6. 拥有加速CSS过滤器的元素
    7. 元素有一个包含复合层的后代节点(一个元素拥有一个子元素,该子元素在自己的层里)
    8. 元素有一个z-index较低且包含一个复合层的兄弟元素(换句话说就是该元素在复合层上面渲染)
  
    一般我们都会尝试对需要进行动画的元素添加一个 3D 的属性从而来触发其硬件加速的动画效果,现在又有一个专门用作触发生成新 RenderLayer 的属性值 will-change。

    当然以上动画最优情况只正对于部分 GPU 加速处理的 CSS 属性,对于涉及到重排 relayout 和 重绘 repaint 的属性我们可能需要慎重考虑以及优化了。

    重排:重排是页面结构调整,获取或者设置元素某些属性值时,触发了浏览器 Layout 过程重新定义的情况,比如调整元素的几何大小,位置,获取宽高等。重排必然导致重绘,浏览器渲染总会依照 Layout,Paint,Composite Layers 的流程来处理。下面是触发重排的一些主要情况:
    
    1. 脚本操作DOM,增加删除可视的DOM节点
    2. 操作 class 属性,或者设置 style 属性
    触发重排的属性:
        盒子模型的相关属性:width, height, padding, margin, display, border
        定位特性的相关属性:top, bottom, left, right, position, float, clear
        内容的相关属性:text-align, overflow, font-family, line-height,font-size
    3. 计算获取当前 offsetTop/Left/Width/Height, scrollTop/Left/Width/Height, clientTop/Left/Width/Height, width, height 等属性值
    4. 内容变化, 窗口调整等

    重绘:重排必然导致重绘,并且对于仅调整各类属性色值等时,浏览器重新绘制输出到浏览器的情况。
    触发重排的属性:color, border-style, visibility, background, outline, box-shadow 等
    
    一些动画的重排重绘我们不可避免,我们要做的并不是禁止它,存在即合理,事实只有我们滥用。以下是一些简单的优化重排,重绘的方法:
    1. 将多次需要频繁改变的样式一次性完成,不要频繁的获取计算样式。
    2. 将大量的操作 DOM 操作放置到页面之外进行。
    3. 尽可能在最末端需要调整的 DOM 上改变 class,减小对其它 DOM 的影响。
    4. 动画尽量应用到 position 为 absolute 或者 fixed 的元素上,以便当前元素的动画造成大规模的重排。
    




阅读(4765) | 评论(0) | 转发(1) |
给主人留下些什么吧!~~