快速悬停时列表项齿轮图标hover效果异常问题求助
解决Framer Motion快速悬停后齿轮图标残留的UI异常
我有一组列表项,每个项悬停时会显示齿轮图标,但快速悬停齿轮图标区域时,部分图标会在光标移开后残留。当前使用Framer Motion的onHoverStart和onHoverEnd控制hover状态,基于Framer Motion 7.8.1和DaisyUI 4.0.7的核心代码如下:
const [hover, setHover] = useState(false); return <> {hover && <details key={id} className="dropdown dropdown-end dropdown-top"> <summary className="list-none block"> <GearIcon /> </summary> <div className="dropdown-content menu"> ... </div> </details> } </>
注意:仅快速悬停齿轮图标区域时出现异常,悬停左侧其他区域无问题。
方案1:改用Framer Motion内置的whileHover管理状态
手动通过onHoverStart/onHoverEnd控制useState,很容易因为快速操作导致事件触发不同步。改用Framer Motion内置的whileHover可以更可靠地处理hover状态的生命周期:
import { motion } from "framer-motion"; return ( <motion.div initial={{ opacity: 0 }} whileHover={{ opacity: 1 }} style={{ opacity: 0 }} // 默认隐藏图标容器 > <details key={id} className="dropdown dropdown-end dropdown-top"> <summary className="list-none block"> <GearIcon /> </summary> <div className="dropdown-content menu"> ... </div> </details> </motion.div> );
Framer Motion会自动处理hover的开始和结束逻辑,避免手动状态更新的异步冲突。
方案2:优化手动状态管理,确保onHoverEnd触发
如果必须保留手动状态管理,可以通过防抖或全局监听来强制重置状态:
方法A:添加防抖处理
import { useState, useCallback } from "react"; import { motion } from "framer-motion"; // 自定义防抖函数 const debounce = (func, delay) => { let timeoutId; return (...args) => { clearTimeout(timeoutId); timeoutId = setTimeout(() => func(...args), delay); }; }; const ListItem = ({ id }) => { const [hover, setHover] = useState(false); const debouncedResetHover = useCallback( debounce(() => setHover(false), 100), [] ); return ( <motion.div onHoverStart={() => { setHover(true); debouncedResetHover.cancel(); // 悬停开始时取消之前的防抖任务 }} onHoverEnd={debouncedResetHover} > {hover && <details key={id} className="dropdown dropdown-end dropdown-top"> <summary className="list-none block"> <GearIcon /> </summary> <div className="dropdown-content menu"> ... </div> </details> } {/* 列表项其他内容 */} </motion.div> ); };
方法B:监听全局鼠标离开事件
import { useEffect, useState } from "react"; import { motion } from "framer-motion"; const ListItem = ({ id }) => { const [hover, setHover] = useState(false); // 鼠标离开窗口时强制重置hover状态 useEffect(() => { const handleMouseLeave = () => setHover(false); window.addEventListener("mouseleave", handleMouseLeave); return () => window.removeEventListener("mouseleave", handleMouseLeave); }, []); return ( <motion.div onHoverStart={() => setHover(true)} onHoverEnd={() => setHover(false)} > {/* 同前的内容 */} </motion.div> ); };
方案3:规避details元素的默认行为干扰
details标签本身有展开/收起的默认交互,可能和hover状态冲突。可以替换成普通div+button结构:
{hover && <div className="dropdown dropdown-end dropdown-top"> <button className="list-none block"> <GearIcon /> </button> <div className="dropdown-content menu"> ... </div> </div> }
用按钮替代summary,避免details的原生行为影响hover状态的正常切换。
内容的提问来源于stack exchange,提问作者PipEvangelist
相关产品推荐
相关产品推荐

