过渡 = 「状态变化时自动补帧」;它需要两个不同的显式状态,本身不会循环

① 四个要素:属性、时长、缓动、延迟

/* 完整形式 */
.btn { transition: background-color 200ms ease-out 0s, border-color 200ms ease-in 50ms; }
/* 简写顺序:property duration easing delay(duration 在前、delay 在后,两个时间同时出现时) */
.btn { transition: .3s ease; }                     /* 未写 property 时默认 all —— 会带来意外的连锁动画 */
.btn { transition-property: none; }                /* 临时关掉过渡 */
/* 常见坑:把 transition 只写在 :hover 里 → 只有悬停时有过渡,移开时瞬间跳回 */
.a:hover { transition: all .3s; }                  /* ✗ */
.a { transition: all .3s; } .a:hover { … }         /* ✓ 写在基础状态上 */

② 缓动决定「手感」

linear
ease(默认)
ease-in-out
cubic-bezier(.2,.9,.2,1.2) 回弹
linear() 关键帧缓动
steps(6,end) 逐帧
/* UI 动效经验值:进入用 ease-out(快起手、慢落位),离开用 ease-in,来回用 ease-in-out */
transform: translateX(0); transition: transform .2s cubic-bezier(.2,.8,.2,1);
/* linear() 可直接写关键帧式曲线,回弹/过冲不必再算贝塞尔控制点 */
transition-timing-function: linear(0, .6 20%, 1 40%, .8 55%, 1);
/* steps() 用于精灵图逐帧、打字机效果 */

③ transform 2D 全家

translate
scale
rotate
skewX
matrix
可串联
.x:hover { transform: translate(6px, -4px) rotate(-6deg) scale(1.1); }
/* 顺序有影响:transform 是矩阵相乘,先 translate 再 rotate 与反过来结果不同 */
/* translate 的百分比相对自身尺寸 → 这是「未知宽高也能居中」的根据(第十二章) */
/* scale 不改变布局占位,只是绘制时被放大:相邻元素不会让位,可能重叠 */

④ 3D 与 transform-origin

rotateX
rotateY
rotateY 180°
origin: top left
正面
背面
/* 3D 需要父级给深度感 */
.wrap { perspective: 500px; }
.card:hover { transform: rotateY(180deg); }
/* 翻转卡片:两面绝对定位叠在一起 + backface-visibility: hidden(见示例右侧那张) */
/* transform-origin 决定旋转/缩放的支点:翻牌要设 center,展开菜单要设左上角 */
.origin { transform-origin: top left; }
/* 3D 里还可以 transform-style: preserve-3d 让子元素保持立体关系(做立方体、层叠卡) */

⑤ 只动 transform 与 opacity

transform 版(合成层,流畅)
margin-left 版(触发布局)
属性代价
transform、opacity、filter(谨慎)、background-position(视情况)只需合成,不触发布局与重排 → 动画首选
width/height/top/left/margin/padding触发布局(layout)→ 大量元素同时动会掉帧
box-shadow、border-radius、background-color触发重绘(paint),大面积时明显
left/top 做位移改用 transform: translate(),视觉一致但成本低得多

⑥ 想给阴影加动效:动伪元素的 opacity

正确:::after 阴影淡入 常见:直接过渡 box-shadow
/* box-shadow 的过渡要逐帧重绘整块模糊,代价高且中间帧发灰 */
.lift::after { box-shadow: 0 8px 22px rgba(0,0,0,.22); opacity: 0; transition: opacity .22s; }
.lift:hover::after { opacity: 1; }
/* 注意 z-index:-1 让阴影层在背景之下,父级背景必须不透明 */

动效必须有降级:@media (prefers-reduced-motion: reduce) 下把过渡与动画收敛(第十三章给的全站写法),或者只保留 opacity 的极短过渡。