如何实现react-spring动画仅在组件进入用户可视视口时触发
解决方法
问题根源
你当前的控制逻辑失效,核心原因是useTransition会在组件首次渲染时就基于传入的items初始化动画序列,即使你通过appear变量控制DOM渲染,动画的时间线已经在页面加载时就启动了,并不会等到DOM渲染时再重新触发。
具体实现步骤
1. 用Intersection Observer监听组件是否进入视口
这是浏览器原生提供的高性能滚动监听API,不需要自己绑定scroll事件计算位置,兼容性覆盖绝大多数现代浏览器。
2. 调整react-spring的触发逻辑
仅当组件进入视口时,才给useTransition传入完整的渲染列表,让动画在此时才开始初始化执行。
修改后完整代码
import { useRef, useEffect, useState } from 'react'; import { useTransition, animated, config } from 'react-spring'; // 你的Container、Row、Col组件按原有逻辑正常引入即可 const cols = [ /*Components here that will be animated ..*/ {component: <div><p>A</p></div> , key:1}, {component: <div><p>B</p></div> , key:2}, {component: <div><p>C</p></div> , key:3}, ] export default function Foocomponent(){ // 给容器绑定ref,用于监听可见性 const containerRef = useRef(null); const [appear, setAppear] = useState(false); // 初始状态下items为空,不会触发动画 const [items, setItems] = useState([]); const transitions = useTransition(items, (item) => item.key, { from: { opacity: 0, transform: 'translateY(70px) scale(0.5)', borderRadius: '0px' }, enter: { opacity: 1, transform: 'translateY(0px) scale(1)', borderRadius: '20px', border: '1px solid #00b8d8' }, delay: 200, config: config.molasses, }) // 监听容器可见性 useEffect(() => { const observer = new IntersectionObserver( (entries) => { const [entry] = entries; if (entry.isIntersecting && !appear) { setAppear(true); // 组件进入视口后,再设置要渲染的列表,触发动画 setItems(cols); // 如果不需要重复触发,此时可以取消监听节约性能 observer.unobserve(entry.target); } }, { threshold: 0.1 } // 10%的内容进入视口就触发,可自行调整阈值 ); if (containerRef.current) { observer.observe(containerRef.current); } return () => { if (containerRef.current) observer.unobserve(containerRef.current); }; }, [appear]); return ( <Container ref={containerRef}> <Row> {transitions.map(({ item, props, key }) => ( <Col className="feature-item"> <animated.div key={key} style={props} > {item.component} </animated.div> </Col> ))} </Row> </Container> ); }
额外说明
如果需要组件每次进入视口都重新触发动画,可以去掉observer.unobserve的逻辑,同时在组件离开视口时把items设为空数组即可。
内容的提问来源于stack exchange,提问作者John C.
相关产品推荐
相关产品推荐

