如何使页面交互更流畅

流畅性

本篇是基于 FDCon2019 上《让你的网页更丝滑by刘博文》的复盘文。该课题也是博主感兴趣的领域, 后续会结合 React 的 Schedule 与该文进行进一步整合, 个人博客

  • 被动交互: animation
  • 主动交互: 鼠标、键盘

被动交互

当前市面上的设备频率在 60 HZ 以上。

主动交互

跑如下界面 https://code.h5jun.com/pojob

结合如下代码块, 可以看到 100ms 以下的点击是顺畅的, 而超过 100ms 的点击就会有卡顿现象。

var observer = new PerformanceObserver(function(list) {
  var perfEntries = list.getEntries()
  console.log(perfEntries)
});
observer.observe({entryTypes: ["longtask"]});

让用户感觉到流畅

衡量一个网页/App 是否流畅有个比较好用的 Rail 模型, 它大概有以下几个评判标准值。

Response —— 100ms
Animation —— 16.7ms
Idle —— 50ms
Load —— 1000ms

像素管道

像素管道一般由 5 个部分组成。JavaScript、样式、布局、绘制、合成。如下图所示:

渲染性能

保证主动交互让用户感觉流畅

function App() {
  useEffect(() => {
    setTimeout(_ => {
      const start = performance.now()
      while (performance.now() - start < 1000) { }
      console.log('done!')
    }, 5000)
  })
  return (
    <input type="text" />
  );
}

一般超过 50 ms 认为是 long task(长任务), long task 会阻塞 main thread 的运行, 如下是两种解决方案。

Web Worker

app.js 代码如下:

import React, {useEffect} from 'react'
import WorkerCode from './worker'

function App() {
  useEffect(() => {
    const testWorker = new Worker(WorkerCode)
    setTimeout(() => {
      testWorker.postMessage({})
      testWorker.onmessage = function(ev) {
        console.log(ev.data)
      }
    }, 5000)
  })
  return (
    <input type="text" />
  );
}

worker.js 代码如下:

const workerCode = () => {
  self.onmessage = function() {
    const start = performance.now()
    while (performance.now() - start < 1000) { }
    postMessage('done!')
  }
}

此时在输入框输入时没有卡顿的感觉。

Time Slicing

下面是另外一种使页面流畅的方法 —— Time Slicing(时间分片)。

观察 Chrome 的 Performance, 火焰图如下,

从火焰图可以看出主线程被拆分为了多个时间分片, 所以不会造成卡顿。时间分片的代码片段如下所示:

function timeSlicing(gen) {
  if (typeof gen === 'function') gen = gen()
  if (!gen || typeof gen.next !== 'function') return

  (function next() {
    const res = gen.next() // ①
    if (res.done) return // ⑤
    setTimeout(next) // ③
  })()
}

// 调用时间分片函数
timeSlicing(function* () {
  const start = performance.now()
  while (performance.now() - start < 1000) {
    console.log('执行逻辑')
    yield // ②
  }
  console.log('done') // ④
})

该函数虽然代码量不长, 但却不易理解。前置知识 Generator

下面对该函数进行分析:

  1. 往时间分片函数 timeSlicing 中传入 generator 函数;
  2. 函数的执行顺序 —— ①、②、③、① (此时有个竞赛的关系, 如果 performance.now() - start < 1000 则继续 ②、③, 如果 performance.now() - start >= 1000 则跳出循环执行 ④、⑤);

conclusion

针对 long task 会阻塞 main thread 的运行的情形, 给出两种解决方案:

  • Web Worker: 使用 Web Worker 提供的多线程环境来处理 long task;
  • Time Slicing: 将主线程上的 long task 进行时间分片;

保证被动交互让用户感觉流畅

保证 16.7ms 有新的一帧传输到界面上。除去用户的逻辑代码, 一帧内留给浏览器整合的时间大概只有 6ms 左右, 回到像素管道上来, 我们可以从这几方面进行优化:

避免 CSS 选择器嵌套过深

