The Parallax Effect occurs when two things move at a different speed relative to each other. This effect, usually implemented through scrolling, can provide a seamless user experience to your website and has recently been a popular trend in user interface design.

This effect is implemented in many ways like

In this blog, we'll create the Zoom-Out-Slide-In Parallax Effect combining the first three ways with the help of framer-motion.

Zoom-Out-Slide-In Parallax Effect

So without further ado, let's get started.

Pre-requisites:

  • Basic knowledge of react version 16.8+
  • Basic knowledge of framer-motion library

We'll break down the animation into 3 stages for the ease of understanding

Create base structure

The structure is simple. We'll create a Main container, a Parent container, and 2 Child containers for the base structure. The Main container will have a certain height greater than the window size as the Parallax effect is implemented through scrolling. Let's keep its height as 300vh though you can assign it as per your requirements. Its width is 100vw, acquiring the whole window width. The Child containers are created using motion.div as they will be animated later. The code for creating the base structure looks as below:

import React from "react";
import { motion } from "framer-motion";

export default function App() {
  return (
    <div style={{ width: "100vw", height: "300vh" }}>
      <div style={{ display: "flex" }}>
        <motion.div className="child" style={{ height: "100%", width: "50vw" }}>
          <h2>Slide In </h2>
        </motion.div>
        <motion.div className="child" style={{ height: "100%", width: "50vw" }}>
          <h2>Scroll Down to Zoom Out</h2>
        </motion.div>
      </div>
    </div>
  );
}

The child class added in styles.css helps in laying out the children.

.child {
  padding: 2rem;
  justify-content: center;
  align-items: center;
  display: flex;
  flex-direction: column;
}

The base structure seems ready.

Base Structure

Create Zoom-Out effect

Now we will implement the Zoom-Out effect on the right child. The basic idea is to scale the right child down from 2x to 1x on the scroll. framer-motion provides useViewPortScroll() hook that returns MotionValues when viewport scrolls. Since we want to perform scaling animation on a vertical scroll, we will extract scrollY from it to keep track of vertical scroll distance in pixels. We want to perform a scaling transformation based on the range of scrollY. For this, we will use the useTransform() hook that transforms and returns the output of a MotionValue based on the input range of another MotionValue.

  • We will perform transformation when scrollY updates from input values 0px to 500px.
  • We will create MotionValue scaleRight and transform it from output values 2 to 1.
  • We will also animate its translation value to add movement while scaling. For vertical translation, we will create MotionValue yRight and transform it from output values 25vh to 0vh.
  • For horizontal translation, we will create MotionValue xRight and transform it from output values -25vw to 0vw.
  • We then assign the output MotionValues to the right child motion.div for animation.

Note that the input values and output values provided to the useTransform() hook are not final and can be fine-tuned according to one's requirements.

Next, we want the effect to be finished before the Parent container scrolls up and goes out of the viewport. To achieve this, let's implement fixed position scrolling and make the Parent container sticky.

The code for the Zoom-Out effect is as follows:

import React from "react";
import { motion, useTransform, useViewportScroll } from "framer-motion";

export default function App() {
  const { scrollY } = useViewportScroll();
  const scaleRight = useTransform(scrollY, [0, 500], [2, 1]);
  const yRight = useTransform(scrollY, [0, 500], ["25vh", "0vh"]);
  const xRight = useTransform(scrollY, [0, 500], ["-25vw", "0vw"]);

  return (
    <div style={{ width: "100vw", height: "300vh" }}>
      <div
        style={{
          height: "110vh",
          display: "flex",
          position: "sticky",
          top: "0px",
        }}
      >
        <motion.div className="child" style={{ height: "100%", width: "50vw" }}>
          <h2>Slide In </h2>
        </motion.div>
        <motion.div
          className="child"
          style={{
            height: "100%",
            width: "50vw",
            scale: scaleRight,
            y: yRight,
            x: xRight,
          }}
        >
          <h2>Scroll Down to Zoom Out</h2>
        </motion.div>
      </div>
    </div>
  );
}

The Zoom-Out Effect seems done.

Zoom-Out Effect

Create Slide-In effect

Now, we will implement the Slide-In effect on the left child. This will be just a simple horizontal translation interpolated on a vertical scroll.

  • Initially, the left child to be out of the viewport and it will gradually slide in the viewport when scrollY reaches 500px.
  • For this let's create MotionValue xLeft and transform from output values -60% to 0vw and assign it to left child motion.div for animation. You can play around with the input and output values as per your needs.

The code is as follows:

import React from "react";
import { motion, useTransform, useViewportScroll } from "framer-motion";

export default function App() {
  const { scrollY } = useViewportScroll();

  const scaleRight = useTransform(scrollY, [0, 500], [2, 1]);
  const yRight = useTransform(scrollY, [0, 500], ["25vh", "0vh"]);
  const xRight = useTransform(scrollY, [0, 500], ["-25vw", "0vw"]);

  const xLeft = useTransform(scrollY, [0, 500], ["-60%", "0vw"]);

  return (
    <div style={{ width: "100vw", height: "300vh" }}>
      <div
        style={{
          height: "110vh",
          display: "flex",
          position: "sticky",
          top: "0px",
        }}
      >
        <motion.div
          className="child"
          style={{
            height: "100%",
            width: "50vw",
            x: xLeft,
          }}
        >
          <h2>Slide In </h2>
        </motion.div>
        <motion.div
          className="child"
          style={{
            height: "100%",
            width: "50vw",
            scale: scaleRight,
            y: yRight,
            x: xRight,
          }}
        >
          <h2>Scroll Down to Zoom Out</h2>
        </motion.div>
      </div>
    </div>
  );
}

Tada! we have implemented the Zoom-Out-Slide-In Parallax effect.

The complete effect 

You can find the code here and play around with it. With framer-motion's useViewPortScroll() and useTransform() hooks, we can create beautiful and engaging parallax scrolling effects in a simple and effortless manner.

References: