解决mouseover/mouseleave触发多步动画的二次hover卡顿问题
问题描述
我编写了如下代码,实现了由mouseover和mouseleave事件触发的多步动画效果:
HTML代码
<body> <a class="one link" href="#a-propos"> <span>about</span> <div class="up"></div> <div class="down"></div> </a> </body>
CSS代码
body{ display: flex; justify-content: center; align-items: center; min-height: 100vh; } .notransition{ transition: none !important; } a{ color: black; text-decoration: none; display: block; position: relative; } a:hover{ color: white; } span{ display: block; width: 100%; height: 100%; z-index: 1; position: relative; transition: all 0.3s ease-in-out; } .up{ display: block; width: 0; height: calc(50% + 10px); border-radius: 0 10px 0 0; position: absolute; background-color: blue; transition: width 0.5s cubic-bezier(0.165, 0.84, 0.44, 1); transform-origin: left; top: -10px; left: -15px; opacity: 0; } .down{ display: block; width: 0; height: calc(50% + 10px); border-radius: 0 0 0 10px; position: absolute; background-color: red; transition: width 0.5s cubic-bezier(0.165, 0.84, 0.44, 1); transform-origin: right; bottom: -10px; right: -15px; opacity: 0; } .hovered .up{ width: calc(100% + 20px); opacity: 1; } .hovered.leaved{ pointer-events: none; } .hovered.leaved .up{ opacity: 0; top: -30px; transition: opacity 0.5s cubic-bezier(0.165, 0.84, 0.44, 1), top 0.5s cubic-bezier(0.165, 0.84, 0.44, 1), height 0.5s cubic-bezier(0.165, 0.84, 0.44, 1); } .hovered .down{ width: calc(100% + 20px); opacity: 1; } .hovered.leaved .down{ opacity: 0; bottom: -30px; transition: opacity 0.5s cubic-bezier(0.165, 0.84, 0.44, 1), top 0.5s cubic-bezier(0.165, 0.84, 0.44, 1), height 0.5s cubic-bezier(0.165, 0.84, 0.44, 1); }
JavaScript代码
jQuery(document).ready(function($) { $('.link').on('mouseover', function() { $(this).addClass('hovered'); }); $('.link').on('mouseleave', function() { $(this).addClass('leaved'); setTimeout(() => { $(this).removeClass('hovered leaved'); }, 500); setTimeout(() => { $(this).removeClass('notransition'); }, 600); }); });
当前存在的问题:当mouseleave动画尚未完成时再次触发mouseover,过渡效果会不流畅。请问如何优化该代码,实现流畅的动画切换?
优化方案
核心思路
在中断退出动画时,先让元素同步到当前动画的实时状态,再无缝启动进入动画,避免状态突变导致的卡顿;同时清理残留的异步操作,确保状态切换的原子性。
具体调整
1. JavaScript逻辑优化
jQuery(document).ready(function($) { $('.link').each(function() { // 为每个元素独立存储定时器,避免互相干扰 let timer1, timer2; $(this).on('mouseover', function() { // 清除所有残留的定时器,避免旧的异步操作干扰新状态 clearTimeout(timer1); clearTimeout(timer2); const $el = $(this); // 中断退出动画,移除leaved类 $el.removeClass('leaved'); // 添加notransition类,强制同步当前动画状态(无过渡) $el.addClass('notransition'); // 强制触发重绘,确保状态生效 void $el[0].offsetWidth; // 移除notransition,恢复过渡能力 $el.removeClass('notransition'); // 启动进入动画 $el.addClass('hovered'); }); $(this).on('mouseleave', function() { const $el = $(this); // 清除残留定时器 clearTimeout(timer1); clearTimeout(timer2); // 启动退出动画 $el.addClass('leaved'); timer1 = setTimeout(() => { $el.removeClass('hovered leaved'); }, 500); timer2 = setTimeout(() => { $el.removeClass('notransition'); }, 600); }); }); });
2. CSS细节修复
修正.hovered.leaved .down中的错误属性(把无效的top改为bottom),确保过渡逻辑正确:
.hovered.leaved .down{ opacity: 0; bottom: -30px; transition: opacity 0.5s cubic-bezier(0.165, 0.84, 0.44, 1), bottom 0.5s cubic-bezier(0.165, 0.84, 0.44, 1), height 0.5s cubic-bezier(0.165, 0.84, 0.44, 1); }
优化原理
- 清理定时器:避免之前的
mouseleave异步操作在动画中断后仍修改元素状态,导致状态冲突 - 强制同步状态:通过
notransition类+强制重绘,让元素直接跳转到退出动画的当前进度,再无缝启动进入动画,消除卡顿感 - 修正CSS错误:移除无效的过渡属性,确保动画逻辑的一致性
内容的提问来源于stack exchange,提问作者Cédric Teixeira Alves
相关产品推荐
相关产品推荐

