【问题标题】:How to animate multiple framer-motion elements once in viewport如何在视口中为多个帧运动元素设置动画
【发布时间】:2021-05-08 09:25:26
【问题描述】:

我正在使用 framer-motion 为一页中的多个元素设置动画。由于 framer-motion 没有一种简单的方法来为元素在视口中设置动画,因此我使用这种方法:

const controls = useAnimation();
const { ref, inView } = useInView();

useEffect(() => {
    if (inView) {
      controls.start("visible");
    }
    if (!inView) {
      controls.start("hidden");
    }
  }, [controls, inView]);

const fadeFromBottom = {
    hidden: {
      opacity: 0,
      y: -5,
    },
    visible: {
      opacity: 1,
      y: 0,
      transition: {
        type: "spring",
        delay: 0.4,
      },
    },
  };

return (
<motion.section
  ref={ref}
  variants={fadeFromBottom}
  initial='hidden'
  animate={controls}
>
   <img src={image} alt='image'>
</motion.section>

但是,这种方法只允许我为每个 .jsx 文件的一个元素设置动画。如果我想在两个部分进入视口时使用不同的动画制作动画,我该怎么做?例如,我如何在不同的时间使用不同的动画为这两个部分制作动画?

<motion.section
  ref={ref}
  variants={fadeFromBottom}
  initial='hidden'
  animate={controls}
>
   <img src={image} alt='image'>
</motion.section>
<motion.section
  ref={ref}
  variants={fadeFromLeft}
  initial='hidden'
  animate={controls}
>
   <img src={image} alt='image'>
</motion.section>

【问题讨论】:

    标签: reactjs animation react-hooks framer-motion react-animations


    【解决方案1】:

    您可以创建一个挂钩,将动画组件的逻辑包装在“视图中”

    const useAnimateOnInView = () => {
        const controls = useAnimation();
        const { ref, inView } = useInView();
        
        useEffect(() => {
            if (inView) {
              controls.start("visible");
            }
            if (!inView) {
              controls.start("hidden");
            }
          }, [controls, inView]);
        
    
         return { ref };
    }
    

    使用钩子来制作你想要动画的所有东西

    const { ref: bananaRef } = useAnimateOnInView();
    const { ref: appleRef } = useAnimateOnInView();
    

    然后将 refs 连接到相关的 dom 元素。

    <motion.section
      ref={bananaRef}
      variants={fadeFromBottom}
      initial='hidden'
      animate={controls}
    >
       <img src={image} alt='banana'>
    </motion.section>
    <motion.section
      ref={appleRef}
      variants={fadeFromLeft}
      initial='hidden'
      animate={controls}
    >
       <img src={image} alt='apple'>
    </motion.section>
    

    你也可以复制useInView钩子的现有用法,并为useEffect添加一些逻辑。我认为这个钩子虽然有点清理它。

    【讨论】:

      【解决方案2】:

      我遇到了这个问题,这就是我为其他偶然发现这个问题的人解决它的方法。

      我们仍然可以使用react-intersection-observer expect 代替导入 useInView() 钩子,我们可以导入 InView 组件

      import { InView } from "react-intersection-observer";
      

      然后我们可以用该组件包装每个元素

      <InView as="div">
              <Image
                  src={designPic}
                  alt=""
                  quality="90"
                  loading="lazy"
                  objectFit="cover"
                  width={718}
                  height={649}
                  placeholder="blur"
              />
      </InView>
      

      然后我们可以为每个元素创建一个useAnimation钩子控件。

      const imageOneControls = useAnimation();
      const imageTwoControls = useAnimation();
      const imageThreeControls = useAnimation();
      

      然后在组件上使用 onChange 引用每个元素中的控件。

      onChange={(inView) => {
          if (inView) imageOneControls.start("show");
          }}
      

      我不确定这是否是最佳解决方案,但这是我在处理此问题时唯一能想到的解决方案。包裹在组件中的最终元素看起来像这样:

      <InView 
          as="div"
          onChange={(inView) => {
          if (inView) imageOneControls.start("show");
          }}
          >
              <Image
                  src={designPic}
                  alt=""
                  quality="90"
                  loading="lazy"
                  objectFit="cover"
                  width={718}
                  height={649}
                  placeholder="blur"
              />
      </InView>
      

      【讨论】:

        猜你喜欢
        • 2014-10-13
        • 2015-05-28
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-08-08
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多