Framer:检查元素是否进入视口

Framer: Check if element is into viewport

在我的网站上使用 Framer Motion API 创建交互和动画时,我找不到如何使用它来在屏幕上显示某些内容时触发动画。

例如,此 SVG 绘制正确,但 Framer 不等待元素出现在视口上并在加载站点后立即触发它:

import React, { Component } from 'react'
import { motion } from "framer-motion";

class IsometricScreen extends Component {

    constructor() {
        super()
        this.icon = {
            hidden: { pathLength: 0 },
            visible: { pathLength: 1 }
        }
    }

    render() {
        return (
            <motion.svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 500 1000" className="svg-mobile">
                <motion.path
                    d="M418,988.93H82c-39.76,0-72-32.24-72-72V83.07c0-39.76,32.24-72,72-72h336c39.76,0,72,32.24,72,72v833.86
                    C490,956.69,457.76,988.93,418,988.93z"
                    variants={this.icon}
                    initial="hidden"
                    animate="visible"
                    transition={{
                        default: { duration: 2, ease: "easeInOut" }
                    }}
                />
            </motion.svg>
        )
    }
}

export default IsometricScreen

Framer 是否有视口检测触发器可以在这里实现?

我终于用一个微小的功能组件解决了这个问题:

function inViewport() {

    const isInViewport = el => {
        const rect = el.getBoundingClientRect()
        const vertInView = (rect.top <= window.innerHeight) && ((rect.top + rect.height) >= 0)
        const horInView = (rect.left <= window.innerWidth) && ((rect.left + rect.width) >= 0)
        return (vertInView && horInView)
    }

    this.elms = document.querySelectorAll('.showOnScreen')

    window.addEventListener("scroll", () => {
        this.elms.forEach(elm => isInViewport(elm) ? elm.classList.add('visible') : elm.classList.remove('visible'))
    })
}

export default inViewport

或者,您可以使用 Intersection Observer,与 React 和 framer motion 很好地融合。

import { useInView } from "react-intersection-observer"; // 1.9K gzipped
import { motion, useAnimation } from "framer-motion";

const Component = () => {
    const animation = useAnimation();    
    const [ref, inView, entry] = useInView({ threshold: 0.1 });

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

    const variants = {
        visible: {
          y: 0,
          opacity: 1,
          transition: { duration: 0.5, delayChilden: 0.2, staggerChildren: 0.1 },
        },
        hidden: {
          y: enter,
          opacity: 0,
        },
    }

    return (
        <motion.div
          ref={ref}
          animate={animation}
          initial="hidden"
          variants={{variants}}
        />
      );
}

您还可以通过查看入口对象(从顶部或底部进入等)来优化动画

framer-motion 自版本 5.3 起内置了对此用例的支持。

这是一个演示模式的 CodeSandbox:https://codesandbox.io/s/framer-motion-animate-in-view-5-3-94j13

相关代码:

function FadeInWhenVisible({ children }) {
  return (
    <motion.div
      initial="hidden"
      whileInView="visible"
      viewport={{ once: true }}
      transition={{ duration: 0.3 }}
      variants={{
        visible: { opacity: 1, scale: 1 },
        hidden: { opacity: 0, scale: 0 }
      }}
    >
      {children}
    </motion.div>
  );
}

用法:

<FadeInWhenVisible>
  <Box />
</FadeInWhenVisible>