Logo

dev-resources.site

for different kinds of informations.

How to create Scroll-Linked Animations with React and Framer Motion πŸ’ƒπŸ»πŸ•ΊπŸ»

Published at
3/2/2023
Categories
react
typescript
framermotion
Author
mmintel
Categories
3 categories in total
react
open
typescript
open
framermotion
open
Author
7 person written this
mmintel
open
How to create Scroll-Linked Animations with React and Framer Motion πŸ’ƒπŸ»πŸ•ΊπŸ»

How to create Scroll-Linked Animations with React and Framer Motion πŸ’ƒπŸ»πŸ•ΊπŸ»

Scroll-linked animations are a popular technique that links animation to the user's scrolling on the page. As you scroll down the page, different elements will animate in or out, giving your website a unique and dynamic feel.

In this tutorial, we'll be using React and Framer Motion to create a scroll-linked animation. Framer Motion is a powerful animation library that makes it easy to create fluid and beautiful animations with React. We'll also be using TypeScript, a popular language that adds strong typing to JavaScript and can help catch errors before runtime.

The Code

Here's the TypeScript code we'll be working with:

import { motion, useScroll, useTransform } from 'framer-motion'
import { useElementViewportPosition } from '../hooks/useElementViewportPosition'
import React, { useRef, PropsWithChildren } from 'react'

export const ScrollReveal: React.FC<PropsWithChildren<{
  offset?: number
  distance?: number
}>> = ({ offset = 0, distance = 50, children }) => {
  const ref = useRef(null)
  const { position } = useElementViewportPosition(ref, offset)
  const { scrollYProgress } = useScroll()
  const opacity = useTransform(scrollYProgress, position, [0, 1])
  const y = useTransform(scrollYProgress, position, [distance, 0])

  return (
    <motion.div
      ref={ref}
      style={{
        opacity,
        y,
      }}
    >
      {children}
    </motion.div>
  )
}
Enter fullscreen mode Exit fullscreen mode

And here's the code for the useElementViewportPosition hook that we'll be using:

import { useState, useEffect, RefObject } from 'react'

export function useElementViewportPosition(
  ref: RefObject<HTMLElement>,
  offset = 0,
) {
  const [position, setPosition] = useState([0, 0])

  useEffect(() => {
    const update = () => {
      if (!ref || !ref.current) return
      const pageHeight = document.body.scrollHeight
      const start = ref.current.offsetTop
      const end = start + ref.current.offsetHeight

      setPosition([(start + offset) / pageHeight, (end + offset) / pageHeight])
    }

    update()

    document.addEventListener('resize', update)

    return () => {
      document.removeEventListener('resize', update)
    }
  }, [offset, ref])

  return { position }
}

Enter fullscreen mode Exit fullscreen mode

How It Works

So, how do these two pieces of code work together to create those scroll-linked animations? Let me break it down for you.

First, we're importing motion, useScroll, and useTransform from Framer Motion, and useElementViewportPosition from our own custom hook. We're also importing some stuff from React.

import { motion, useScroll, useTransform } from 'framer-motion'
import { useElementViewportPosition } from '../hooks/useElementViewportPosition'
import React, { useRef, PropsWithChildren } from 'react'

Enter fullscreen mode Exit fullscreen mode

Next, we're defining a new component called ScrollReveal. This component takes two optional props: offset and distance, which we'll explain in a bit. It also takes some children that we'll animate as the user scrolls.

