You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

快速悬停时列表项齿轮图标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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.05 15:52:51