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; }
修改说明
- 移除CSS动画:删除了
alert-list中的animation属性,避免和JS滚动逻辑冲突 - 无缝滚动优化:复制一份列表内容,滚动到复制区域时瞬间跳回原列表顶部,实现无感知循环
- 简化状态:去掉冗余的
shouldScroll状态,仅用isHovering控制滚动启停 - 流畅度调整:将滚动间隔改为16ms(约60帧/秒),提升滚动流畅度
- 固定容器高度:给
alerts-container设置固定高度,确保滚动容器的可视区域正常计算
内容的提问来源于stack exchange,提问作者Harish Mahi
相关产品推荐
相关产品推荐

