1

我正在使用 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>
4

2 回答 2

2

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

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 添加一些逻辑。我认为这个钩子虽然有点清理它。

于 2021-05-10T02:37:23.160 回答
0

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

我们仍然可以使用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>
于 2021-09-15T00:02:45.960 回答