Style 这部分的优化在 css 样式选择器的使用, css 选择器使用的层级越多, 耗费的时间越多。以下是测试 css 选择器不同层级筛选相同元素的一次测试结果。

div.box:not(:empty):last-of-type span         2.25ms
index.html:85 .box--last span                 0.28ms
index.html:85 .box:nth-last-child(-n+1) span  2.51ms

避免布局重排

// 先修改值
el.style.witdh = '100px'
// 后取值
const width = el.offsetWidth

这段代码有什么问题呢?

可以看到它会造成布局重排。

应对的策略是调整它们的执行顺序,

// 先取值
const width = el.offsetWidth
// 后修改值
el.style.witdh = '100px'

可以看到经过调换顺序后, 后执行的 el.style.width 会新开一个像素管道, 而不会在原先的像素管道进行重排。

此外不要在循环中执行如下的操作,

for (var i = 0; i < 1000; i++) {
  const newWidth = container.offsetWidth; // ①
  boxes[i].style.width = newWidth + 'px'; // ②
}

可以在火焰图中看到它发生了重绘的警告,

执行顺序是 ①②①②①②①..., 假若我们在第一个 ① 后面插入一条竖线后 ①|②①②①②①, 其就变成先修改值后取值的情景, 所以也就发生了重绘!

正确的使用姿势应该如下:

const newWidth = container.offsetWidth;
for (var i = 0; i < 1000; i++) {
  boxes[i].style.width = newWidth + 'px';
}

避免重绘

创建 Layers(图层) 可以避免重绘,

{
  transform: translateZ(0);
}

原文地址:https://www.cnblogs.com/MuYunyun/p/10924924.html

时间: 2024-10-02 08:11:32

如何使页面交互更流畅的相关文章

如何让语音交互更流畅?

在体验过几款云后视镜和目前在实现'eagle'过程中,各家都使用第三方语音识别技术的情况下,如何让语音交互过程更流畅.智能?初步思考出两种体验优化方法,请大家斧正! 一.自定义语义词汇完善(难度:**) 1. 用户与车载硬件,通过语音交互,目前阶段更多的是指令的下发与执行.对指令的执行无非两种情况"是或不是".但由于不同方言或文化背景,导致能够表达出"是或不是"的词汇非常多,为了让车载APK更智能,可在后台建立更为完整的'肯定词汇组'与'否定词汇组',这些词汇组通过

[ jquery 效果 slideDown([speed,[easing],[fn]]) slideUp([speed,[easing],[fn]]) ] 此方法用于滑动显示隐藏的被选元素:动画效果只调整元素的高度,可以使匹配的元素以“滑动”的方式显示出来。在jQuery 1.3中,上下的padding和margin也会被有动画,效果更流畅

此方法用于滑动显示隐藏的被选元素:动画效果只调整元素的高度,可以使匹配的元素以“滑动”的方式显示出来.在jQuery 1.3中,上下的padding和margin也会被有动画,效果更流畅 实例: <!DOCTYPE html><html lang='zh-cn'> <head> <title>Insert you title</title> <meta http-equiv='description' content='this is my

谷歌推出全新Android开发语言Sky:让App更流畅

土豆网同步更新:http://www.tudou.com/plcover/VHNh6ZopQ4E/ 使用HTML 创建Mac OS App 视频教程. 官方QQ群: (1)App实践出真知 434558944 (2)App学习交流 452180823 百度网盘同步:http://pan.baidu.com/s/1jG1Q58M 分享  [中文纪录片]互联网时代                 http://pan.baidu.com/s/1qWkJfcS 谷歌推出全新Android开发语言Sky

谷歌、百度页面交互设计简约风格之谈

