CSS 阴影动画不明显,通常不是动画没触发,而是阴影的可动画参数、关键帧设计和缓动函数没有形成足够的视觉对比。直接给元素加上 box-shadow 的过渡或动画后,阴影从无到有、从浅到深的过程可能被默认的 ease 缓动压缩,再加上颜色透明度设置过低,最终看起来就像瞬间切换一样。要解决这个问题,需要重新设计关键帧,并选择合适的 timing-function。

一、阴影动画不明显的三个直接原因
box-shadow 是一个复合属性,它可以同时描述阴影的水平偏移、垂直偏移、模糊半径、扩散半径和颜色。浏览器做动画插值时,会对这些分量分别计算中间值。如果起始阴影和结束阴影的差异主要体现在透明度上,而模糊半径又设置得很大,阴影会被大幅度分散,视觉上很难察觉到变化。例如从 rgba(0, 0, 0, 0) 到 rgba(0, 0, 0, 0.2) 的阴影,本身就不够强烈,加上高斯模糊后边缘更柔和,动画过程容易被忽略。
第二个原因是关键帧写得过于简单。很多动画只设置了 from 和 to 两个状态,中间完全交给浏览器插值。如果动画时长较短,或者两端状态差异小,中间过渡自然不明显。更合适的做法是把动画拆成多个阶段,让阴影在某个时间点达到较高的不透明度和较大的偏移量,再逐渐收回,这样阴影会形成明显的呼吸感。
第三个原因是缓动函数的影响。CSS 动画默认使用 ease,它的特点是开始和结束较慢、中间加速。对于阴影这种需要持续存在的视觉效果,缓动会让阴影在大多数时间停留在接近起点或终点的状态,真正变化明显的时间窗口很短,用户自然会觉得阴影动画很弱。
二、用 animation 与 timing-function 让阴影起伏更清晰
要让阴影动画变得明显,可以重新组织关键帧,不再只写两个端点。把动画分成四个阶段:初始无阴影、快速增强到明显阴影、保持并稍微减弱、最后回到无阴影。这样阴影的变化幅度更大,视觉停留时间也更合理。下面是一段阴影呼吸动画的示例。
@keyframes shadowPulse {
0% {
box-shadow: 0 0 0 0 rgba(0, 0, 0, 0);
}
40% {
box-shadow: 0 18px 36px rgba(0, 0, 0, 0.35);
}
70% {
box-shadow: 0 6px 12px rgba(0, 0, 0, 0.12);
}
100% {
box-shadow: 0 0 0 0 rgba(0, 0, 0, 0);
}
}
.card {
animation: shadowPulse 2.4s cubic-bezier(0.4, 0.2, 0.2, 1) infinite;
}在上面的代码中,阴影在 40% 处已经达到最深状态,之后的 30% 时间用于减弱,最后再恢复到无阴影。这样的曲线比简单的线性变化更有节奏。cubic-bezier(0.4, 0.2, 0.2, 1) 是一个前段较快、后段较慢的缓动函数,能让阴影快速出现、缓慢消失,增强视觉存在感。如果想制造轻微的弹性回弹,也可以使用 cubic-bezier(0.34, 1.56, 0.64, 1),贝塞尔曲线的第二个控制点允许大于 1,阴影会出现类似弹跳的效果。
timing-function 不仅能写在 animation 简写属性里,还可以写在每个关键帧内部,例如 @keyframes shadowPulse { 0% { animation-timing-function: ease-in; ... } }。这样不同的动画段可以有不同的缓动策略。需要注意的是,关键帧内部的 animation-timing-function 作用于从该关键帧到下一个关键帧之间的区间,而不是整个动画。对阴影动画来说,如果希望出现阶段性的跳动,可以使用 steps(2, end) 这样的阶跃函数,让阴影在两个状态之间直接切换,适合制作闪烁或警报类效果。
三、性能友好的替代方案:伪元素承载阴影
直接对元素自身的 box-shadow 做动画,浏览器需要反复计算阴影的模糊、扩散和绘制,可能会触发持续的重绘。在低性能设备上,动画就容易出现卡顿或掉帧,进一步削弱阴影变化的视觉效果。如果阴影本身不需要改变形状,只改变透明度,可以把阴影固定在伪元素上,让伪元素的 opacity 参与动画。
.card {
position: relative;
transform: translateZ(0);
}
.card::before {
content: "";
position: absolute;
inset: 0;
border-radius: inherit;
box-shadow: 0 16px 32px rgba(0, 0, 0, 0.25);
opacity: 0;
z-index: -1;
animation: shadowFade 2s cubic-bezier(0.4, 0, 0.2, 1) infinite;
}
@keyframes shadowFade {
0%, 100% {
opacity: 0;
}
50% {
opacity: 1;
}
}这段代码中,::before 伪元素使用 position: absolute 和 inset: 0 覆盖整个卡片区域,并通过 border-radius: inherit 继承圆角,避免阴影和元素形状不一致。阴影颜色和模糊值固定不变,动画只改变 opacity。由于 opacity 可以交给合成器处理,不需要重新计算阴影,动画的流畅度会明显提升。为了保证伪元素显示在卡片背后,设置了 z-index: -1,卡片本身需要创建层叠上下文,例如通过 position: relative 和 transform: translateZ(0)。
CSS阴影动画box-shadowtiming-function修改时间:2026-08-22 03:04:00