DEV Community

jesuscovam
jesuscovam

Posted on

React Parallax effect with Framer-Motion

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.

Top comments (3)

Collapse
 
loq24 profile image
Lougie

Good tutorial. You can actually use Framer Motion's built in whileInView function instead of react-intersection-observer.

Collapse
 
donaldboulton profile image
Donald W. Boulton

Nice Work incorporated this and works perfect.
Thanks
Donald Boulton

Collapse
 
mahdisoultana profile image
Mahdi

thank you very much for this article it's really helpfull