Jump to content
Search Community

Hi there I'm new in gsap I'm having issues with animating images using scrollTrigger in react js.

Md. Israfil test
Moderator Tag

Recommended Posts

Hi there I'm new in gsap I'm having issues with animating images on scrollTrigger in react js.  When I'm scrolling onUpdate function not calling I know i'm doing something wrong in my code but I can't figure out.

 

 

import React, { useEffect, useLayoutEffect, useRef, useState } from 'react';
import './App.css';
 
import Header from './Components/Header';
import Page from './Components/Page';
import Page1 from './Components/Page1';
import Page2 from './Components/Page2';
import Page3 from './Components/Page3';
import Page4 from './Components/Page4';
import Page5 from './Components/Page5';
import Page6 from './Components/Page6';
import Page7 from './Components/Page7';
import Page8 from './Components/Page8';
import Page9 from './Components/Page9';
 
import LocomotiveScroll from 'locomotive-scroll';
import gsap from 'gsap';
import { ScrollTrigger } from 'gsap/ScrollTrigger';
import { useGSAP } from '@gsap/react';
 
gsap.registerPlugin(ScrollTrigger);
function App() {
    const mainRef = useRef<HTMLElement>(null);
    const page = useRef<HTMLElement>(null);
    const page1 = useRef<HTMLElement>(null);
    const page2 = useRef<HTMLElement>(null);
    const page3 = useRef<HTMLElement>(null);
    const page4 = useRef<HTMLElement>(null);
    const page5 = useRef<HTMLElement>(null);
    const [frameCount, setFrameCount] = useState(467);
 
    useLayoutEffect(() => {
        new LocomotiveScroll({ smooth: true });
    }, []);
 
    const canvaRef = useRef<HTMLCanvasElement>(null);
    const [imageSeq, setImageSeq] = useState({ frame: 1 });
 
    const [renderImages, setRenderImages] = useState<Array<HTMLImageElement>>([]);
    const [loading, setLoading] = useState(true);
 
    // useLayoutEffect(() => {}, [canvaRef.current]);
 
    function scaleImage(img: HTMLImageElement, ctx: CanvasRenderingContext2D) {
        console.log(img);
        const canvas = ctx.canvas;
        const hRatio = canvas.width / img?.width;
        const vRatio = canvas.height / img?.height;
        const ratio = Math.max(hRatio, vRatio);
        const centerShift_x = (canvas.width - img.width * ratio) / 2;
        const centerShift_y = (canvas.height - img.height * ratio) / 2;
        ctx.clearRect(0, 0, canvas.width, canvas.height);
        ctx.drawImage(
            img,
            0,
            0,
            img.width,
            img.height,
            centerShift_x,
            centerShift_y,
            img.width * ratio,
            img.height * ratio
        );
    }
 
    useEffect(() => {
        const loadImages = async () => {
            const images = import.meta.glob('./assets/sequence/*');
            const imagePromises = Object.values(images).map((resolve) => resolve());
            const imageModules = await Promise.all(imagePromises);
            const imageUrls = imageModules.map((module) => module.default);
 
            // setImages(imageUrls); // This will log an array of image URLs
            return imageUrls;
        };
        loadImages()
            .then((urls) => {
                const imgs = urls.map((item) => {
                    const img = new Image();
                    img.src = item;
                    return img;
                });
 
                setRenderImages(imgs);
            })
            .then(() => {
                setLoading(false);
            })
            .catch((err) => console.log(err));
    }, []);
 
    useGSAP(
        () => {
            animation();
        },
        { dependencies: [renderImages.length], scope: mainRef }
    );
 
    const animation = async () => {
        if (!canvaRef.current || !renderImages[0]) return;
 
        console.log({ canvaRef });
 
        const canvas = canvaRef.current;
 
        const context = canvas.getContext('2d');
        canvas.width = window.innerWidth;
        canvas.height = window.innerHeight;
 
        window.addEventListener('resize', function () {
            canvas.width = window.innerWidth;
            canvas.height = window.innerHeight;
            console.log(this.window.innerWidth);
            render(context as CanvasRenderingContext2D);
        });
 
        gsap.to(imageSeq, {
            frame: frameCount, // Decrement the frame in the animation
            snap: 'frame',
            ease: 'none',
            scrollTrigger: {
                scrub: 0.5,
                trigger: page.current,
                start: 'top top',
                end: '400% top',
                scroller: mainRef.current,
            },
            onUpdate: () => {
                // Update the state with the new frame count
                setFrameCount((prev) => prev - 1);
 
                // Set the updated frame value without animating
                gsap.set(imageSeq, { frame: frameCount });
 
                // Render based on the updated frame
                render(context as CanvasRenderingContext2D);
 
                console.log(imageSeq);
            },
        });
 
        ScrollTrigger.create({
            trigger: mainRef.current,
            pin: true,
            markers: true,
            scroller: mainRef.current,
            //   set start end according to preference
            start: `top top`,
            end: `400% top`,
        });
 
        renderImages[1].onload = () => render(context as CanvasRenderingContext2D);
        // renderImages[1].onload = () =>
        //  scaleImage(renderImages[imageSeq.frame], context);
    };
 
    function render(context: CanvasRenderingContext2D) {
        console.log(imageSeq?._gsap);
        scaleImage(renderImages[imageSeq.frame], context);
    }
 
    console.log(imageSeq);
 
    if (loading) return <div>Loading</div>;
    return (
        <div
            ref={mainRef as React.RefObject<HTMLDivElement>}
            className='relative overflow-hidden bg-[#2C2C2C]'
        >
            <Header />
 
            <div className='r relative overflow-hidden  min-h-[100vh] min-w-[100vw]'>
                <Page ref={canvaRef as React.RefObject<HTMLCanvasElement>} />
                <canvas
                    id='canvas'
                    style={{ zIndex: '9999' }}
                    className='z-50  max-w-[100vw]  max-h-[100vh]'
                    ref={canvaRef as React.RefObject<HTMLCanvasElement>}
                ></canvas>
            </div>
            <Page1 ref={page1 as React.RefObject<HTMLElement>} />
            <Page2 ref={page2 as React.RefObject<HTMLElement>} />
            <Page3 ref={page3 as React.RefObject<HTMLElement>} />
            <Page4 ref={page4 as React.RefObject<HTMLElement>} />
            <Page5 ref={page5 as React.RefObject<HTMLElement>} />
            <Page6 />
            <Page7 />
            <Page8 />
        </div>
    );
}
 
