最可靠方案是用::before或::after伪元素配合transform:scaleX()和transition,因border-bottom-width和text-decoration不支持平滑过渡,会跳变或无效;伪元素需设content、定位及transform-origin以精准控制滑入效果。

直接用 ::before 或 ::after 伪元素 + transform: scaleX() + transition,是最可靠、性能最好、且无需 JS 的方案。其他方式(如 border-bottom 过渡宽度、text-decoration 改颜色)要么不生效,要么会触发重排或兼容性差。
为什么不能直接 transition border-bottom-width 或 text-decoration
浏览器根本不支持对 border-bottom-width 做平滑过渡——它会“跳变”,不是动画;text-decoration: underline 更惨,连 color 和 thickness 都无法单独控制,transition 完全无效。你看到的“下划线出现”,其实是浏览器强制重绘,毫无动画可言。
-
border-bottom只能过渡color或opacity,但宽度变化必须靠伪元素模拟 -
text-decoration是渲染层指令,不是布局盒,无法参与 CSS 动画管线 - 想让线条“从左滑入”“从中展开”“双侧合拢”,都得靠独立定位的伪元素 +
transform
::before 伪元素实现左到右滑入下划线
核心是把下划线做成一个绝对定位的细条,初始缩成 0,hover 时拉伸到 100%,同时用 transform-origin: left center 控制缩放锚点。
- 父级
a必须设position: relative,否则::before会相对 body 定位 -
content: ""不可省,否则伪元素不生成 - 推荐用
height: 2px+background-color,比border更可控、无边框盒模型干扰 - 过渡只写
transform 0.3s ease-out,不要用all,避免意外触发其他属性重绘
/* 示例代码 */
a {
position: relative;
text-decoration: none;
}
a::before {
content: "";
position: absolute;
bottom: -2px;
left: 0;
width: 100%;
height: 2px;
background-color: #007bff;
transform: scaleX(0);
transform-origin: left center;
transition: transform 0.3s ease-out;
}
a:hover::before {
transform: scaleX(1);
}移动端和高 DPI 屏幕要注意什么
在 iOS Safari 或 macOS Chrome 上,scaleX(0) 有时会因 subpixel 渲染导致线条残留模糊像素;transform 在某些安卓 WebView 中若未触发硬件加速,可能卡顿。
立即学习“前端免费学习笔记(深入)”;
- 加
will-change: transform到伪元素,提前提示浏览器启用 GPU 加速 - 避免用小数像素值(如
bottom: -1.5px),统一用整数并配合Math.round()(JS 场景下) - 真机测试时重点看快速连续悬停是否掉帧——如果卡,把
transition时间降到0.2s或换用transform: translateX()+width组合 - 不要依赖
:hover做核心交互逻辑,触屏设备无 hover 状态,需搭配:focus或 JS 补充
想让下划线“跟随鼠标移动”,纯 CSS 做不到
所谓“导航栏下划线随鼠标滑动”,本质是动态计算每个 a 的位置再更新共享下划线元素的 left 和 width。CSS 的 :hover 只能作用于当前元素及其后代,没法让一个兄弟节点响应另一个的悬停状态。
- 必须用 JS 监听
mouseenter,读取getBoundingClientRect()计算相对坐标 - 伪元素无法被 JS 直接操作
left/width,得改用真实 DOM 元素(如<span class="underline"></span>) - 别用
offsetLeft—— flex 布局、padding、transform 缩放都会让它失准 - 过渡要分开写:
transition: transform 0.3s ease, width 0.3s ease,别合在all里
真正难的不是写出第一版动画,而是让所有设备上都清晰、不闪、不跳、不糊——尤其是当导航项字体大小不一、有 padding 差异、或用了 variable font 时,bottom 值和 transform-origin 得反复调。别迷信“复制粘贴就完事”,每个项目都要实机测三遍。


















