Jump to content
GreenSock

Search In
  • More options...
Find results that contain...
Find results in...
talkingSkunk

(React/Next.js) AirPods image swap on scroll with ScrollTrigger

Recommended Posts

I am trying to create scroll animation using scrollTrigger plugin on my Next.js, but I'm getting error "cannot read property "_gsap" of undefined, etc.

 

I have a spritesheet of 50 images (1.5 MB), that would be providing the scroll images.

 

I have looked at these two, which was helpful, but only it doesn't translate very well into use for Next.js.

Help would be appreciated.

 

 

import Link from 'next/link'
import Head from 'next/head'
import Image from 'next/image'
import {useEffect, useRef, useState} from 'react'
import styles from '../styles/Home.module.css'
import {gsap} from 'gsap'
import { ScrollTrigger } from "gsap/dist/ScrollTrigger";

const Home = () => {

const imageViewer = useRef(null)
const imageScene = useRef(null)
console.log('imageID', imageViewer)

// if (process.client) {
// }
gsap.registerPlugin(ScrollTrigger)


let frame_count = 9
let offset_value = 100

gsap.to(imageViewer.current, {
// backgroundPosition: (-offset_value * frame_count * 2) + "px 50%",
// ease: "steps(" + frame_count + ")", // use a stepped ease for the sprite sheet
scrollTrigger: {
trigger: imageScene.current,
start: "top top",
end: "+=" + (frame_count * offset_value),
pin: true,
scrub: true
}
})



return (
<>
<Head>
<title>TalkingSkunk | Home</title>
<meta name='keywords' content='talkingskunk' />
<link rel="icon" href="/favicon.ico" />
{/* <script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.6.1/gsap.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.6.1/ScrollTrigger.min.js"></script> */}
</Head>

{/* <div className={styles.bgWrap}>
<Image
src="/home.png"
className='cityscape'
data-speed="0.2"
layout="fill"
objectFit="cover"
quality={100}
/>
</div> */}
<div className={`${styles.header} ${styles.section}`}>
<div className={styles.center}>&darr;START</div>
</div>

<p className={styles.bgText}>
Discover
</p>
<div ref={imageScene} className={`${styles.scene} ${styles.section}`} id="sticky">
<div ref={imageViewer} className={styles.viewer}></div>
</div>

<div className={styles.section}>
<div className={styles.center}>End</div>
</div>

<div className={styles.canvas}>

</div>




<div className={styles.body}>

</div>

</>
)
}

export default Home;

 

See the Pen 2152a28cffe2c2c0cca8a3e47f7b21c6?editors=0010 by osublake (@osublake) on CodePen

Link to comment
Share on other sites

Hi there - I'm not sure exactly what you'll need to do code-wise to fix this - I'm not a react dev.

But that error is because you're trying to animate elements that aren't yet in the DOM.

 

I think this can be fixed by moving the GSAP animations inside a useEffect hook or refs? Something like that.

 

Hopefully this article will get you pointed in the right direction - else I'm sure someone more knowledgable with react will get to you shortly!

https://www.smashingmagazine.com/2020/09/animating-react-components-greensock/

 

  • Like 3
Link to comment
Share on other sites

HI,

 

Cassie is completely right, you need to move your GSAP code to a useEffect or useCallback hook. Most of the time a useEffect hook should be enough, but if you run into some issues you might need to switch to a useCalback or useLayoutEffect.

 

Right now when react runs your code, it does it in the regular JS way, so it starts at the top and goes through it until everything is ran. The issue is that your GSAP code runs before the return statement that actually tells react what it should add to the DOM. A useEffect hook with an empty dependencies array:

useEffect(() => {
  let frame_count = 9
  let offset_value = 100
  
  const t = gsap.to(imageViewer.current, {
    scrollTrigger: {
      trigger: imageScene.current,
      start: "top top",
      end: "+=" + (frame_count * offset_value),
      pin: true,
      scrub: true
    }
  });
  // Clean up before unmounting the component/page
  return () => {
    t.kill();
    ScrollTrigger.getAll().forEach(e => e.kill());
  };
}, []);

You can learn about the useEffect hook here:

https://reactjs.org/docs/hooks-effect.html

https://reactjs.org/docs/hooks-effect.html#tip-optimizing-performance-by-skipping-effects

https://reactjs.org/docs/hooks-effect.html#effects-with-cleanup

 

Finally since you're using Next, is not really a good idea to check the process object to see if the code is running on the client or the server, that is basically an object created and used by webpack, is better to check if the window is undefined or not:

if (typeof window !== "undefined") {
  gsap.registerPlugin(ScrollTrigger);
}

Happy Tweening!!! 

  • Like 4
Link to comment
Share on other sites

This was really wonderfully explained. Thank you @Rodrigo

That's really clicked some react bits in place for me.

  • Like 1
Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
  • Recently Browsing   0 members

    • No registered users viewing this page.
×