页面视觉稳定性之CLS

BitLuminaryX
• 阅读 3013

你是否曾经在线读一篇文章的时候,页面上突然发生了一些改变?没有任何警告,文字移动了,你不知道读到那里了。或者更糟糕的,你准备点一个链接或者按钮,但就在你手指触摸到屏幕的前一秒,链接移动了,你点到了其他地方。

大部分情况,这些体验只会引起你的反感,但有时候,会对你造成无法挽回的伤害。

页面视觉稳定性之CLS

如上图,当你想要点击灰色按钮的时候,突然出现了一个提示,你将点击到蓝色按钮。

页面视觉稳定性之CLS

预料之外的页面内容的移动经常发生,因为资源的异步加载,或者dom元素的动态添加。罪魁祸首有可能是未知尺寸的图片或者视频,或者字体文件(比默认字体大或者小),亦或是第三方广告或者挂件(会自动改变大小)。

CLS(Cumulative Layout Shift) 的性能指标可以帮你定位这些问题,通过测试真实用户这些问题发生的频率。

什么是CLS?

CLS 是所有布局偏移分数的汇总,凡是在页面完整生命周期内预料之外的布局偏移都包括。

布局偏移发生在任意时间,当一个可见元素改变了它的位置,从一个渲染帧到下一个。

页面视觉稳定性之CLS

什么是一个好的CLS分数?

75%以上的用户小于0.1。

布局偏移的具体内容

布局偏移是由 Layout Instability API 定义的。这个API会在任意时间上报 layout-shift 的条目,当一个可见元素在两帧之间,改变了它的起始位置(默认的 writing mode 下指的是top和left属性)。这些元素被当成不稳定元素。

需要注意的是,布局偏移只发生在已经存在的元素改变起始位置的时候。如果一个新的元素被添加到dom上,或者已存在的元素改变它的尺寸,除非改变了其他元素的起始位置,否则都不算布局偏移。

布局偏移分数

为了计算布局偏移分数,浏览器看的是视图尺寸和两帧之间不稳定元素的移动。布局偏移的分数是两个度量的乘积:影响分数(impact fraction)和距离分数(distance fraction)。

layout shift score = impact fraction * distance fraction
为什么是两个度量的乘积?因为如果是一个很大的元素移动了较小的距离,实际影响并不大,所以分数需要依赖两个度量。

影响分数

影响分数测试的是两帧之间,不稳定元素在视图上的影响范围。

包含前后两帧,不稳定元素的可见区域的总和除以总的视图大小,就是当前帧的影响分数。

页面视觉稳定性之CLS

上图中,元素在一帧中占了屏幕的一半。下一帧,元素下移了25%的视图高度。红色虚线框起来的部分就是不稳定元素在两帧的占的视图总和(75%),所以影响分数是0.75。

距离分数

距离分数测试的是两帧之间,不稳定元素在视图上移动的距离(水平和纵向取最大值)。如果有多个不稳定元素,也是取其中最大的一个。

页面视觉稳定性之CLS

上面的例子,不稳定元素在纵向移动了25%,所以距离分数是0.25。

所以布局偏移分数是 0.75 * 0.25 = 0.1875

下一个例子是在已存在的元素中插入一个新的元素,计算布局偏移分数:

页面视觉稳定性之CLS

"Click Me!"按钮被插入到灰色盒子下方,把绿色盒子往下推了。

在这个例子中,灰色盒子只是改变了尺寸,所以不算不稳定元素。

"Click Me!"按钮因为是新插入的元素,所以也不算不稳定元素。

绿色盒子的起始位子改变了,但因为部分区域不在视图内,不在视图内的区域不算入影响分数,所以影响分数是0.5。

距离分数是蓝色尖头的部分,绿色盒子往下移动了14%,所以距离分数是0.14。

布局偏移分数是 0.5 * 0.14 = 0.07

最后一个例子是多个不稳定元素:

页面视觉稳定性之CLS

第一帧中,API结果返回了4个,按字母序排列。第二帧返回了更多,同样的是字母序。

第一个元素 "Cat" 前后没有改变起始位子,所以是稳定的。同样,新加入的元素,也是稳定的。只有 "Dog", "Horse", "Zebra" 这三个元素改变了起始位子,是不稳定元素。

红色虚线框起来的部分就是影响分数,表示所有不稳定元素可见区域占视图的比例,大概是0.38。

蓝色箭头所指的就是距离分数,因为这是所有不稳定元素中最大的移动距离,大概是0.3。

布局偏移分数是 0.38 x 0.3 = 0.1172

预料 vs 预料之外的布局偏移

并非所有布局偏移都是不好的。实际上,很多动态web应用经常性的改变元素的起始位置。

用户启动的布局偏移

用户预料之外的布局偏移才是不好的。换句话说,如果是为了响应用户的交互而产生的布局偏移通常是很好的。只要布局偏移与用户交互足够紧密,这两者之间的关系对用户而言是很明确的。

例如,用户触发了一个网络请求,可能需要花一段时间才能结束,最好的做法是创造一些空白区域或者展示loading,避免请求结束后的预料之外的布局偏移。如果用户没有意识到正在加载数据,或者不知道资源是否加载完成,他们在等待的过程中会点击某些元素,这些元素后面可能会移动到了下方,就跟文章开头的图一样。

布局偏移如果发生在用户输入之后的500ms内,会有一个 hadRecentInput 的标记,这样就可以被排除在外。

动画和过渡

