用 transform + ::after 实现下划线平滑位移,需监听 mouseenter 获取目标位置与尺寸,结合 position: relative 容器、absolute 伪元素、动态 width 与 translatex,并添加 transition、防抖、移动端 touch 适配及 overflow 处理。

用 transform + ::after 实现下划线平滑位移
直接用 transform: translateX() 控制伪元素位置,比修改 left 更高效,避免重排。关键不是“画线”,而是让线“知道鼠标在哪”——所以得监听导航项的 mouseenter 事件,拿到目标元素的 offsetLeft 和 offsetWidth。
- 下划线容器(如
.nav)需设position: relative,否则::after无法相对于它定位 - 伪元素默认用
position: absolute,bottom: 0对齐,width初始设为0,靠 JS 动态赋值 - 过渡必须写在伪元素上:
transition: transform 0.3s ease, width 0.3s ease,不能只写all
监听 mouseenter 而非 mousemove
导航栏项数通常固定且不多,mouseenter 触发更精准、开销更低。mousemove 在整个 nav 区域频繁触发,容易卡顿,还得多算鼠标是否真在某个 item 上。
- 每个
.nav-item绑定mouseenter,读取e.currentTarget.offsetLeft和clientWidth - 下划线元素(比如
.underline)用getBoundingClientRect()获取父容器宽度,确保计算基准一致 - 别忘了加防抖:快速连续划过多个 item 时,先
clearTimeout上一次动画计时器
处理宽度变化和居中对齐
如果导航项文字长度不一,或用了 justify-content: center,单纯用 offsetLeft 会偏移。此时要基于容器内布局重新计算相对位置。
- 用
item.getBoundingClientRect().left - nav.getBoundingClientRect().left替代offsetLeft,绕过 padding/border 影响 - 若 nav 本身有
padding-left,记得从结果里减去该值,否则线会右偏 - 居中场景下,可改用
transform: translateX(calc((100% - itemWidth) / 2))配合 JS 设置--item-widthCSS 变量
兼容性与性能注意点
IE 不支持 transform 在伪元素上过渡,但现代项目基本不用考虑;真正容易翻车的是移动端 touch 事件没适配,以及未关闭硬件加速导致闪屏。
- 移动端需额外监听
touchstart,并阻止默认行为(e.preventDefault()),否则可能触发双击缩放 - 给下划线加
will-change: transform提前启用 GPU 加速,但别滥用——只在动画期间加,动画结束删掉 - 如果导航项是动态渲染(比如 Vue/React),确保 DOM 挂载完成后再初始化下划线逻辑,否则
offsetWidth可能为 0
实际最易忽略的是父容器的 overflow: hidden ——有些设计稿要求 nav 外观圆角,顺手加了 border-radius 和 overflow: hidden,结果下划线被截断。这时候要么去掉 overflow,要么把下划线提到父容器外层用 z-index 控制层级。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











