profile picture of Olivier Larose

Olivier Larose

May 25, 2024

/

Intermediate

/

Short

Background Image Parallax

How to Make a Background Image Parallax using Framer Motion and React

A website animation featuring a background image moving on scroll in a parallax motion, made with Framer Motion and React, inside a Next.js app. Inspired by: https://inkfishnyc.com/. Pictures by Matthias Leidinger

Live DemoSource code
background video

Creating the Project

Let's start the project by creating a Next.js application. We can do that by running npx create-next-app@latest client inside of a terminal.

  • We will use Framer Motion for the animation, so we can run npm i framer-motion.
  • We will use the Lenis Scroll for the smooth scrolling, so we can run npm i lenis.

Basic Background Parallax

The basics of creating a background image parallax is as follow:

  • Create a container with an overflow hidden
  • Add an image inside of that container taking the full width and height of it
  • Translate the image on scroll

components/Intro.jsx

1

import React from 'react'

2

import Image from 'next/image';

3

import Background from '../../public/images/2.jpg';

4

import { useScroll, useTransform, motion } from 'framer-motion';

5

import { useRef } from 'react';

6

7

export default function Intro() {

8

const container = useRef();

9

const { scrollYProgress } = useScroll({

10

target: container,

11

offset: ['start start', 'end start']

12

})

13

14

const y = useTransform(scrollYProgress, [0, 1], ["0vh", "150vh"])

15

16

return (

17

<div className='h-screen overflow-hidden'>

18

<motion.div style={{y}} className='relative h-full'>

19

<Image src={Background} fill alt="image" style={{objectFit: "cover"}}/>

20

</motion.div>

21

</div>

22

)

23

}

Couple notes about the code above:

  • The progress of the scroll is tracked using the useScroll Hook
  • Since the component is the first inside the page, we use an offset of ['start start']
  • The progress of the scroll (a value between 0 and 1) is transformed into a value between 0vh and 150vh and used as a translateY value

Then I'm adding that component inside the page.js:

page.js

componen...

1

'use client';

2

import { useEffect } from 'react';

3

import Lenis from 'lenis'

4

import Intro from '@/components/Intro';

5

import Description from '@/components/Description';

6

7

export default function Home() {

8

9

useEffect( () => {

10

const lenis = new Lenis()

11

12

function raf(time) {

13

lenis.raf(time)

14

requestAnimationFrame(raf)

15

}

16

17

requestAnimationFrame(raf)

18

}, [])

19

20

return (

21

<main>

22

<Intro />

23

<Description />

24

<div className='h-screen'></div>

25

</main>

26

);

27

}

We should have something like this:

Advanced Background Parallax

The other way of making a background parallax is the same way but instead the container will be in position fixed.

components/Section.jsx

1

import Image from 'next/image';

2

import Background from '../../public/images/1.jpg';

3

import Text from './components/Text';

4

import { useScroll, useTransform, motion } from 'framer-motion';

5

import { useRef } from 'react';

6

7

export default function Section() {

8

const container = useRef();

9

const { scrollYProgress } = useScroll({

10

target: container,

11

offset: ["start end", 'end start']

12

})

13

const y = useTransform(scrollYProgress, [0, 1], ["-10vh", "10vh"]);

14

15

return (

16

<div

17

ref={container}

18

className='relative flex items-center justify-center h-screen overflow-hidden'

19

style={{clipPath: "polygon(0% 0, 100% 0%, 100% 100%, 0 100%)"}}

20

>

21

<Text />

22

<div className='fixed top-[-10vh] left-0 h-[120vh] w-full'>

23

<motion.div style={{y}} className='relative w-full h-full'>

24

<Image src={Background} fill alt="image" style={{objectFit: "cover"}}/>

25

</motion.div>

26

</div>

27

</div>

28

)

29

}

Couple notes about the above code:

  • The image container is in position fixed, so we need to add a clip-path on the parent to crop that fixed div.
  • The same principles apply from the previous implementation, we translate the image based on the position of the scroll.
  • The amount translated here (-10vh and 10vh) should be accounted in the height of the fixed container (120vh).

We should have something like this:

Wrapping up

That's it for this animation!

A super clean animation that in my opinion should be in almost every single websites, it really adds a nice dimensionality and brins it to another level of quality. Hope you learned something!

-Oli

Related Animations

image

June 2, 2024

Mask Section Transition

A website tutorial featuring a scroll animation using an SVG Mask to create a section transition, made with React, Framer Motion. Inspired by: https://axelvanhessche.com/. Pictures by Eric Asamoah, Inka and Niclas Lindergård, Daniel Ribar

image

May 25, 2024

Text Parallax

A website animation featuring a Text Parallax with sliding text on scroll, made with Framer Motion and React, inside a Next.js app

image

May 21, 2024

Sticky Footer

A website tutorial featuring a sticky footer animation with a reveal effect, made with position fixed and sticky using React and CSS.