export const ScrollReveal: React.FC<PropsWithChildren<{
  offset?: number
  distance?: number
}>> = ({ offset = 0, distance = 50, children }) => {
Enter fullscreen mode Exit fullscreen mode

Inside this component, we're creating a new ref with useRef(null). We're also calling our useElementViewportPosition hook and passing in our ref and offset. This hook will return a value called position.

Breaking Down the Code

Now that we've seen the code in action, let's break it down line by line.

import { motion, useScroll, useTransform } from 'framer-motion'
import { useElementViewportPosition } from '../hooks/useElementViewportPosition'
import React, { useRef, PropsWithChildren } from 'react'
Enter fullscreen mode Exit fullscreen mode

Here we have our imports. We're using Framer Motion, a popular React animation library, and importing motion, useScroll, and useTransform. We're also importing a custom hook called useElementViewportPosition from our ../hooks folder. Finally, we're importing React and a couple of types.

export const ScrollReveal: React.FC<PropsWithChildren<{
  offset?: number
  distance?: number
}>> = ({ offset = 0, distance = 50, children }) => {

Enter fullscreen mode Exit fullscreen mode

This line exports a React functional component called ScrollReveal. It's defined as a function that takes a prop object with optional offset and distance properties, as well as the children prop. The component will use these props to determine how much to reveal and how far to animate.

const ref = useRef(null)

Enter fullscreen mode Exit fullscreen mode

This line creates a React ref called ref that we'll use later to track the position of the component in the viewport.

const { position } = useElementViewportPosition(ref, offset)

Enter fullscreen mode Exit fullscreen mode

This line calls the useElementViewportPosition hook we imported earlier, passing in our ref and offset props. The hook returns an object with a position property that we'll use to animate the component.

const { scrollYProgress } = useScroll()

Enter fullscreen mode Exit fullscreen mode

This line calls another Framer Motion hook called useScroll, which returns an object with a scrollYProgress property that we'll use to animate the component.

useTransform does all the Magic!

The useTransform hook is part of the Framer Motion library, which is used in the ScrollReveal component.

The useTransform hook is used to create a relationship between the scroll position and an animated property of an element. In this case, it's used to adjust the opacity and y-position of the animated element (motion.div) as the user scrolls.

In more detail, useTransform takes three arguments: input, inputRange, and outputRange. The input argument is a value that changes over time, in this case the scrollYProgress from useScroll. The inputRange argument is an array that defines the range of the input value (e.g. [0, 1]), and the outputRange argument is an array that defines the range of values for the output (e.g. [distance, 0]).

const opacity = useTransform(scrollYProgress, position, [0, 1])
const y = useTransform(scrollYProgress, position, [distance, 0])
Enter fullscreen mode Exit fullscreen mode

These lines use the useTransform hook to create two new variables, opacity and y, that we'll use to animate the component. The useTransform hook takes three arguments: a value to transform (in this case scrollYProgress), the range of the input values (position), and the range of the output values ([0, 1] for opacity and [distance, 0] for y).

return (
    <motion.div
      ref={ref}
      style={{
        opacity,
        y,
      }}
    >
      {children}
    </motion.div>
  )

Enter fullscreen mode Exit fullscreen mode

Finally, we return a motion.div with our ref and two style properties, opacity and y, that we created using the useTransform hook. We also render the children prop inside the motion.div.

Conclusion

In this article, we've explored how to use TypeScript, React, and Framer Motion to create a scroll-linked animation. We've broken down the code line by line and explained what each part does. If you want to learn more about TypeScript, check out the official documentation. If you want to learn more about Framer Motion, check out the official documentation. Happy animating!

framermotion Article's
30 articles in total
Favicon
Building a food simulation game with Next.js and generative design
Favicon
How to Create a Flipping Card Animation Using Framer Motion
Favicon
State-of-the-Art Web Design with Remix, Tailwind, and Framer Motion
Favicon
How I Created a Stunning Portfolio with Next.js, Tailwind CSS, and Framer Motion
Favicon
Creating the iMessage Card Stack Animation: The Interactive Layer
Favicon
Creating the iMessage Card Stack Animation: The TimelineΒ Design
Favicon
How to build awesome website with stunning animation?
Favicon
Adding motion to 3D models with Framer Motion and Three.js
Favicon
Creating a Smooth Animated Menu with React and Framer Motion
Favicon
My-Portfolio
Favicon
Watch Out For Broken Links, 404 Page With Framer Motion, TailwindCSS and NextJs
Favicon
Could not find a declaration file for module framer-motion Error in Next.js 14
Favicon
How to add Animations and Transitions in React
Favicon
Creating a text writing animation in React using Framer-Motion
Favicon
Unveiling My Portfolio Website: A Fusion of Tech and Creativity πŸš€
Favicon
React developer portfolio template use framer motion to animate components
Favicon
Building a mini casual game with Next.js
Favicon
Creating a Multi-Level Sidebar Animation with Tailwind and Framer Motion in React
Favicon
Easy Animated Tabs in ReactJS with Framer Motion
Favicon
A Tinder-like card game with Framer-Motion
Favicon
Elevating Web Design with Framer Motion: Bringing Your Website to Life
Favicon
Animating the Web: Route Transitions in Next.js with Framer Motion
Favicon
Enhancing Form Usability with Framer Motion: A Guide to Animated, Chunked Form Transitions
Favicon
How to Build a fully accessible Accordion with HeadlessUI, Framer Motion, and TailwindCSS
Favicon
Build Portfolio Theme in Tailwind CSS & Next.js
Favicon
How to create Scroll-Linked Animations with React and Framer Motion πŸ’ƒπŸ»πŸ•ΊπŸ»
Favicon
How to build 3 simple animation with framer motion
Favicon
How to create an awesome navigation menu using chakra-UI and framer-motion.
Favicon
Make a slide open envelope
Favicon
Animate everything with Framer Motion

Featured ones: