大家好,我是程序员柏风,AI 最全的 Vibecoding 学习地图 http://coderoadmap.cn 作者。
动画与变换
这一课你将能做到
- 用 transition 做平滑的属性过渡
- 用 transform 做位移、旋转、缩放、倾斜
- 用 @keyframes + animation 做复杂关键帧动画
- 用 will-change 和 transform 优化动画性能
为什么要学
没有动画的页面像静态照片,加了动画就有了生命。但动画做得不好反而伤害体验——卡顿、闪烁、过度炫技。学这一课,你要让动画「丝滑且克制」。
人话:transition 像「渐变开关」——从 A 状态平滑到 B 状态;@keyframes 像「分镜脚本」——你写好每一帧,浏览器照着演;transform 是「直接操作图层」,性能最好的动画方式。
先建立图像
<style>
.btn {
padding: 12px 24px;
background: #0366d6;
color: #fff;
border: none;
border-radius: 6px;
cursor: pointer;
transition: all 0.3s ease;
}
.btn:hover {
background: #024fa3;
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(3, 102, 214, 0.4);
}
</style>
<button class="btn">悬停我</button>
鼠标悬停,按钮 0.3 秒内平滑变深色、上移 2px、出现阴影。
跟着做
步骤 1:transition 过渡
.box {
transition: 属性 时长 缓动 延迟;
transition: all 0.3s ease;
transition: background 0.3s, transform 0.5s 0.1s;
}
四要素:
- 属性:要过渡的 CSS 属性(
all表示全部) - 时长:
0.3s或300ms - 缓动:
ease(默认)/linear/ease-in/ease-out/ease-in-out/cubic-bezier(...) - 延迟:可选,多久后开始
步骤 2:哪些属性能过渡
能过渡:color、background、width、height、padding、margin、opacity、transform、box-shadow 等数值/颜色类属性。
不能过渡:display(none 到 block 突变)、font-family、position。
步骤 3:transform 变换
.box { transform: translate(20px, 30px); } /* 平移 x, y */
.box { transform: translateX(20px); }
.box { transform: scale(1.2); } /* 缩放 */
.box { transform: scale(2, 0.5); } /* x 放大 2 倍,y 缩一半 */
.box { transform: rotate(45deg); } /* 旋转 */
.box { transform: skew(10deg, 20deg); } /* 倾斜 */
.box { transform: translate(20px, 30px) rotate(45deg) scale(1.2); } /* 链式 */
transform-origin:变换的中心点,默认 50% 50%(中心):
.box { transform-origin: top left; transform: rotate(45deg); }
步骤 4:实战——卡片悬停效果
<style>
.card {
width: 240px;
background: #fff;
border-radius: 8px;
overflow: hidden;
box-shadow: 0 2px 8px rgba(0,0,0,0.08);
transition: transform 0.3s, box-shadow 0.3s;
}
.card:hover {
transform: translateY(-6px);
box-shadow: 0 12px 24px rgba(0,0,0,0.15);
}
.card img {
width: 100%;
display: block;
transition: transform 0.5s;
}
.card:hover img { transform: scale(1.1); }
</style>
<div class="card">
<img src="https://picsum.photos/400/200" alt="">
<div style="padding: 16px;">卡片标题</div>
</div>
步骤 5:@keyframes 关键帧
@keyframes spin {
from { transform: rotate(0deg); }
to { transform: rotate(360deg); }
}
@keyframes pulse {
0% { transform: scale(1); opacity: 1; }
50% { transform: scale(1.1); opacity: 0.7; }
100% { transform: scale(1); opacity: 1; }
}
.loader {
width: 40px; height: 40px;
border: 4px solid #eee;
border-top-color: #0366d6;
border-radius: 50%;
animation: spin 0.8s linear infinite;
}
步骤 6:animation 属性
.box {
animation: 名称 时长 缓动 延迟 次数 方向 填充模式 播放状态;
animation: pulse 1.5s ease-in-out infinite;
animation: spin 1s linear 0s infinite normal both running;
}
各子属性:
animation-name:@keyframes 名animation-duration:时长animation-timing-function:缓动animation-delay:延迟animation-iteration-count:次数(infinite无限)animation-direction:normal/reverse/alternate/alternate-reverseanimation-fill-mode:forwards(停在最后)/backwards/bothanimation-play-state:running/paused
步骤 7:性能优化——只用 transform 和 opacity
浏览器渲染分四步:Layout(布局)→ Paint(绘制)→ Composite(合成)。
只动 transform 和 opacity 的动画,跳过 Layout 和 Paint,直接 Composite,最流畅。
/* 好性能 */
.card:hover { transform: translateY(-4px); opacity: 0.9; }
/* 差性能——会触发 Layout */
.card:hover { width: 320px; margin-top: -4px; }
will-change 提前告诉浏览器「我要动这个属性」:
.card { will-change: transform; }
不要滥用,只在确定要动的元素上加。
步骤 8:尊重用户偏好
@media (prefers-reduced-motion: reduce) {
*, *::before, *::after {
animation-duration: 0.01ms !important;
transition-duration: 0.01ms !important;
}
}
操作系统里设了「减少动画」的用户,给他们静态体验。
讲清楚要点
- transition 是状态之间的过渡,需要触发(hover、加类);animation 是独立播放,可以自动循环。
- transform 不影响布局:兄弟元素不会被推挤,性能最好。
- 动画性能黄金法则:只用 transform + opacity;其他属性会触发 Layout/Paint。
- will-change 是预告不是许愿:滥用反而耗内存。
- transition 写在元素上而不是 :hover 上:写在 :hover 上只有 hover 时才有过渡,离开时突变。
你可能会懵的地方
-
transition 和 animation 选哪个? 简单两态转换用 transition;多帧、循环、复杂节奏用 animation。
-
display: none 能过渡吗? 不能。先用 opacity 淡出,再用
visibility: hidden或 JS 移除。 -
transform 多个值顺序有影响吗? 有。
translate rotate和rotate translate结果不同(后者先转再沿转后的轴平移)。 -
GPU 加速是什么?
transform: translateZ(0)或will-change: transform会让浏览器创建独立图层,交给 GPU 合成,所以流畅。 -
animation 结束想保持最后一帧? 加
animation-fill-mode: forwards。
错法对照
| 错法 | 问题 | 正解 |
|---|---|---|
transition 写在 :hover 上 | 离开时突变 | 写在元素默认状态 |
| 想动画 height 从 0 到 auto | auto 不能过渡 | 用 max-height 大值,或 JS 计算 |
动画用 left/top 改变位置 | 触发 Layout 卡顿 | 用 transform: translate |
给所有元素 will-change | 内存爆炸 | 只在确定要动的元素加 |
@keyframes 名称拼错 | 动画静默不播放 | 检查拼写和引用一致 |
卡点急救
- transition 不生效:检查属性是否可过渡;是否有单位突变(如 0 → auto);是否在
display: none之间切换。 - 动画一卡一卡:F12 → Performance 录制;改用 transform/opacity;减少 box-shadow 复杂度。
- 动画结束跳回初始:加
animation-fill-mode: forwards。
面试常问
-
CSS 动画性能优化原则? 只动 transform 和 opacity(合成层动画,跳过 Layout 和 Paint);合理使用 will-change;避免大面积 box-shadow 动画。
-
transition 和 animation 区别? transition 需要触发,A 到 B 两态;animation 自动播放,可有任意多个关键帧,支持循环。
-
GPU 加速怎么触发?
transform: translateZ(0)、will-change: transform、opacity动画会创建合成层。
自测三问
- 为什么
left: 100px的动画比transform: translateX(100px)卡? transition: all 0.3s写在元素上和:hover上有何区别?- 如何让 animation 停在最后一帧?
小结一句话
简单过渡用 transition,复杂节奏用 @keyframes;性能上只动 transform 和 opacity,让 GPU 干活,浏览器渲染管线会谢谢你。
延伸
- MDN CSS 动画
- 下一课:进阶工程化,BEM、CSS 变量、响应式、暗色主题。