页面丝滑程度的优化检测

本文原创:fanjiayucss

什么是丝滑的页面

  • chrome团队提出了一个以用户为中心的性能模型被称为RAIL,它为工程师提供一个目标,只要达到目标的网页,用户就会以为很流畅;它将用户体验拆解为一些关键操做,例如:点击,加载等;并给这些操做规定一个目标,例如:点击一个按钮后,多长时间给反馈用户会以为流畅。html

  • RAIL将影响性能的行为划分为四个方面,分别是:response(响应)、animation(动画)、idle(空闲)与load(加载)。没错,RAIL这个名字来自于这四个单词的首字母,方便记忆。css3

response(响应)

  • 100ms
  • 研究代表,100ms内对用户的输入操做进行响应,一般会被人类认为是当即响应。时间再长,操做与反应之间的链接就会中断,人们就会以为它的操做有延迟。
  • 例如:当用户点击一个按钮,若是100ms内给出响应,那么用户就会以为响应很及时,不会察觉到丝毫延迟感。

animation(动画)

  • 60fps
  • 现现在大多数设备的屏幕刷新频率是60hz,也就是每秒钟屏幕刷新60次;所以网页动画的运行速度只要达到60fps,咱们就会以为动画很流畅。
  • fps指的画面每秒钟传输的帧数,**60fps指的是每秒钟60帧;**换算下来每一帧差很少是16毫秒。
  • 但一般浏览器须要花费一些时间将每一帧的内容绘制到屏幕上(包括样式计算、布局、绘制、合成等工做),因此一般咱们只有10毫秒来执行JS代码。

idle(空闲)

  • 为了更好的性能,一般咱们会充分利用浏览器空闲周期idle period作一些低优先级的事情。例如:在空闲周期预请求一些接下来可能会用到的数据或上报分析数据等。
  • RAIL规定,空闲周期内运行的任务不得超过50ms。由于浏览器同一时间主线程只能处理一个任务,若是一个任务执行时间过长,浏览器则没法执行其余任务,用户会感受到浏览器被卡死了,为了达到100ms内给出响应,将空闲周期执行的任务限制为50ms意味着,即便用户的输入行为发生在空闲任务刚开始执行,浏览器仍有剩余的50ms时间用来响应用户输入,而不会产生用户可察觉的延迟

1.png

  • 事实上,不管是空闲任务仍是高优先级的其余任务,执行时间都不得超过50ms。

load(加载)

  • 秒级
  • 若是不能在1秒钟内加载网页并让用户看到内容,用户的注意力就会分散。用户会以为他要作的事情被打断,若是10秒钟还打不开网页,用户会感到失望,会放弃他们想作的事,之后他们或许都不会再回来。

丝滑页面的决定因素(像素管道)

2.png

  • JavaScript:通常来讲,咱们会使用JavaScript来实现一些视觉变化的效果。chrome

  • Style:计算样式。这个过程是根据CSS选择器,对每一个DOM元素匹配对应的CSS样式。浏览器

  • Layout:布局。具体计算每一个DOM元素最终在屏幕上显示的大小和位置。Web页面中元素的布局是相对的,所以一个元素的布局发生变化,会联动地引起其余元素的布局发生变化。所以,对于浏览器而言,布局过程是常常发生的。bash

  • Paint:绘制。本质上就是**填充像素的过程。**包括绘制文字、颜色、图像、边框和阴影等,也就是一个DOM元素全部的可视效果。通常来讲,这个绘制过程是在多个层上完成的。布局

  • Composite:渲染层合并。性能

3.png

  • 若是你修改一个DOM元素的“layout”属性,也就是改变了元素的样式(好比width、height或者position等),那么浏览器会检查哪些元素须要从新布局,而后对页面激发一个reflow(重排)过程完成从新布局。reflow一定会引起重绘,这对于WEB的性能影响是极大的。测试

  • 影响WEB性能主要过程包括layout、paint和composite。那么对于cssanimation而言,咱们的全部操做都是经过CSS的样式控制动画,只要是会触发layout、paint和composite的CSS属性都会直接影响动画的性能。因此整个动画应尽可能避开重排和重绘。字体

  • 会触发重排重绘:调整窗口大小、改变字体、增长或者移除样式表、内容变化、激活CSS伪类、计算offsetwidth和offsetheight等等。

  • 影响layout的CSS属性:csstriggers.com/

