由浅入深了解css3动画
前端动画,是前端工程师“拉开差距”非常明显的一项能力。
很多人会:
transitiontransform- 会一点
animation
但真正高级前端,会:
- 理解浏览器渲染机制
- 知道为什么动画会卡
- 知道什么时候该用 CSS、JS、Canvas、WebGL
- 能做复杂交互动效
- 能做高性能动画架构
- 能实现产品级动画系统
你现在的目标,不应该是“记 API”。
而是:建立一整套“动画思维体系”。
一、先建立动画世界观
你先记住一句:动画,本质是“让某个值随时间变化”。
二、浏览器如何产生动画
你必须理解浏览器渲染流程。
生成 DOM 树 // 解析 HTML 标签,生成文档对象模型(DOM)
↓
生成 CSSOM 树 // 解析 CSS 样式,生成 CSS 对象模型(CSSOM)
↓
合成渲染树 Render Tree // 将 DOM 和 CSSOM 合并,dom节点和对应的样式合并
↓
Layout(重描/回流) // 计算每个元素在屏幕上的确切位置和大小
↓
Paint(重绘) // 将渲染树的每个节点转换为屏幕上的实际像素
↓
Composite(合成)// 将不同的图层合成在一起,最终显示在屏幕上
1. layout(重描/回流)
最贵
例如:宽高(width,height)位置信息(top,left)自身布局(margin,padding,font-size)等
发生改变时,会影响外部布局或者内部布局,会触发:重新计算布局
2. paint(重绘)
中等。
例如:背景(background)、阴影(box-shadow)、颜色(color)、圆角(border-radius)
这些属性发生改变,需要重新绘制。
3. composite(合成层)
最便宜。
例如:transform、opacity
只需要 GPU 合成。
所以:
黄金法则
尽量动画:
- transform
- opacity
少动:
- width
- height
- top
- left
这是动画性能核心。
为什么transform,opacity不会引起重描和重绘?
1、元素使用了 transform 或 opacity(且该元素被提升为独立图层)
2、元素的几何尺寸没变,只是变换矩阵变了,所以跳过重排。图层里的像素内容没变(只是整体移动或变透明),所以跳过重绘。
三、Transition(控制如何变化)
transition:
- transition-property,// all/background/opacity,transform
- transition-duration,// 动画持续时长 2s
- transition-timing-function,//linear/ease/ease-in/ease-out/ease-in-out
- transition-delay
示例:
transition:
transform 0.35s cubic-bezier(0.34, 1.56, 0.64, 1),
box-shadow 0.35s ease;
1. timing-function
控制速度曲线。
linear
匀速。斜率为1的直线
ease
默认。
慢 -> 快 -> 慢。
起步稍快一点,中间加速明显,结尾减速更柔和;非对称 S 形(默认)
ease-in
越来越快。
ease-out
越来越慢。
ease-in-out
两头慢。
前半段加速、后半段减速,形状镜像;对称 S 形
四、真正理解贝塞尔曲线
CSS 动画核心:cubic-bezier(x1, y1, x2, y2, x3, y3)
含义:
- 起点固定:(0, 0)
- 终点固定:(1, 1)
- (x1, y1):第一个控制点
- (x2, y2):第二个控制点 // 二次贝塞尔
- (x3, y3):第三个控制点 // 三次贝塞尔
控制点不落在曲线上,而是把曲线「拉」向自己
看斜率,斜率与变化速率成正比
| 控制点 | 直觉 |
|---|---|
|
P1 的 y1 小 |
开头慢(ease-in) |
|
P1 的 y1 大 |
开头快 |
|
P2 的 y2 大 |
结尾还快一阵再刹住(ease-out) |
|
P2 的 y2 小 |
结尾提前减速 |
|
P1、P2 对称 |
接近 ease-in-out |
等价写法示例
linear: cubic-bezier(0, 0, 1, 1);
ease: cubic-bezier(0.25, 0.1, 0.25, 1);
ease-in: cubic-bezier(0.42, 0, 1, 1);
ease-out: cubic-bezier(0, 0, 0.58, 1);
ease-in-out: cubic-bezier(0.42, 0, 0.58, 1);
合法范围(timing-function 时)
| 参数 | 范围 | 说明 |
|---|---|---|
|
x1, x2 |
必须在 0 ~ 1 |
时间不能倒流、不能跳过 |
|
y1, y2 |
可以 小于 0 或大于 1 |
允许「冲过头再回来」(回弹) |
例如:
transition: all .5s cubic-bezier(0.4, 0, 0.2, 1);
本质:
控制速度变化。
五、 transform(触发transition,及自身如何变化),
1. translate 移动
translate(tx, ty)。如果ty未指定,则假定其值为零。
2. scale 缩放
scale(sx, sy)。如果sy未指定,则假定等于sx
3. rotate 旋转
将元素绕其原点(由transform-origin属性指定)旋转指定角度。可以写成rotate(a)
4、transform-origin: 水平方向 垂直方向;
- x:0%,50%,100%
- y:0%,50%,100%
可以想象元素是一个矩形:
top left top center top right
┌─────────────────────────┐
│ │
│ │
left center right center
│ │
│ │
└─────────────────────────┘
bottom left bottom center bottom right
六、 keyframes 定义关键帧
@keyframes move1 {
0% {
transform: translateX(0);
}
100% {
transform: translateX(300px);
}
}
@keyframes move2 {
from {
transform: translateX(0);
}
to {
transform: translateX(300px);
}
}
七、animation(使用关键帧) 核心参数
- @keyframes:使用哪个动画帧
- animation-name:使用的动画帧的名称
- animation-duration:完成动画时长
- animation-timing-function:动画变化速率
- animation-delay:动画延迟多久开始
- animation-iteration-count:动画循环次数
示例:
animation:
typing 3s steps(12, end) forwards,
cursorBlink 0.7s step-end infinite;
八、真正重要:requestAnimationFrame
requestAnimationFrame会让浏览器在下一次页面重绘前执行你传入的函数。- 浏览器通常一秒刷新 60 次,也就是:1000ms / 60 ≈ 16.6ms
- 所以大约每隔
16.6ms绘制一帧。 - 如果你每一帧都更新一次元素位置,就能形成流畅动画。
基本用法
let animationId;
function animate(time) {
console.log("time",time);
animationId = requestAnimationFrame(animate);
}
animationId = requestAnimationFrame(animate);
// 停止
cancelAnimationFrame(animationId);
// 不需要时间参数的时候
animate()
- 这个
time表示当前动画帧的时间,可以用来计算动画进度。
注意:requestAnimationFrame 只执行一次。如果想连续动画,需要在回调里再次调用它。
更多推荐


所有评论(0)