DEV Community

loading...

React Parallax effect with Framer-Motion

jesuscovam profile image jesuscovam ・3 min read

Let's recreate the example from https://usehooks.com/useOnScreen/ but adding framer-motion for a parallax effect

The first thing will be installing the packages. For simplicity, I'm going to use the react-intersection-observer, that uses the IntersectionObserver API.

install

npm i framer-motion react-intersection-observer

The first look at our component will be 2 divs with a hight of 100vh each, and any background you like to make a differentiation.

export default function Home() {

  return (
    <div>
      <div style={{ height: "100vh" }}>
        <h1>Scroll down to next section πŸ‘‡</h1>
      </div>
      <div style={{ height: "100vh", backgroundColor: "#23cebd" }}></div>
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

Let's add the div with an image that we'd like to see move when we scroll it into the viewport.

//* everything until here is the same *//
<div style={{ height: "100vh", backgroundColor: "#23cebd" }}>
  <div style={{ marginLeft: "50px" }}>
    <h1>Hey I'm on the screen</h1>
    <img style={{ borderRadius: "30px" }}
      alt="ralph from the simpsons waving his hand"
      src="https://i.giphy.com/media/ASd0Ukj0y3qMM/giphy.gif"
    />
  </div>
</div>
Enter fullscreen mode Exit fullscreen mode

For the moment is a static image, we want it to appear when as a DOM element enters in the screen. For that we are going to use react-intersection-observer, let's update our code.

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

export default function Home() {
  const [ref, isVisible] = useInView({ threshold: 0.7 });
  return (
    <div>
      <div style={{ height: "100vh" }}>
        <h1>Scroll down to next section πŸ‘‡</h1>
      </div>
      <div ref={ref} style={{ height: "100vh", backgroundColor: "#23cebd" }}>
        {isVisible && (
          <div style={{ marginLeft: "50px" }}>
            <h1>Hey I'm on the screen</h1>
            <img
              style={{ borderRadius: "30px" }}
              alt="ralph from the simpsons waving his hand"
              src="https://i.giphy.com/media/ASd0Ukj0y3qMM/giphy.gif"
            />
          </div>
        )}
      </div>
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

the useInView will manage the communication with the IntersectionObserver API, we'll take from it the ref to be placed on the DOM element we want to observe and a boolean value for us to use. It will only take a threshold or a rootMargin, any of those will work and the propuse is to decide how much % of the DOM element we want to scroll before switching the isVisible boolean from false to true, in this case we wrote 0.7 that's equivalent to 70% of the DOM element has to be on screen to change our boolean.

Bringing framer-motion

For the moment our element is just appearing on the screen, that could be rude. Let's update our code to make it slide from the left.

import { useInView } from "react-intersection-observer";
import { motion } from "framer-motion";

export default function Home() {
  const [ref, isVisible] = useInView({ threshold: 0.7 });
  const variants = {
    visible: {
      opacity: 1,
      x: 0,
    },
    hidden: {
      opacity: 0,
      x: -100,
    },
  };
  return (
    <div>
      <div style={{ height: "100vh" }}>
        <h1>Scroll down to next section πŸ‘‡</h1>
      </div>
      <div style={{ height: "100vh", backgroundColor: "#23cebd" }}>
        <motion.div
          ref={ref}
          variants={variants}
          animate={isVisible ? "visible" : "hidden"}
          transition={{ duration: 0.5, ease: "easeOut" }}
          style={{ marginLeft: "50px" }}
        >
          <h1>Hey I'm on the screen</h1>
          <img
            style={{ borderRadius: "30px" }}
            alt="ralph from the simpsons waving his hand"
            src="https://i.giphy.com/media/ASd0Ukj0y3qMM/giphy.gif"
          />
        </motion.div>
      </div>
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

This would be the whole example, we did one important change besides adding the framer-motion code, changing the ref from the div with the 100vh to the div that wraps the text and the image we want to slide in.

Thank you for your time
My name is Jesus Cova, I'm a full-stack developer based in Quintana Roo, MΓ©xico.

Discussion (0)

pic
Editor guide