第 31 节 · 过渡与动画全解
第 31 节 · 过渡与动画全解
📌 版本信息:基于 CSS Animations/Transitions Level 1/2(现行规范;2026-08-29 核对) 📚 来源:MDN CSS transitions | MDN CSS animations | MDN 合成层与性能
一、这一节的目标
- 精通
transition的五个子属性与简写 - 精通
@keyframes+animation全参数 - 理解浏览器渲染三步:重排 > 重绘 > 合成,知道"哪些属性能安全地动"
- 掌握
transform/opacity优先原则与will-change的克制用法 - 完成地图场景的经典动效(弹窗弹出、脉冲标记、骨架屏)
二、transition:状态 A → B 的补间
/* 四个子属性:property duration timing-function delay */
.card {
transition: background 0.3s ease, transform 0.2s ease-out;
}
.card:hover {
background: #f0f7ff;
transform: translateY(-2px);
}
/* hover 改变状态,transition 负责"平滑过去"——transition 永远写在"静止态" */
| 子属性 | 说明 | 常用值 |
|---|---|---|
| property | 要过渡的属性(写 all 省事但易误伤) |
明确列出 |
| duration | 时长 | 0.15 |
| timing-function | 缓动曲线 | ease(默认)、ease-out(进场利器)、linear(匀速循环)、cubic-bezier(.4,0,.2,1) |
| delay | 延迟 | 多元素错峰入场 |
⚠️
transition: all的坑:all 会把 width/height 等昂贵属性也补间(掉帧+意外动画),明确列属性是纪律。
三、@keyframes:自己编排动画
transition 只能"两点之间",@keyframes 能排整段演出:
/* 定义:0% 和 100% 是帧锚点,中间帧随意插 */
@keyframes pulse {
0% { box-shadow: 0 0 0 0 rgba(255, 0, 0, 0.6); }
70% { box-shadow: 0 0 0 12px rgba(255, 0, 0, 0); }
100% { box-shadow: 0 0 0 0 rgba(255, 0, 0, 0); }
}
/* 使用:animation: 名字 时长 曲线 次数 ... */
.marker-alert {
animation: pulse 1.5s ease-out infinite; /* infinite = 循环 */
}
animation 完整参数:name duration timing-function delay iteration-count direction fill-mode。
高频参数:
animation-iteration-count: infinite | 3;
animation-direction: alternate; /* 往返(波浪、呼吸灯) */
animation-fill-mode: forwards; /* 结束后停在最后一帧(不加会弹回原状!) */
animation-play-state: paused; /* JS 可控制暂停 */
💡 GIS 场景:警报点脉冲(上面就是)、骨架屏 shimmer(灰色块左右扫光)、marker 掉落(translateY + opacity 入场)。第 15 模块 deck.gl 的 TripLayer 是数据维度的动画,DOM 层的这套是界面维度的。
四、性能:重排 > 重绘 > 合成
浏览器把"画面更新"分三档成本:
1. 重排(layout):几何变了(width/height/top/left/margin…)
→ 重新计算布局 + 绘制 + 合成,整条链最贵
2. 重绘(paint):只变了颜色/阴影(color/background/shadow)
→ 绘制 + 合成
3. 合成(composite):只用 transform/opacity
→ GPU 直接搬已有图层,几乎免费 ✅
结论(动画铁律):优先动画 transform(translate/scale/rotate)和 opacity;避免动画 width/height/top/left。例:
/* ❌ 动 top:每帧重排 */
.toast { position: absolute; top: 0; transition: top .3s; }
.toast.show { top: 20px; }
/* ✅ 动 transform:只走合成 */
.toast { transform: translateY(-100%); transition: transform .3s; }
.toast.show { transform: translateY(0); }
will-change:提前告知"这个属性要动画",让浏览器备好图层:
.map-canvas { will-change: transform; } /* ⚠️ 克制:只给真正高频动画的元素,滥用反而吃内存 */
💡 地图关联:地图拖动时浏览器就是在做海量 transform 合成——Leaflet 用
transform: translate3d移动瓦片层。你懂了合成层,就懂了地图为什么能 60fps。
五、动手跟练(无独立练习文件,融进两个现成练习)
本节为阶段二文档,动手任务藏在两个已有练习里回炉:
- 回炉
examples/07-定位与层叠.html:给弹窗加入场动画(opacity 0→1 + translateY 8px→0,200ms ease-out)——注意用 transform 而不是 top - 回炉
examples/27-暗色切换页.html:给主题切换加transition: colors 0.3s( Tailwind 的transition-colors类),并说明它走的是重绘还是合成
六、自测题
- transition 和 @keyframes 各适合什么场景?
- animation-fill-mode: forwards 不写会怎样?
- 为什么动画 top 掉帧、动画 transform 流畅?
- 重排/重绘/合成的成本排序?各举两个触发属性。
- will-change 为什么不能滥用?
参考答案
- transition:两个既有状态之间的切换补间(hover/类切换触发);keyframes:多帧编排、自动播放、循环、复杂路径。
- 动画结束瞬间跳回原始状态(除非原始态恰好等于最后一帧)。
- top 改变几何 → 每帧重排(重新布局绘制);transform 由合成器(GPU)处理已生成图层,不进布局与绘制。
- 重排 > 重绘 > 合成;重排:width/top;重绘:color/box-shadow;合成:transform/opacity。
- 每个提示都消耗额外显存建图层;页面里成百个 will-change 会爆内存、反而更卡。只给"已知要频繁动画"的元素用,动画结束后可移除。
七、下一步
样式篇收官 → 第 32 节:原型与 this 全解,进入 JS 阶段二硬核区。