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

React实现鼠标悬停暂停滚动、离开恢复滚动的问题求助

React无限滚动列表悬停暂停问题修复

问题说明

我在React页面实现了从div底部到顶部的无限滚动列表,目前列表能循环滚动,但鼠标悬停时无法暂停,离开后也无法恢复滚动。需要实现鼠标悬停停在当前位置,离开后继续滚动的效果。

问题根源

当前代码同时使用了CSS动画和JS的requestAnimationFrame控制滚动,两者逻辑冲突导致悬停失效;另外滚动到顶部时直接跳转的体验生硬,且状态管理冗余。

解决方案及修改代码

关键修改点

  • 移除CSS中的滚动动画,避免和JS逻辑冲突
  • 实现无缝滚动:复制列表内容,滚动到复制区域时瞬间跳回原列表顶部,消除生硬跳转
  • 简化状态管理:仅用isHovering控制滚动开关
  • 优化动画帧管理,确保组件卸载时清理资源

修改后的React代码

import React, { useEffect, useState, useRef } from 'react';
import "../css/dashBoard.css";

const ScrollChart = () => {
  const [data, setData] = useState([]);
  const alertListRef = useRef(null);
  const scrollSpeed = 1;
  const [isHovering, setIsHovering] = useState(false);
  let animationFrameId = null;
  let lastTimestamp = 0;

  useEffect(() => {
    const fetchData = async () => {
      try {
        const jsonStr = '[{"item0":10},{"item1":11},{"item2":12},{"item3":13},{"item4":14},{"item5":15},{"item6":16},{"item7":17},{"item8":18},{"item9":19}]';
        setData(JSON.parse(jsonStr));
      } catch (error) {
        console.log("Error-- " + error);
      }
    };
    fetchData();
  }, []);

  useEffect(() => {
    const scrollAlerts = (timestamp) => {
      if (!isHovering && alertListRef.current) {
        const scrollContainer = alertListRef.current;
        const scrollHeight = scrollContainer.scrollHeight;
        let scrollTop = scrollContainer.scrollTop;

        // 无缝滚动:滚动到复制内容区域时,跳回原列表顶部
        if (scrollTop >= scrollHeight / 2) {
          scrollContainer.scrollTop = 0;
          scrollTop = 0;
        }

        // 约60帧/秒的滚动频率,保证流畅度
        if (timestamp - lastTimestamp >= 16) {
          scrollContainer.scrollTop += scrollSpeed;
          lastTimestamp = timestamp;
        }
      }
      animationFrameId = requestAnimationFrame(scrollAlerts);
    };

    animationFrameId = requestAnimationFrame(scrollAlerts);

    return () => {
      cancelAnimationFrame(animationFrameId);
    };
  }, [isHovering]);

  const handleMouseEnter = () => {
    setIsHovering(true);
  };

  const handleMouseLeave = () => {
    setIsHovering(false);
  };

  return (
    <div className="dashboard_alertBarChart" >
      <div className="alerts-header">Alerts</div>
      <div className="alerts-container" onMouseEnter={handleMouseEnter} onMouseLeave={handleMouseLeave}>
        <div className="alert-list" ref={alertListRef}>
          {/* 渲染原列表 */}
          {data.map((alert, index) => {
            const severity = alert.severity || 'medium';
            return (
              <div key={index} className="alert-item">
                <div className={`legend-box ${severity}`}></div>
                {Object.keys(alert)[0]}: {Object.values(alert)[0]}
              </div>
            );
          })}
          {/* 复制列表实现无缝滚动 */}
          {data.map((alert, index) => {
            const severity = alert.severity || 'medium';
            return (
              <div key={`copy-${index}`} className="alert-item">
                <div className={`legend-box ${severity}`}></div>
                {Object.keys(alert)[0]}: {Object.values(alert)[0]}
              </div>
            );
          })}
        </div>
      </div>
    </div>
  );
};

export default ScrollChart;

修改后的CSS代码

.alerts-header {
  text-align: center;
  font-weight: bold;
  margin-bottom: 10px;
}
.alerts-container {
  position: relative;
  height: 300px; /* 固定高度,确保滚动容器有明确可视区域 */
  overflow: hidden;
}

.alert-list {
  overflow-y: auto;
  scrollbar-width: thin;
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
}

.alert-item {
  font-weight: bold;
  padding-bottom: 10px;
  color: #09094a;
}

.legend-box {
  width: 14px;
  height: 14px;
  display: inline-block;
  margin-right: 5px;
}

.high {
  background-color: red;
}

.medium {
  background-color: yellow;
}

.low {
  background-color: green;
}

.dashboard_alertBarChart {
  width: 45%;
  background-color: #1eeeee1a;
  border-radius: 10px;
  margin-top: 1%;
  position: relative;
  overflow: hidden;
  padding: 10px;
}

修改说明

  1. 移除CSS动画:删除了alert-list中的animation属性,避免和JS滚动逻辑冲突
  2. 无缝滚动优化:复制一份列表内容,滚动到复制区域时瞬间跳回原列表顶部,实现无感知循环
  3. 简化状态:去掉冗余的shouldScroll状态,仅用isHovering控制滚动启停
  4. 流畅度调整:将滚动间隔改为16ms(约60帧/秒),提升滚动流畅度
  5. 固定容器高度:给alerts-container设置固定高度,确保滚动容器的可视区域正常计算

内容的提问来源于stack exchange,提问作者Harish Mahi

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.16 09:48:06