export default App;

App.tsx

Link to comment
Share on other sites

It's very difficult to troubleshoot without a minimal demo; the issue could be caused by CSS, markup, a third party library, your browser, an external script that's totally unrelated to GSAP, etc. Would you please provide a very simple CodePen or Stackblitz that illustrates the issue? 

 

Please don't include your whole project. Just some colored <div> elements and the GSAP code is best. See if you can recreate the issue with as few dependancies as possible. If not, incrementally add code bit by bit until it breaks. Usually people solve their own issues during this process! If not, then at least we have a reduced test case which greatly increases your chances of getting a relevant answer.

 

Here's a starter CodePen that loads all the plugins. Just click "fork" at the bottom right and make your minimal demo

See the Pen aYYOdN by GreenSock (@GreenSock) on CodePen

 

Using a framework/library like React, Vue, Next, etc.? 

CodePen isn't always ideal for these tools, so here are some Stackblitz starter templates that you can fork and import the gsap-trial NPM package for using any of the bonus plugins: 

 

Please share the StackBlitz link directly to the file in question (where you've put the GSAP code) so we don't need to hunt through all the files. 

 

Once we see an isolated demo, we'll do our best to jump in and help with your GSAP-specific questions. 

Link to comment
Share on other sites

Hi,

 

Besides echoing the need for a minimal demo I agree with this:

On 1/14/2024 at 12:05 AM, GSAP Helper said:

By the way, this looks very odd to me: 

dependencies: [renderImages.length]

What are you trying to do there?

Is better to just pass the entire renderImages array as a dependency and not the length of the array, React normally does a shallow comparison of dependencies so there is no need to pass the length property there.

 

Also I see you're doing this:

useGSAP(
  () => {
    animation();
  },
  { dependencies: [renderImages.length], scope: mainRef }
);

But your animation method doesn't return any GSAP instance so every GSAP instance in it will not be added to the GSAP Context instance created by the useGSAP hook and reverted when the component gets unmounted. Is better to just add all the code inside the useGSAP scope or use contextSafe:

const {contextSafe} = useGSAP(
  () => {
    animation();
  },
  {
    dependencies: [renderImages],
    scope: mainRef,
    revertOnUpdate: true,
  }
);

const animation = contextSafe(async () => {
  if (!canvaRef.current || !renderImages[0]) return;
  // REST OF YOUR CODE HERE
});

Hopefully this helps.

Happy Tweening!

Link to comment
Share on other sites

10 hours ago, Rodrigo said:

Also I see you're doing this:

useGSAP(
  () => {
    animation();
  },
  { dependencies: [renderImages.length], scope: mainRef }
);

But your animation method doesn't return any GSAP instance so every GSAP instance in it will not be added to the GSAP Context instance created by the useGSAP hook and reverted when the component gets unmounted.

It's fine to call the animation() method from inside useGSAP() like that. The animations will get recorded in the context because they're created during execution of that useGSAP() hook function. That dependency thing with the renderImages.length is definitely wrong, though. 

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.
×
×
  • Create New...