直接transition box-shadow生硬因浏览器线性插值缺乏物理扩散感;::after伪元素通过scale、opacity和blur过渡模拟中心向外涌出的阴影效果。

为什么直接 transition box-shadow 会显得生硬
直接给 box-shadow 加 transition 确实能动,但阴影边缘的“扩散感”很弱——因为浏览器对阴影的过渡是线性插值颜色和尺寸,缺乏物理意义上的“从中心向外晕开”的视觉节奏。人眼更容易感知到阴影轮廓的伸缩或位移变化,而不是模糊半径的匀速增大。
::after 伪元素怎么模拟阴影扩散
核心思路是:用一个绝对定位的 ::after 元素,把它做成和卡片等宽高的椭圆(border-radius: 50%),填充深色半透明色,再通过 transform: scale() 控制它从 0.8x 缩放至 1.4x,同时配合 opacity 和 filter: blur() 的过渡,就能骗过眼睛,产生“阴影从内向外涌出”的效果。
关键点:
- 伪元素必须设
content: ""、position: absolute、z-index: -1(确保在卡片下方) -
top/left建议设为50%,再用transform: translate(-50%, -50%)居中,方便后续scale()围绕中心展开 -
blur()值建议从8px过渡到24px,太大易糊,太小无感 - 过渡属性要写全:
transition: transform 0.3s ease, opacity 0.3s ease, filter 0.3s ease(不能只写all,否则可能触发重排)
完整可复用的 CSS 片段
.card {
position: relative;
border-radius: 12px;
overflow: hidden;
}
.card::after {
content: "";
position: absolute;
top: 50%;
left: 50%;
width: 200%;
height: 200%;
background: rgba(0, 0, 0, 0.16);
border-radius: 50%;
opacity: 0;
filter: blur(8px);
transform: translate(-50%, -50%) scale(0.8);
transition: transform 0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275),
opacity 0.3s ease,
filter 0.3s ease;
z-index: -1;
}
.card:hover::after {
opacity: 1;
filter: blur(24px);
transform: translate(-50%, -50%) scale(1.4);
}
注意:width/height 设为 200% 是为了确保缩放时阴影始终覆盖卡片区域;cubic-bezier() 用了略微超调的缓动,增强“涌出”感,比默认 ease 更自然。
立即学习“前端免费学习笔记(深入)”;
容易被忽略的兼容性与性能细节
::after 扩散阴影在 Safari 15.4+ 和 Chrome 100+ 表现稳定,但旧版 Safari 对 filter: blur() + transform 同时过渡有闪烁问题——可加 will-change: transform, opacity, filter 到伪元素上强制 GPU 加速。
另外,如果卡片本身有 overflow: hidden(比如带圆角的图片容器),要确认伪元素是否被裁切:此时需把伪元素移到卡片父容器上,或改用 clip-path 替代 border-radius 来规避裁剪。
真正在意性能的场景下,避免在大量卡片上同时 hover——浏览器要为每个伪元素单独合成图层,百级卡片可能触发掉帧。


