动画和过渡,如果做得好,对用户而言是一个不错的更新内容的方式,这样不会给用户“惊喜”。突然出现的或者预料之外的内容,会给用户一个很差的体验。但如果是一个动画或者过渡,用户可以很清楚的知道发生了什么,在状态变化的时候可以很好的引导用户。

CSS中的 transform 属性可以让你在使用动画的时候不会产生布局偏移。

  • transform:scale() 来替换 widthheight 属性
  • transform:translate() 来替换 top, left, bottom, right 属性

如何测试CLS?

可以集成 web-vitals 的库,代码如下:

import {getCLS} from 'web-vitals';

// Measure and log the current CLS value,
// any time it's ready to be reported.
getCLS(console.log);

CLS是所有独立的 layout-shift 条目的汇总(排除掉用户输入近期的条目)。

比起统计每一个 layout-shift 条目,在页面隐藏之后,统计一个CLS的总分更合适。

如何改善CLS?

对大部分网站而言,可以参考以下:

  • 对图片和视频元素总是设定好尺寸,否则保留所需的空间。这样可以保证浏览器给这些元素分配足够的空间,在加载之后,不会产生布局偏移。如果浏览器支持,可以开启 unsized-media feature policy 的策略。
  • 永远不要把内容插入到已有元素的上方,除非为了响应用户交互。
  • 如果需要用到动画,优先使用不会产生布局偏移的CSS属性。

更多CLS优化可以期待后面的文章。

总结

CLS是平时开发很少关注的点,页面视觉稳定性对很多web开发而言,可能没有加载性能那么关注度高,但对用户而言,这确实是很困扰的一点。平时开发中,尽可能的提醒自己,不管是产品交互图出来之后,或者是UI的视觉稿出来之后,如果出现了布局偏移的情况,都可以提出这方面的意见。开发过程中也尽可能的遵循上面提到的一些优化点,给用户一个稳定的视觉体验。

参考

https://web.dev/cls/

点赞
收藏
评论区
推荐文章
Wesley13 Wesley13
3年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
美凌格栋栋酱 美凌格栋栋酱
7个月前
Oracle 分组与拼接字符串同时使用
SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(
Wesley13 Wesley13
3年前
RAC环境单实例启动数据库收到ORA
     在RAC环境中,如果你在没有启动节点的集群服务的情况下单实例启动数据库,将收到类似如下的报错:\oracle@rhel1u01\$sqlSQL\Plus:Release10.2.0.5.0ProductiononTueApr215:00:272013Copyright(
陆石六 陆石六
2年前
为什么苹果用户都在使用Downie 4 for Mac?
Downie4forMac:让你轻松下载任何视频你是否经常在网上看到一些有趣或有用的视频,想要下载到自己的Mac上,但是却发现没有合适的工具或方法?你是否想要在没有网络的情况下,也能随时随地观看你喜欢的视频?你是否想要把你下载的视频转换成其他格式,或者提取
Peter20 Peter20
4年前
mysql中like用法
like的通配符有两种%(百分号):代表零个、一个或者多个字符。\(下划线):代表一个数字或者字符。1\.name以"李"开头wherenamelike'李%'2\.name中包含"云",“云”可以在任何位置wherenamelike'%云%'3\.第二个和第三个字符是0的值wheresalarylike'\00%'4\
Karen110 Karen110
4年前
​一篇文章总结一下Python库中关于时间的常见操作
前言本次来总结一下关于Python时间的相关操作,有一个有趣的问题。如果你的业务用不到时间相关的操作,你的业务基本上会一直用不到。但是如果你的业务一旦用到了时间操作,你就会发现,淦,到处都是时间操作。。。所以思来想去,还是总结一下吧,本次会采用类型注解方式。time包importtime时间戳从1970年1月1日00:00:00标准时区诞生到现在
Stella981 Stella981
3年前
Python+Selenium自动化篇
本篇文字主要学习selenium定位页面元素的集中方法,以百度首页为例子。0.元素定位方法主要有:id定位:find\_element\_by\_id('')name定位:find\_element\_by\_name('')class定位:find\_element\_by\_class\_name(''
Wesley13 Wesley13
3年前
mysql设置时区
mysql设置时区mysql\_query("SETtime\_zone'8:00'")ordie('时区设置失败,请联系管理员!');中国在东8区所以加8方法二:selectcount(user\_id)asdevice,CONVERT\_TZ(FROM\_UNIXTIME(reg\_time),'08:00','0
Stella981 Stella981
3年前
Spring Boot 2.x基础教程:配置元数据的应用
在使用SpringBoot开发应用的时候,你是否有发现这样的情况:自定义属性是有高量背景的,鼠标放上去,有一个Cannotresolveconfigurationproperty的配置警告。!(https://oscimg.oschina.net/oscnet/up18d0b9a752073814dc961b2245368afa3d4.
Wesley13 Wesley13
3年前
ISO27001信息安全管理体系
0x00前言初入甲方,刚开始接触的应该就是ISO27001信息安全管理体系,你拿到的应该就是一整套安全管理类的文档。在甲方,稍微有点规模的公司很注重制度和流程,岗位职责分工明细,那么这些安全管理制度,就是你所能掌控的游戏规则,几个人的信息安全部生存之道。0x01ISO27001简介ISO/IEC27001信息安全管理体系
Stella981 Stella981
3年前
CSS实现文字两端对齐
最近的项目遇到了这样的需求:(要求标题部分不管文字多少,都必须两端对齐)如下图:!(https://oscimg.oschina.net/oscnet/6e151291c0c55e2a231d00ec198d6c5be11.png)当时也没有多想直接使用‘ ’进行代替,毕竟产品同学想快一点看到效果,不敢怠慢!不过到第二个页面就傻眼了