React自定义onScroll滚动动画异常:仅首个section触发时机正常
问题原因与解决方案
问题根源
你当前的触发条件sectionTop < triggerPoint存在逻辑缺陷:section.getBoundingClientRect().top是元素顶部到视口顶部的距离,对于页面下方的section来说,它们的初始sectionTop等于前面所有section的高度总和,只有当滚动距离足够大,让这个值小于triggerPoint时才会触发动画。这就导致后面的section需要滚动更多距离(甚至过半)才能满足条件,而第一个后续section(Clients)因为紧跟Home,滚动少量距离就能满足sectionTop < triggerPoint,所以看起来“按时触发”。
另外,你只在组件挂载时计算一次triggerPoint,如果窗口大小变化,这个值不会更新,也会导致触发时机偏差。
解决方案
方案1:修复Scroll事件逻辑
调整触发条件,确保section进入指定视口区域时就触发,同时添加窗口大小变化监听:
import './App.css'; import Home from './components/Sections/Home'; import Clients from './components/Sections/Clients'; import AboutUs from './components/Sections/AboutUs'; import Career from './components/Sections/Career'; import Discover from './components/Sections/Discover'; import OurWork from './components/Sections/OurWork'; import Testimonial from './components/Sections/Testimonial'; import Plan from './components/Sections/Plan'; import Footer from './components/Sections/Footer'; import { useEffect } from 'react'; function App() { useEffect(() => { let triggerPoint = window.innerHeight / 5 * 4; const triggerElement = document.querySelector(".trigger"); triggerElement.style.top = triggerPoint + "px"; const handleScroll = () => checkSections(triggerPoint); const handleResize = () => { triggerPoint = window.innerHeight / 5 * 4; triggerElement.style.top = triggerPoint + "px"; checkSections(triggerPoint); }; window.addEventListener("scroll", handleScroll); window.addEventListener("resize", handleResize); checkSections(triggerPoint); return () => { window.removeEventListener("scroll", handleScroll); window.removeEventListener("resize", handleResize); }; }, []); function checkSections(triggerPoint){ const sections = document.querySelectorAll("section"); sections.forEach(section => { const rect = section.getBoundingClientRect(); // 当section顶部到达triggerPoint位置,且未完全滚出视口时触发 const shouldTrigger = rect.top <= triggerPoint && rect.bottom > 0; if(shouldTrigger){ section.classList.add("on-scroll"); }else{ section.classList.remove("on-scroll") } }) } return ( <div id="App" style={{position: "relative"}}> <div className='trigger' style={{position: "fixed", left: "100px", background: "white", zIndex: "99", width: "50px", height: "50px"}}></div> <Home /> <Clients /> <AboutUs /> <Career /> <Discover /> <OurWork /> <Testimonial /> <Plan /> <Footer /> </div> ); } export default App;
方案2:改用Intersection Observer API(推荐)
这是现代浏览器提供的更高效的元素可见性监听方案,无需手动处理scroll事件,逻辑更简洁:
import './App.css'; import Home from './components/Sections/Home'; import Clients from './components/Sections/Clients'; import AboutUs from './components/Sections/AboutUs'; import Career from './components/Sections/Career'; import Discover from './components/Sections/Discover'; import OurWork from './components/Sections/OurWork'; import Testimonial from './components/Sections/Testimonial'; import Plan from './components/Sections/Plan'; import Footer from './components/Sections/Footer'; import { useEffect } from 'react'; function App() { useEffect(() => { let triggerPoint = window.innerHeight / 5 * 4; const triggerElement = document.querySelector(".trigger"); triggerElement.style.top = triggerPoint + "px"; // 创建观察者,设置触发时机为section顶部到达triggerPoint位置 const observer = new IntersectionObserver( (entries) => { entries.forEach(entry => { entry.target.classList.toggle("on-scroll", entry.isIntersecting); }); }, { rootMargin: `-${window.innerHeight - triggerPoint}px 0 0 0`, threshold: 0 } ); // 监听所有section const sections = document.querySelectorAll("section"); sections.forEach(section => observer.observe(section)); // 窗口大小变化时更新触发点 const handleResize = () => { triggerPoint = window.innerHeight / 5 * 4; triggerElement.style.top = triggerPoint + "px"; // 重新配置观察者 observer.disconnect(); observer.rootMargin = `-${window.innerHeight - triggerPoint}px 0 0 0`; sections.forEach(section => observer.observe(section)); }; window.addEventListener("resize", handleResize); return () => { observer.disconnect(); window.removeEventListener("resize", handleResize); }; }, []); return ( <div id="App" style={{position: "relative"}}> <div className='trigger' style={{position: "fixed", left: "100px", background: "white", zIndex: "99", width: "50px", height: "50px"}}></div> <Home /> <Clients /> <AboutUs /> <Career /> <Discover /> <OurWork /> <Testimonial /> <Plan /> <Footer /> </div> ); } export default App;
内容的提问来源于stack exchange,提问作者Zohaib Mushtaq
相关产品推荐
相关产品推荐

