如何实现Riot Games官网导航下拉菜单的同款交互效果
Riot Games官网导航下拉菜单实现方案
效果核心特征
先明确要还原的核心表现,避免做偏:
- 鼠标悬浮到顶部导航项时,下拉面板是全宽通栏样式,不是绑定在单个导航项下方的窄面板
- 显隐带淡入+小幅上移的缓动动画,没有生硬跳变
- 鼠标在不同导航项、导航和下拉面板之间移动时,面板不会闪烁、不会意外消失
- 下拉弹出时,原本透明的顶部导航栏会自动切换为实心底色,下拉收起后回到透明状态
具体实现步骤
1. HTML结构搭建
重点:通栏下拉面板不要嵌套在单个导航项内部,要放在整个导航头容器里,和导航列表平级,否则无法实现全宽效果
参考结构代码:
<header class="site-header"> <div class="nav-inner"> <!-- 这里放logo、主导航列表、右侧登录/语言切换按钮 --> <ul class="nav-list"> <li class="nav-item" data-target="games">游戏</li> <li class="nav-item" data-target="esports">电竞</li> <li class="nav-item" data-target="company">公司</li> <li class="nav-item" data-target="news">资讯</li> </ul> </div> <!-- 通栏下拉容器,和nav-inner平级 --> <div class="mega-menu-wrap"> <div class="mega-panel" data-panel="games"> <!-- 游戏分类下的所有内容,按原站分三栏排版即可 --> </div> <div class="mega-panel" data-panel="esports"> <!-- 电竞板块内容 --> </div> <div class="mega-panel" data-panel="company"> <!-- 公司介绍相关内容 --> </div> <div class="mega-panel" data-panel="news"> <!-- 新闻资讯内容 --> </div> </div> </header>
2. CSS核心样式
- 根导航头固定定位:给
.site-header设置position: fixed; top: 0; left: 0; width: 100%; z-index: 999; transition: background-color 0.25s ease;,默认背景透明,新增.header-solid类时切换为实心底色。 - 下拉容器初始状态:给
.mega-menu-wrap设置position: absolute; top: 100%; left: 0; width: 100%; opacity: 0; visibility: hidden; transform: translateY(8px); transition: all 0.2s ease;,新增.menu-show类时,把属性改为opacity: 1; visibility: visible; transform: translateY(0);,实现淡入上移动画。 - 面板切换逻辑:所有
.mega-panel默认display: none,只有和当前悬浮导航项匹配的面板设为display: block即可,面板内部用flex做分栏排版,和普通页面布局没有区别。 - 防闪烁处理:给导航项和下拉面板之间的衔接区域补透明占位块(可以用nav-item的伪元素实现,高度设为20px,绝对定位在导航项底部,宽度和导航项一致),避免鼠标从导航项移到下拉面板的过程中,因为短暂离开hover区域导致面板意外收起。
3. JS交互逻辑
不建议纯用CSS:hover实现,跨导航项切换时很容易出现闪烁,用少量JS加延迟缓冲逻辑体验更贴近原站
参考代码:
const siteHeader = document.querySelector('.site-header') const navItems = document.querySelectorAll('.nav-item') const megaMenuWrap = document.querySelector('.mega-menu-wrap') const megaPanels = document.querySelectorAll('.mega-panel') let hideTimer = null // 导航项悬浮事件 navItems.forEach(item => { item.addEventListener('mouseenter', () => { // 清除之前的收起定时器,避免快速移动时闪烁 if (hideTimer) clearTimeout(hideTimer) // 匹配要显示的对应面板 const targetPanel = item.dataset.target // 导航切实色、下拉容器显示 siteHeader.classList.add('header-solid') megaMenuWrap.classList.add('menu-show') // 切换面板内容 megaPanels.forEach(panel => panel.style.display = 'none') document.querySelector(`[data-panel="${targetPanel}"]`).style.display = 'block' }) }) // 鼠标离开整个导航头区域时才触发收起 siteHeader.addEventListener('mouseleave', () => { // 加100ms延迟收起,给鼠标移动留缓冲 hideTimer = setTimeout(() => { siteHeader.classList.remove('header-solid') megaMenuWrap.classList.remove('menu-show') }, 100) })
调试注意点:如果出现下拉面板被页面其他内容遮挡的问题,检查其他页面模块的z-index值是否超过了导航头设置的999,不要单独给下拉面板加z-index,统一由导航头的层级管控即可。
内容的提问来源于stack exchange,提问作者Glowing_glasses
相关产品推荐
相关产品推荐