搜索引擎当真在中国互联网的浪潮中起了无可替代的巨大作用,尤其是谷歌和百度-世界级的老大和中文级的老大.随着用户对信息索取渠道的重要性越来越重要,搜索引擎页面的设计业越来越值得关注,毕竟用户的上网操作能力与互联网素质都在不断的提高中,昨晚网页浏览到一篇关于视频未来发展方向的访谈,艾瑞某高级市场营销负责人谈到互联网的普及也无形中增加了产品设计人员压力,不断的需要自身瘦身,另一方面还得不断增加专业服务强度等,有数据显示,中国网民变得越来越"懒",正如目前知名的这些电子产品和手机应用的变得越来

秒开缓存系统让网速更快更流畅

互联网发展势头日趋迅猛,一些高清视频.精美图片以及网页的加载内容日趋丰富,使得运行流量无限增长,由于对出口产生压力,导致用户体验越来越差.这要是放在从前,你要是着急就只有增加带宽,这是要继续增加投入的,看来这不是长远打算.武汉秒开网络科技有限公司研发的秒开缓存系统的出现,彻底解决了这些难题. 说了这么多,也许你还没有了解秒开缓存系统是个什么东东,举个例子来说吧.你入住一家还算有规模的酒店,想要看个电影解解乏,可是看着看着画面就停在那里了,一个圈圈在那儿转啊转的就是看不了.这说明了什么,是因为这家

(基础篇)PHP与Web页面交互

PHP与Web页面交互是实现PHP网站与用户交互的重要手段.在PHP中提供了两种与Web页面交互的方法,一种是通过Web表单提交数据,另一种是通过URL参数传递. 这里我们将详细讲解表单的相关知识,为以后学习PHP页面交互做好铺垫. 1 .创建表单 Web表单的功能是让浏览者和网站有一个互动的平台.Web表单主要用来在网页中发送数据到服务器,如提交注册信息时需要使用表单.当用户填写完信息后执行提交(submit)操作,于是将表单中的数据从客户端的浏览器传送到服务器端,经过服务器端PHP程序进行处

可以使你成为更优秀程序员的5个好习惯

我们都希望能够在我们所做的事情中得到成长,在WEB开发领域,我们花费时间最多的就是编写代码.这可能包括HTML, CSS, JavaScript, PHP, Python, ActionScript或者任何其他你构建WEB站点时选用的语言. 这篇文章中,我们将分享一些实际的步骤,使你可以扩充技能,成为一个更优秀的程序员.我们提出五个不同的生活习惯,它们可以使你在你所从事的领域变得更加优秀. 1. 一个时间段内只专注于一种类型的语言 如果你正在设计或开发网站,你将需要同时熟悉多种不同的语言.你可能

保养硬盘的技巧,让电脑读写更流畅!

有时候我们会遇到电脑读取不到硬盘无法开机.电脑复制粘贴文件较慢或者是蓝屏的情况,其实这些都可能与我们平时对硬盘保养不当,造成硬盘出现坏掉等损伤有关.今天就与大家分享几个保养电脑硬盘的小技巧,让你的电脑读写更流畅~ 我们在使用硬盘时应注意以下几点: 这样保养硬盘,让你的电脑读写更流畅! 一.硬盘高速运转时不要突然关机 硬盘高速运转时如果我们中途突然关闭电源,可能会导致磁头与盘片猛烈磨擦而损坏硬盘,因此要避免突然关机.怎么判定硬盘是不是在运转可以看面板硬盘指示灯只有在其指示灯停止闪烁.硬盘读写结束后

【转】【C#】C# 5.0 新特性——Async和Await使异步编程更简单

一.引言 在之前的C#基础知识系列文章中只介绍了从C#1.0到C#4.0中主要的特性,然而.NET 4.5 的推出,对于C#又有了新特性的增加--就是C#5.0中async和await两个关键字,这两个关键字简化了异步编程,之所以简化了,还是因为编译器给我们做了更多的工作,下面就具体看看编译器到底在背后帮我们做了哪些复杂的工作的. 二.同步代码存在的问题 对于同步的代码,大家肯定都不陌生,因为我们平常写的代码大部分都是同步的,然而同步代码却存在一个很严重的问题,例如我们向一个Web服务器发出一个