返回笔记列表
01 · NOTES / WebGIS 开发

第 31 节 · 过渡与动画全解

2026 年 9 月 1 日

第 31 节 · 过渡与动画全解

📌 版本信息:基于 CSS Animations/Transitions Level 1/2(现行规范;2026-08-29 核对) 📚 来源:MDN CSS transitionsMDN CSS animationsMDN 合成层与性能

一、这一节的目标

  1. 精通 transition 的五个子属性与简写
  2. 精通 @keyframes + animation 全参数
  3. 理解浏览器渲染三步:重排 > 重绘 > 合成,知道"哪些属性能安全地动"
  4. 掌握 transform/opacity 优先原则与 will-change 的克制用法
  5. 完成地图场景的经典动效(弹窗弹出、脉冲标记、骨架屏)

二、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.150.3s 微交互 / 0.40.6s 大面板
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。


五、动手跟练(无独立练习文件,融进两个现成练习)

本节为阶段二文档,动手任务藏在两个已有练习里回炉:

  1. 回炉 examples/07-定位与层叠.html:给弹窗加入场动画(opacity 0→1 + translateY 8px→0,200ms ease-out)——注意用 transform 而不是 top
  2. 回炉 examples/27-暗色切换页.html:给主题切换加 transition: colors 0.3s( Tailwind 的 transition-colors 类),并说明它走的是重绘还是合成

六、自测题

  1. transition 和 @keyframes 各适合什么场景?
  2. animation-fill-mode: forwards 不写会怎样?
  3. 为什么动画 top 掉帧、动画 transform 流畅?
  4. 重排/重绘/合成的成本排序?各举两个触发属性。
  5. will-change 为什么不能滥用?

参考答案

  1. transition:两个既有状态之间的切换补间(hover/类切换触发);keyframes:多帧编排、自动播放、循环、复杂路径。
  2. 动画结束瞬间跳回原始状态(除非原始态恰好等于最后一帧)。
  3. top 改变几何 → 每帧重排(重新布局绘制);transform 由合成器(GPU)处理已生成图层,不进布局与绘制。
  4. 重排 > 重绘 > 合成;重排:width/top;重绘:color/box-shadow;合成:transform/opacity。
  5. 每个提示都消耗额外显存建图层;页面里成百个 will-change 会爆内存、反而更卡。只给"已知要频繁动画"的元素用,动画结束后可移除。

七、下一步

样式篇收官 → 第 32 节:原型与 this 全解,进入 JS 阶段二硬核区。

TAGSweb