reactjsframerjsframer-motion

Framer: Check if element is into viewport


While using Framer Motion API to create interaction and animations on my site, I can not find how to use it in order to trigger an animation when something is on the screen.

For example, this SVG draws correctly, but Framer does not wait for the element to be on the viewport and triggers it right after loading site:

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

Does Framer have a viewport detection triggerer to be implemented here?


Solution

  • Alternatively, you can use Intersection Observer, blends pretty well with React and 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}}
            />
          );
    }
    

    You can also refine your animation by looking at entry object (entering from top or bottom, etc)