如何实现平滑侧边栏鼠标悬停交互动画效果
侧边悬停导航跟随动画实现方案
这个交互用原生Web技术即可实现,不需要引入第三方动画库,核心是通过JS获取鼠标悬停元素的位置,驱动绝对定位的高亮块移动,配合CSS过渡实现平滑效果。
核心逻辑拆解
- 导航容器固定在视口右侧,默认收起为窄条仅展示汉堡图标,鼠标移入时平滑拉宽展示完整导航文字
- 导航容器内部放置一个绝对定位的高亮指示块,默认透明隐藏
- 监听每个导航项的鼠标移入事件,实时计算当前悬停项相对于导航容器的偏移位置、自身高度,同步给高亮指示块
- 鼠标移出整个导航区域时,高亮块平滑淡出回到初始隐藏状态
- 所有尺寸、位置、颜色、透明度变化都通过CSS transition实现,保证动画流畅无卡顿
完整实现代码
HTML 结构
<!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <link rel="stylesheet" href="style.css"> <title>侧边导航交互动画</title> </head> <body> <nav class="side-nav"> <!-- 汉堡菜单按钮 --> <div class="nav-toggle"> <div class="toggle-bar"></div> <div class="toggle-bar"></div> <div class="toggle-bar"></div> </div> <!-- 导航项容器 --> <div class="nav-links"> <!-- 跟随移动的高亮指示块 --> <div class="nav-indicator"></div> <a href="#" class="nav-item">work</a> <a href="#" class="nav-item">about</a> <a href="#" class="nav-item">pricing</a> </div> </nav> <script src="script.js"></script> </body> </html>
CSS 样式
* { margin: 0; padding: 0; box-sizing: border-box; } /* 侧边导航基础样式 固定在右侧 */ .side-nav { position: fixed; right: 0; top: 0; height: 100vh; width: 70px; background: #efefef; display: flex; flex-direction: column; align-items: center; transition: width 0.3s cubic-bezier(0.4, 0, 0.2, 1); overflow: hidden; z-index: 999; } /* 鼠标移入导航区域 展开宽度 */ .side-nav:hover { width: 120px; } /* 汉堡按钮样式 */ .nav-toggle { width: 100%; padding: 22px 0; display: flex; flex-direction: column; align-items: center; gap: 6px; cursor: pointer; } .toggle-bar { width: 34px; height: 4px; background: #121212; border-radius: 2px; } /* 导航项容器 */ .nav-links { position: relative; width: 100%; flex: 1; display: flex; flex-direction: column; padding: 20px 0; } /* 高亮指示块 绝对定位不干扰正常交互 */ .nav-indicator { position: absolute; left: 8px; right: 8px; height: 0; border-radius: 6px; background: #121212; opacity: 0; transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); pointer-events: none; } /* 导航项样式 */ .nav-item { position: relative; width: 100%; padding: 18px 0; text-align: center; text-decoration: none; color: #121212; font-weight: 700; font-size: 1.3rem; text-transform: lowercase; transition: color 0.3s ease; z-index: 1; } /* 导航项悬停时文字反色 显示在高亮块上层 */ .nav-item:hover { color: #ffffff; }
JS 逻辑
// 获取需要操作的DOM节点 const sideNav = document.querySelector('.side-nav'); const navIndicator = document.querySelector('.nav-indicator'); const navItems = document.querySelectorAll('.nav-item'); // 遍历所有导航项 绑定鼠标移入事件 navItems.forEach(item => { item.addEventListener('mouseenter', (e) => { const currentItem = e.currentTarget; // 获取元素相对于视口的位置信息 const itemRect = currentItem.getBoundingClientRect(); const navRect = sideNav.getBoundingClientRect(); // 动态更新高亮块的位置和尺寸 navIndicator.style.opacity = '1'; navIndicator.style.height = `${itemRect.height}px`; navIndicator.style.top = `${itemRect.top - navRect.top}px`; }) }) // 鼠标移出整个导航区域时 隐藏高亮块 sideNav.addEventListener('mouseleave', () => { navIndicator.style.opacity = '0'; })
实现说明
- 高亮块添加
pointer-events: none属性,鼠标事件可以直接穿透到下层的导航项,不会出现交互阻断 - 所有动画使用CSS过渡实现,由浏览器渲染层优化处理,比JS逐帧修改样式的性能表现更好
- 导航项位置通过JS实时计算获取,后续增删导航项不需要额外修改逻辑代码,适配性更强
- 所有动画的缓动函数、时长保持统一,视觉上不会有节奏割裂的感觉
内容的提问来源于stack exchange,提问作者Bart
相关产品推荐
相关产品推荐

