reactjs 如何在视口中一次性设置多个帧运动元素的动画

guz6ccqo  于 2023-04-11  发布在  React
关注(0)|答案(3)|浏览(122)

我使用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>
xhv8bpkk

xhv8bpkk1#

您可以创建一个钩子,将动画组件的逻辑 Package 在“in view”上

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();

然后将引用连接到相关的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中添加一些逻辑。

mnemlml8

mnemlml82#

我遇到了这个问题,这是我如何解决它的任何人谁在这个绊倒。
我们仍然可以使用react-intersection-observer,而不是导入useInView()钩子,我们可以导入InView组件

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

然后我们可以用那个组件 Package 每个元素

<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>
0yg35tkg

0yg35tkg3#

我不知道这是否已经被添加到帧运动最近,但这是我是如何做到这一点很容易:只需要添加你想应用到whileInView中的元素的动画,而不是像这样的animate

<motion.div
    initial={{ opacity: 0 }}
    whileInView={{ opacity: 1 }}
    viewport={{ once: true }}
>
    Your Content
</motion.div>

这个viewport={{ once: true }}告诉framer motion,你希望这个动画只发生一次,而不是每次元素离开和进入视图时。

相关问题