让页面更加快速的加载,运行更加流畅,并且动画和交互也应该顺滑如丝,以顺应更多网络用户的需求。而编写高性能的网站应用就需要了解浏览器如何处理HTML、CSS 和 JavaScript,确保代码尽可能地高效运行
浏览器的渲染
通常浏览器渲染分以下几步进行
- 根据HTML构建HTML树(DOM),根据CSS构建CSS树(CSSOM),将两棵树合并在一起,形成渲染树(render tree)
- 样式计算,根据匹配选择器,计算出哪些元素应该用那些CSS规则的过程,知晓规则后将应用并计算每个元素的最终样式
- 布局,即根据文档流、盒模型等计算出元素的大小以及所在位置
- 绘制,将CSS中的边框、背景等样式进行适配
- 合成,根据层叠关系等,将元素样式共同展现出来
如果指定帧发生变化,即使用 JavaScript | CSS 或者 网络动画 ,实现视觉变化的时候,针对变化通常由以下三种运行方式.
JS / CSS > 样式 > 布局 > 绘制 > 合成
如果修改了元素的 “layout” 属性,例如修改了元素的宽度,高度或者位置等,那么浏览器就必须检查其他的所有元素,然后自动重新排版页面,任何受影响的部分都将重新绘制,最终绘制好的元素再进行合成JS / CSS / > 样式 > 绘制 > 合成
如果修改的是背景图片 或者 文字颜色阴影,这些不影响页面排版布局的属性,那么浏览器会跳过布局,执行绘制,最后再合成
- JS / CSS > 样式 > 合成
如果更改的是一个不要布局也不要绘制的属性,那么浏览器就将跳转到执行合成,如改变transform
[最后这个版本浏览器执行的步骤最少,适用于动画或者滚动]
渲染优化
优化JavaScript
- 对于动画效果的实现,使用
requestAnim``ationFrame
来代替setTimeout
或setInterval
- 将长时间运行的JavaScript 从主线移到 Web Worker
- 使用微任务来执行对多个帧的DOM更改
- 使用Chrome的开发者工具里面的Timeline和JavaScript分析器评估JavaScript的影响
缩小样式计算的范围并降低其复杂性
通过添加和删除元素,更改属性 类,或者通过动画来更改DOM,都会导致浏览器重新计算元素样式,很多时候会对页面或者其中一部分进行自动重排.
- 降低选择器的复杂性;使用以类为中心的方法,例如BEM.
- 减少必须计算器央视的元素数量
避免大型 复杂的布局和布局抖动
- 布局的作用范围一般为整个文档
- DOM元素的数量将影响性能; 尽可能避免触发布局
- 评估布局模型的性能;
- 避免强制同步布局和布局抖动; 先读取样式值,然后进行样式更改
简单绘制的复杂度 减小绘制区域
- 除了
transform
或者opacity
属性外,更改任何属性始终都会触发绘制 - 绘制通常是像素管道中开销最大的部分, 所以尽可能避免绘制
- 通过层的提升和动画的编排来减少绘制区域
- 使用开发者工具中绘制分析器评估绘制的复杂性和开销, 尽可能降低复杂性并且减少开销
坚持仅合成器的属性和管理层计数
- 使用 t
ransform
和opacity
属性更改来实现动画 - 使用
will-change
或者translateZ
提升移动的元素 - 避免过度使用提升规则; 各层都需要内存和管理开销
使输入处理程序去除抖动
- 避免长时间运行输入处理程序; 它们可能组织滚动
- 不要再输入处理程序中进行更改样式
- 使处理程序去除抖动 ; 存储事件值并在下一个
requestAnimationFrame
回调中处理样式更改
CSS动画
利用transform
来实现动画效果,其常用属性为以下四个
translate: 这是一个控制元素移动的属性,有如下的表达方式 [支持的单位可以是像素,也可以是容器的%]translateX( px | % )
translateY( px | % )
translate( px|% , px|% )[X 和 Y的缩写]
left: 50%;
top: 50%;
transform: translate( -50%,-50% );
可以用来实现绝对定位元素的垂直水平居中translateZ( px | % ); [父容器需要设定十点距离:perspective(px) ]
scale: 此为控制元素缩放的属性scaleX( number ); 在X方向上缩放()倍
scaleY( number ); ``在Y方向上缩放()倍
scale( number , number ); 将X和Y合并
rotate:让元素旋转,单位可以是度数[deg] | 圈数[turn]rotateX(angle|zero);绕X轴旋转多少度
rotateY(angle|zero);``绕Y轴旋转多少度
rotate( turn ); 旋转多少圈
rotateZ(angle|zero);``绕Z轴旋转多少度
skew: 倾斜,单位也是度数[deg] | 圈数[turn]skewX( angle | zero );在X轴方向倾斜
skewY( angle | zero );在Y轴方向倾斜
skew( angle|zero , angle|zero );综合X和Y
需要注意的是inline
元素并不支持transform
,需要先将其转换成bloc
k.
再着通常会添加transition
让变化更加平滑.
transition相关属性transition:
过度属性名 时长 过渡方式 延迟执行时间
- 可以用 逗号 分隔开两个不同的过度属性来分开设置
- 可以用 `all` 来代表所有属性
- 过渡的方式有如下几种
- `linear[线性] | ease[灵活] | ease-in[淡入] | ease-out[淡出] | ease-in-out[淡入淡出] | cubic-bezier[贝塞尔曲线] | step-start | step-end | steps`
- 如有`display: none过渡到block` ,则需要将其改为 `visibility: hidden==>visible`
- 如果有多次过渡变动的话
- 可以使用多次transform,
- `a== transform ==b`
b== transform ==c
- 用`setTimeout `或者监听` transitionend `事件
- 使用`animation`
- 声明关键帧
- 添加动画
animation
先为animation
定义关键帧,通过@keyframes
定义关键帧来控制CSS动画序列中的中间步骤,通常有两种写法
1. 定义初始 from
状态 和to
结束状态
@keyframes slidein {
from {
margin-left: 100%;
width: 300%;
}
to {
margin-left: 0%;
width: 100%;
}
}
2 . 以%形式定义对应帧数的样式
@keyframes identifier {
0% { top: 0; left: 0; }
30% { top: 50px; }
68%, 72% { left: 50px; }
100% { top: 100px; left: 100%; }
}
animation
语法
缩写为: animation: 时长 | 过渡方式 | 延迟 | 循环次数 | 方向 | 填充模式 | 是否暂停 | 动画名
- 时长: 可以是 s 或者 ms
- 过渡方式: 线性 | 淡入 | 淡出等,类似于 transition取值
- 延迟: 写入时间控制动画延时执行
- 次数: 可以指定循环多少次,可以是 infinite无限次
- 方向:
- `reverse`[反向运行动画,周期结束动画由尾到头运行]
- `alternate`[交替反向运行]
- `alternate-reverse`
- 填充模式:
- `none` [动画未执行时,不会应用任何样式]
- `forwards `[动画结束之后将保留动画的最后一个关键帧]
- `backwards`[动画将在应用于目标时,立即应用第一个关键帧的值, 在延时时会会保留这个第一帧的值]
- `both `[遵循`forwards` 和` backward `的规则, 在两个方向上都扩展动画属性]
- 是否暂停: `paused | running`
- 动画名: 即是上面@keyframes定义的动画名称