如何用JavaScript实现<li>悬停启动CSS动画、切换时停止重置
解决鼠标悬停控制多元素CSS动画的启动与重置问题
我来帮你搞定这个需求——当鼠标悬停某个<li>时启动四个圆形元素的CSS动画,切换到其他<li>时停止并重置之前的动画。下面是完整的可实现方案:
调整后的HTML结构
首先给每个导航项加上标识类,方便后续JS定位,同时确保每个<li>里的动画元素结构清晰:
<nav class="strokes"> <ul> <li class="nav-item"> <div class="circle one"></div> <div class="circle2 two"></div> <div class="circle3 three"></div> <div class="circle4 four"></div> </li> <li class="nav-item"> <!-- 第二个li的动画元素,结构和第一个保持一致即可 --> <div class="circle five"></div> <div class="circle2 six"></div> <div class="circle3 seven"></div> <div class="circle4 eight"></div> </li> </ul> </nav>
CSS 动画与样式定义
我们用类来触发动画,同时定义重置状态的样式,确保动画能彻底回到初始状态:
/* 基础圆形样式,对应动画初始状态 */ .circle, .circle2, .circle3, .circle4 { width: 24px; height: 24px; border-radius: 50%; margin: 8px 0; background-color: #2c3e50; opacity: 0; transform: translateX(30px); transition: none; /* 避免和动画冲突 */ } /* 动画触发类:给元素添加这个类就会启动动画 */ .animate-circle { animation: slideInRight 0.6s forwards; } /* 给四个圆形设置不同的动画延迟,形成依次出现的效果 */ .animate-circle.one { animation-delay: 0s; } .animate-circle.two { animation-delay: 0.15s; } .animate-circle.three { animation-delay: 0.3s; } .animate-circle.four { animation-delay: 0.45s; } /* 动画关键帧(可以根据你的需求修改动画效果) */ @keyframes slideInRight { to { opacity: 1; transform: translateX(0); } } /* 重置类:强制元素回到初始状态 */ .reset-circle { animation: none !important; opacity: 0; transform: translateX(30px); }
JavaScript 核心逻辑
通过监听鼠标事件,控制动画的启动和重置,核心是处理切换时的重置逻辑:
// 获取所有导航项 const navItems = document.querySelectorAll('.nav-item'); // 给每个导航项绑定鼠标事件 navItems.forEach(currentItem => { // 鼠标悬停时:启动当前项的动画,重置其他项的动画 currentItem.addEventListener('mouseover', () => { // 遍历所有导航项,重置非当前项的动画 navItems.forEach(item => { if (item !== currentItem) { const circles = item.querySelectorAll('.circle, .circle2, .circle3, .circle4'); circles.forEach(circle => { // 先移除动画类,添加重置类 circle.classList.remove('animate-circle'); circle.classList.add('reset-circle'); // 强制触发浏览器重绘,确保重置生效(关键步骤) void circle.offsetWidth; // 移除重置类,为下次动画做准备 circle.classList.remove('reset-circle'); }); } }); // 启动当前导航项的动画 const currentCircles = currentItem.querySelectorAll('.circle, .circle2, .circle3, .circle4'); currentCircles.forEach(circle => { circle.classList.add('animate-circle'); }); }); // 可选:鼠标离开当前项时,也重置自身的动画 currentItem.addEventListener('mouseout', () => { const currentCircles = currentItem.querySelectorAll('.circle, .circle2, .circle3, .circle4'); currentCircles.forEach(circle => { circle.classList.remove('animate-circle'); circle.classList.add('reset-circle'); void circle.offsetWidth; circle.classList.remove('reset-circle'); }); }); });
关键细节解释
- 动画重置的核心:
void circle.offsetWidth这一行是为了强制浏览器重新计算元素尺寸,触发重绘,否则直接移除动画类可能不会让元素立刻回到初始状态,导致下次动画无法正常播放。 - 动画触发方式:通过添加/移除
animate-circle类来控制动画的启动,这种方式比直接操作style属性更灵活,也符合CSS与JS分离的原则。 - 多元素序列动画:通过给每个圆形元素设置不同的
animation-delay,实现依次出现的连贯效果,你可以根据需求调整延迟时间。
这个方案完美匹配你的需求:鼠标悬停任意<li>时,该<li>内的四个圆形元素依次启动动画;切换到其他<li>时,之前的动画会立刻停止并重置,新的动画随即启动。
内容的提问来源于stack exchange,提问作者user3404273
相关产品推荐
相关产品推荐

