平滑过渡按钮悬停阴影需显式声明 transition: box-shadow 0.3s cubic-bezier(0.25,0.46,0.45,0.94),避免多层阴影与 background-color 同时过渡,强制 gpu 加速并适配暗色模式。

按钮悬停时阴影突然变大、卡顿,怎么平滑过渡?
CSS 动态阴影的核心不是“加阴影”,而是控制 box-shadow 的变化节奏。直接写 transition: box-shadow 0.3s 往往效果生硬,因为阴影的四个参数(偏移、模糊、扩散、颜色)中,blur-radius 和 spread-radius 对渲染开销影响较大,尤其在低性能设备上容易掉帧。
- 必须显式声明
transition,不能依赖浏览器默认(它通常不包含box-shadow) - 推荐只过渡
box-shadow和transform,避免同时过渡background-color和box-shadow导致重绘范围扩大 - 使用
cubic-bezier(0.25, 0.46, 0.45, 0.94)替代ease,让阴影“弹出”更自然 - 避免在
:hover中新增多层阴影(如写成0 4px 12px #000, 0 8px 24px rgba(0,0,0,0.1)),这会显著增加合成层计算量
button {
box-shadow: 0 2px 6px rgba(0,0,0,0.1);
transition: box-shadow 0.3s cubic-bezier(0.25, 0.46, 0.45, 0.94);
}
button:hover {
box-shadow: 0 6px 16px rgba(0,0,0,0.2);
}
想让阴影随鼠标位置实时移动,但 :hover 不够用
:hover 只能触发区域级响应,无法读取鼠标坐标。要实现“跟随式阴影”,必须用 JavaScript 捕获 mousemove 事件,动态更新自定义 CSS 变量。
- 给按钮设置
position: relative,阴影用伪元素::before实现,避免影响布局流 - 通过
element.style.setProperty('--x', e.offsetX + 'px')向 CSS 传入坐标 - CSS 中用
box-shadow: calc(var(--x) - 50px) calc(var(--y) - 50px) 24px rgba(0,0,0,0.15),注意减去按钮中心偏移(假设宽高为 100px) - 别忘了节流
mousemove,否则高频触发会导致主线程阻塞;用requestAnimationFrame比setTimeout更稳妥
阴影在 Safari 上显示异常或完全消失
Safari 对 box-shadow 的模糊半径(blur-radius)有更严格的硬件加速限制:当值超过约 40px 且未配合 transform: translateZ(0) 时,可能退化为软件渲染,甚至不显示。
- 在按钮上强制开启 GPU 加速:
transform: translateZ(0)或will-change: box-shadow(后者慎用,仅对频繁变化的元素) - 避免在
box-shadow中混用inset和外阴影,Safari 解析顺序易出错 - 如果用了
filter: drop-shadow()替代box-shadow,注意它不支持多层阴影,且无法独立过渡
暗色模式下阴影发灰、对比度不足
box-shadow 的颜色是静态的,不会随系统主题自动切换。硬编码 rgba(0,0,0,0.2) 在深色背景上几乎不可见。
- 用
color-scheme: light dark声明按钮支持主题适配 - 优先使用
hsl()或oklch()表示阴影色,并结合@media (prefers-color-scheme: dark) - 更可靠的方式是定义 CSS 变量:
--shadow-color: hsl(0 0% 0% / 0.2);,然后在媒体查询里覆盖:@media (prefers-color-scheme: dark) { :root { --shadow-color: hsl(0 0% 100% / 0.15); } } - 不要用
currentColor做阴影色——它取的是文字色,在暗色模式下通常是浅色,反而导致阴影过亮
真实项目里最容易被忽略的是:阴影动画的“起点”和“终点”必须保持相同结构(比如都是单层阴影、单位一致、RGBA 通道完整)。少一个 px、多一个空格、或漏写 alpha 通道,都可能导致浏览器放弃过渡而直接跳变。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











