本节摘要:变换让元素位移缩放旋转而不动版面,过渡让状态切换平滑收场,动画让元素按剧本自行演出。本节讲三者的分工与写法、缓动函数的手感差异,并兑现 1.3 节的伏笔:只动外观与合成层属性的动效最便宜,动几何属性的重排最贵。看板的徽章换装、卡片入场与流动条纹在本节完成。
动效家族三兄弟各管一段:变换负责"摆在哪里"——平移、缩放、旋转、倾斜,只影响视觉呈现不惊动版面;过渡负责"变化的过程"——浏览器自动补齐前后两个状态之间的中间帧;动画负责"完整的剧本"——关键帧序列写好,元素自己照演。判断该用哪个的问题只有一句:是换个状态(过渡),还是放段影片(动画),还是仅仅挪个视角(变换)。

过渡是看板用得最多的动效。写法只需要两条:常态规则里声明"哪些属性要过渡、多久、什么缓动",状态规则里改这些属性的值——浏览器补全过程。工单状态切换时徽章从灰变蓝再变绿,若没有过渡,颜色瞬跳像信号灯故障;有过渡,颜色在半秒里流过去,视线自然跟上。
/* 徽章换装的过渡:行为班换类名,样式班负责过程 */ .badge { transition: color 0.25s ease, background-color 0.25s ease; /* 声明在常态规则里:颜色与底色变化时按四分之一秒缓动过渡 */ } .badge-running { color: var(--status-running); background: rgb(225 240 255 / 45%); } .badge-done { color: var(--status-done); background: rgb(226 246 233 / 50%); } /* 类名一换,颜色渐变收场,无需任何脚本参与过程 */ /* 卡片悬停:边框显色加微抬 */ .stat-card { transition: transform 0.2s ease, border-color 0.2s ease, box-shadow 0.2s ease; } .stat-card:hover { transform: translateY(-3px); /* 变换上移三像素:视觉抬起,版面纹丝不动 */ border-color: rgb(26 110 200 / 35%); box-shadow: 0 6px 16px rgb(44 62 80 / 12%); }
/* 缓动手感对照:同一场位移动的三种性格 */ .demo-ease-in-out { transition: transform 0.3s ease-in-out; } /* 缓起缓收:稳重,默认首选 */ .demo-ease-out { transition: transform 0.3s ease-out; } /* 快起缓收:响应,界面反馈常用 */ .demo-ease-in { transition: transform 0.3s ease-in; } /* 缓起快收:退出感,少用 */ /* 过渡的纪律:只声明需要动的属性 */ .card-bad { transition: all 0.3s; } /* 反面:全部属性打包过渡, 改任何一条声明都平白动一下 */ .card-good { transition: transform 0.2s; } /* 正面:点名要动的属性, 其他变化即时生效 */
过渡需要一个状态触发,动画不需要——声明即上演,适合入场、加载、常驻循环。剧本用关键帧书写:给几个时间点各写一套属性值,浏览器补齐全程。看板两个用工:统计卡片入场时依次淡入上浮(用延迟参数错峰),加工中徽章的条纹背景流动(3.3 节已见其一半)。
/* 卡片入场:淡入上浮,三张卡片错峰 */ @keyframes card-in { from { opacity: 0; transform: translateY(12px); } /* 起点:透明且低十二像素 */ to { opacity: 1; transform: translateY(0); } /* 终点:完全可见归位 */ } .stat-card { animation: card-in 0.4s ease-out backwards; /* backwards:延迟开始前保持起点帧,不会先闪现再消失 */ } .stat-card:nth-child(2) { animation-delay: 0.08s; } /* 第二张晚八十毫秒 */ .stat-card:nth-child(3) { animation-delay: 0.16s; } /* 第三张再晚一档 */ /* 错峰入场:三张卡片鱼贯而入,页面有了呼吸 */
/* 常驻循环:加工中的呼吸点 */ @keyframes pulse { 0% { box-shadow: 0 0 0 0 rgb(26 110 200 / 45%); } 100% { box-shadow: 0 0 0 10px rgb(26 110 200 / 0%); } } .badge-running .dot { animation: pulse 1.6s ease-out infinite; /* 扩散的圆环:一圈一圈荡开,"机器在转"的视觉隐喻 无限循环但极轻:只动投影,不动几何 */ } /* 动画的谦逊原则:用户偏好减少动效时尊重之 */ @media (prefers-reduced-motion: reduce) { .stat-card, .badge-running .dot { animation: none; } .badge { transition: none; } /* 读屏与眩晕用户在系统层关掉动效,页面照常工作 */ }
第二段代码末尾的媒体查询是动效的礼貌课:部分用户会因为眩晕症或注意力问题在系统设置里关闭动效,样式班查询这个偏好并服从它,动效是增强而非必需品——这延续了 2.6 节"撤掉增强仍可用"的结构哲学。
兑现 1.3 节的伏笔。渲染流水线上,改几何属性(宽高、位置偏移、边距)触发重新布局,全页相关盒子跟着重算,最贵;改外观属性(颜色、背景、阴影)跳过布局直接重绘,中等;只动变换与透明度时,浏览器把元素放进独立合成层,调整只发生在合成工序,最便宜。所以动效的黄金律只有一条:能动变换,不动几何。
/* 同是位移,两种代价 */ .slide-bad { left: 0; transition: left 0.3s; } /* 动布局属性:每帧重排 */ .slide-bad:hover { left: 24px; } .slide-good { transform: translateX(0); transition: transform 0.3s; } /* 动变换:合成层直达 */ .slide-good:hover { transform: translateX(24px); } /* 两者肉眼相同,帧率计上差距悬殊:列表一长,前者掉帧后者丝滑 */
⚠️ 常见坑:动画时长一律写一秒以上、给大块面积加持续阴影动画。前者让界面显得迟缓(界面动效的经验区间是两百到四百毫秒),后者让绘制工序每帧大面积重刷。动效的目标是"被感知而不被注意",超过这个区间就是在抢戏。
💡 关键直觉:写每条动效前默念一遍黄金律——能动变换与透明度,不动几何;再加一条保险——元素面板里有渲染分层视图,勾选后页面涂上彩色图层,持续变化的区域是否独立成层一目了然。
装修动感就位,下一节做全尺寸验收:响应式设计,让同一张看板在车间平板与办公室显示器上都体面。