React:如何解决Framer Motion与Intersection Observer单图滚动动画异常?
问题解决思路
核心问题分析
- 把
ref绑定在容器上时,所有motion.div共用同一个animation控制器,容器进入视图后所有卡片会同步触发动画。 - 在
map循环中直接使用同一个useInView的ref,由于循环变量的覆盖问题,最终只有最后一个卡片会被Intersection Observer监听;而前面的卡片因为初始动画状态是x: '-100vw'且未触发inView,会一直处于屏幕外不可见。
解决方案:抽离独立卡片组件
给每个博客卡片单独创建useInView和useAnimation实例,避免状态共享导致的冲突。具体步骤如下:
- 创建独立的博客卡片组件
import { useInView } from 'react-intersection-observer'; import { motion, useAnimation } from 'framer-motion'; const BlogCardItem = ({ post, urlFor }) => { const { ref, inView } = useInView({ threshold: 0.05 }); const animation = useAnimation(); // 单独控制当前卡片的动画 if (inView) { animation.start({ x: 0, transition: { type: 'spring', duration: 1, bounce: 0.5 } }); } if (!inView) { animation.start({ x: '-100vw' }); } return ( <BlogCardsContainer ref={ref}> <motion.div animate={animation}> <BlogLink to={`/${post.slug.current}`}> <BlogCards src={urlFor(post.mainImage.asset.url).url()} alt={post.title} /> <BlogCardTitle>{post.title}</BlogCardTitle> </BlogLink> </motion.div> </BlogCardsContainer> ); }; export default BlogCardItem;
- 在主组件中渲染子组件
修改主组件的返回逻辑,循环渲染独立的BlogCardItem:
return ( <> {isLoading ? ( <LoaderContainer> <RingLoader /> </LoaderContainer> ) : ( <GalleryContainer> {allPostsData && allPostsData.map((post) => ( <BlogCardItem key={post.slug.current} post={post} urlFor={urlFor} /> ))} </GalleryContainer> )} </> );
额外优化建议
- 如果不需要卡片离开视图时回退动画,可以去掉
!inView的逻辑,避免滚动时卡片反复移出视野。 - 调整
threshold值(比如设为0.2),让卡片进入视野一部分时就触发动画,提升交互体验。
内容的提问来源于stack exchange,提问作者lss555
相关产品推荐
相关产品推荐

