React滚动位置触发事件失效求助:仅页面刷新时生效
解决滚动到指定位置触发事件无响应的问题
你的代码核心问题在于:useEffect的依赖数组为空,只会在组件首次挂载时执行一次,仅能在刷新页面时检查一次滚动位置;而滚动过程中不会重新执行位置判断逻辑,自然无法触发事件。以下是两种可行的解决方案:
方案一:滚动监听+状态标记
通过添加滚动监听函数,在滚动过程中持续检查元素位置,同时用useRef标记动画是否已触发,避免重复执行:
import { useEffect, useRef } from 'react'; // 组件内的useEffect代码 useEffect(() => { AOS.init(); const cnt = document.querySelectorAll(".count")[props.num]; const water = document.querySelectorAll(".water")[props.num]; const Skills = document.querySelector('#skills'); // 标记动画是否已执行,防止重复触发 const animationTriggered = useRef(false); const handleScroll = () => { // 计算元素相对于文档顶部的位置 const elementTop = Skills.offsetTop; // 滚动位置+视口高度,判断元素是否进入视口(可根据需求调整触发条件) const currentScroll = window.scrollY + window.innerHeight; if (currentScroll >= elementTop && !animationTriggered.current) { animationTriggered.current = true; let percent = 0; // 从0开始计数,避免初始innerText非数字的问题 const interval = setInterval(() => { percent++; cnt.textContent = percent; water.style.transform = `translate(0, ${100 - percent}%)`; if (percent === props.percent) { clearInterval(interval); } }, 60); } }; // 首次挂载时先检查一次(防止页面加载时元素已在视口内) handleScroll(); // 添加滚动监听 window.addEventListener('scroll', handleScroll); // 清理函数:移除监听,避免内存泄漏 return () => { window.removeEventListener('scroll', handleScroll); }; }, [props.num, props.percent]); // 依赖props变量,确保变化时重新初始化
关键优化点:
- 新增
scroll事件监听,滚动过程中持续触发位置检查 - 用
useRef存储触发状态,避免滚动时重复启动定时器 - 修正元素位置计算逻辑,使用
offsetTop获取稳定的文档相对位置 - 在组件卸载时移除监听,防止内存泄漏
- 依赖数组添加
props.num和props.percent,确保props变化时重新执行逻辑
方案二:IntersectionObserver(更优性能)
使用浏览器原生的IntersectionObserverAPI,专门监听元素是否进入视口,性能比滚动监听更高效:
import { useEffect, useRef } from 'react'; // 组件内的useEffect代码 useEffect(() => { AOS.init(); const cnt = document.querySelectorAll(".count")[props.num]; const water = document.querySelectorAll(".water")[props.num]; const Skills = document.querySelector('#skills'); const animationTriggered = useRef(false); // 创建观察者实例 const observer = new IntersectionObserver((entries) => { entries.forEach(entry => { // 元素进入视口且未触发过动画 if (entry.isIntersecting && !animationTriggered.current) { animationTriggered.current = true; let percent = 0; const interval = setInterval(() => { percent++; cnt.textContent = percent; water.style.transform = `translate(0, ${100 - percent}%)`; if (percent === props.percent) { clearInterval(interval); } }, 60); // 触发后停止观察,节省性能 observer.unobserve(entry.target); } }); }, { threshold: 0.1 // 元素进入视口10%时触发,可自行调整比例 }); if (Skills) observer.observe(Skills); // 清理函数:停止观察 return () => { if (Skills) observer.unobserve(Skills); }; }, [props.num, props.percent]);
优势:
- 无需持续监听滚动事件,由浏览器原生处理元素可见性判断,性能更优
- 可灵活配置触发条件(如元素进入视口的比例)
- 触发后可主动停止观察,减少不必要的资源消耗
内容的提问来源于stack exchange,提问作者jym
相关产品推荐
相关产品推荐