使页面丝滑的方法

####防止FSL(强制同步布局)

  • FLS:先执行JS,而后在JS中修改了样式从而致使样式计算,而后样式的改动触发了布局、绘制、合成。但JavaScript能够强制浏览器将布局提早执行,这就叫FSL强制同步布局

4.png

以下代码为会触发FSL的一段代码

<body>
    <button id="btn">Click me~</button>
    <div class="container">
        <!-- 建立多个class为box的div使效果更明显>
        <div class="box"></div>
    </div>
    <script>
        const btn = document.querySelector('#btn');
        const container = document.querySelector('.container');
        const boxes = document.querySelectorAll('.box');

        btn.onclick = function () {
            for (var i = 0; i < boxes.length; i++) {
                boxes[i].style.width = container.offsetWidth + 'px';
            }
        }
    </script>
</body>
复制代码

5.png

  • 面板中已经明确给出警告:Forced reflow is a likely performance bottleneck.(“强制同步布局”可能会致使性能问题)

对代码进行以下优化

<script>
   const btn = document.querySelector('#btn');
   const container = document.querySelector('.container');
   const boxes = document.querySelectorAll('.box');

   btn.onclick = function () {
       const newWidth = container.offsetWidth;
       for (var i = 0; i < boxes.length; i++) {
           boxes[i].style.width = newWidth + 'px';
       }
   }
</script>
复制代码
  • 文中前半部分已经说明,计算offsetwidth会触发重排重绘,因此第一种方法在循环中先获取容器box的宽度,随后设置了box元素的样式。这会致使浏览器去布局,而后计算样式。每次更改样式,都会致使刚刚执行的布局失效,由于咱们又改了新的样式,因此下一轮循环读取宽度时,浏览器又要执行一次布局,如此反复直到循环结束。在循环期间,浏览器不停地执行无效布局,因而出现了性能问题。为了使效果更佳明显,在CPU 6x slowdown的条件下进行了测试,对好比下:

6.png
7.png

从对比中能够看出,前者的Rendering是后者的几倍之多,可见,优化重排重绘,防止FSL的发生对性能的提高效果是很明显的。

####新建图层

  • 事实上浏览器在渲染页面时,能够将页面分为不少个图层,有点相似于photoshop,一张图片在potoshop中是由多个图层组合而成,而浏览器最终显示的页面实际也是由多个图层构成的。

  • 因此将本来不断发生变化的元素提高到单独的图层中,就再也不须要绘制了,浏览器只须要将两个图层合并在一块儿便可。(注:图层的维护也须要成本,不要一味地不断新建图层)

<body>    
    <div class="ball-running" style="width:100px;height:50px;background:red;position:absolute;left:0;top:0"></div>
</body>
<style>
    .ball-running { animation: run-around 4s infinite;} 

    @keyframes run-around {

        0%: { top: 0; left: 0; } 

        25% { top: 0; left: 200px; } 

        50% { top: 200px; left: 200px; } 

        75% { top: 200px; left: 0; } 

    }
</style>
复制代码
<body>    
    <div class="ball-running" style="width:100px;height:50px;background:red;position:absolute;left:0;top:0"></div>
</body>
<style>
    .ball-running { animation: run-around 4s infinite; } 

    @keyframes run-around {

    0%: { transform: translate(0, 0); } 

    25% { transform: translate(200px, 0); } 

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

    75% { transform: translate(0, 200px); } 

    }
</style>
复制代码
  • 两者的区别是前者不断改变元素的位置使元素运动起来,后者则经过transform来实现运动,其实是将元素动画提高到了一个新的图层,开启GPU加速单独处理图像部分,下图是两者性能图对比。

8.png
9.png

  • 能够看到,前者不断重复发生重排和重绘过程,然后者已经再也不发生重排和重绘,由于transform这个属性已经由合成器单独处理了,因此使用这个属性能够避免布局与绘制。

总结

  • JS动画要保证预留出6ms的时间给浏览器处理像素管道,而自身执行时间应该小于10ms来保证总体运行速度小于16ms。

  • 避免一切FSL,它很是影响性能。

  • CSS动画尽可能使用transform属性来完成动画。建议使用transform的translate替代margin或position中的top、right、bottom和left,同时使用transform中的scalex或者scaley来替代width和height。

参考

相关文章
相关标签/搜索