You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React:如何解决Framer Motion与Intersection Observer单图滚动动画异常?

问题解决思路

核心问题分析

  • 把ref绑定在容器上时,所有motion.div共用同一个animation控制器,容器进入视图后所有卡片会同步触发动画。
  • 在map循环中直接使用同一个useInView的ref,由于循环变量的覆盖问题,最终只有最后一个卡片会被Intersection Observer监听;而前面的卡片因为初始动画状态是x: '-100vw'且未触发inView,会一直处于屏幕外不可见。

解决方案:抽离独立卡片组件

给每个博客卡片单独创建useInView和useAnimation实例,避免状态共享导致的冲突。具体步骤如下:

  1. 创建独立的博客卡片组件
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;
  1. 在主组件中渲染子组件
    修改主组件的返回逻辑,循环渲染独立的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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.16 10:20:36