Jump to content
Search Community

Scrolltrigger Question with labels, and a visual timeline to jump to labels

newguy123 test
Moderator Tag

Go to solution Solved by Rodrigo,

Recommended Posts

Hi

 

In my React site I have a fairly long scrolltrigger image sequence site, with around 50 text labels animated with GSAP coming up during various stages in the scroll. This all works as it should. Now to add labels to these titles and jump between these scrollpositions is pretty easy with a button.

 

However, does GSAP have a library for a visual timeline or scroll progress component where I can click on parts in the timeline, and it will jump to those sections?
I'm visually talking about this sort of thing:
image.png.a69f863ca7a6c561da8b2871d1b5f6f1.pngimage.png.0a8d1a68b9f8d6823e8428219a263ad3.png

If not, can somebody point me to a library that may assist with this sort of thing or a video that may help me create my own?

Link to comment
Share on other sites

4 minutes ago, newguy123 said:

However, does GSAP have a library for a visual timeline or scroll progress component where I can click on parts in the timeline, and it will jump to those sections?

No, but you could easily build it, like you said:

 

4 minutes ago, newguy123 said:

Now to add labels to these titles and jump between these scrollpositions is pretty easy with a button.

 

If you need help please provide a minimal demo so that we can take a look at your setup.

Link to comment
Share on other sites

2 hours ago, newguy123 said:

Hi

 

In my React site I have a fairly long scrolltrigger image sequence site, with around 50 text labels animated with GSAP coming up during various stages in the scroll. This all works as it should. Now to add labels to these titles and jump between these scrollpositions is pretty easy with a button.

 

However, does GSAP have a library for a visual timeline or scroll progress component where I can click on parts in the timeline, and it will jump to those sections?
I'm visually talking about this sort of thing:
image.png.a69f863ca7a6c561da8b2871d1b5f6f1.pngimage.png.0a8d1a68b9f8d6823e8428219a263ad3.png

If not, can somebody point me to a library that may assist with this sort of thing or a video that may help me create my own?


You coudl start from here, where the navigation menu could be the dots or fraction of a slide

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

  • Like 2
Link to comment
Share on other sites

1 hour ago, AntonioNB4 said:


You coudl start from here, where the navigation menu could be the dots or fraction of a slide

 

Yes that sort of thing. However I have 50 'sections' so spacing will get a bit tight

If we use this example as is, how would one modify it, so you keep seeing only 5 sections in that section menu/timeline at a time, but the entire thing shifts up as you progress, meaning, when you start, you see it as is, ie
section 1
section 2
section 3

section 4

section 5

but then as you go to each section, or scroll down the page, the entire section menu/timeline thing shifts, so for example lets say you scrolled halfway down, then the section timeline/menu thingy will read:
section 25
section 26
section 27
section 28
section 29


then when you scroll all the way to the bottom, it would be

section 46

section 47

section 48

section 49

section 50

understand what I mean?

Link to comment
Share on other sites

There are a lot of ways to approach this - It would be great if you could make a minimal demo showing your attempt and your own attempted solution, then we can advise.. 

 

You could do something as simple as tying the position of the nav to the page's scroll with a scrolltrigger

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

 

Or you could approach it by shifting the nav up/down in callbacks when each section comes into view, or adjust the pen above to shift the y position at th same time as changing the color of the text.

 

Hope this helps.

  • Like 2
Link to comment
Share on other sites

17 hours ago, Cassie said:

There are a lot of ways to approach this - It would be great if you could make a minimal demo showing your attempt and your own attempted solution, then we can advise.. 

 

You could do something as simple as tying the position of the nav to the page's scroll with a scrolltrigger

 

 

Or you could approach it by shifting the nav up/down in callbacks when each section comes into view, or adjust the pen above to shift the y position at th same time as changing the color of the text.

 

Hope this helps.

Thanks @Cassie that helped me out to make a start. I'm having a couple of issues, likely due to rooky mistakes, kindly bear with me:

 

Here's my stackblitz to follow along: https://stackblitz.com/edit/react-ptxtbc?file=src%2FApp.js

  • So I have a scrolltrigger, scrubbing through a 500 frame video sequence, 501 if you count the start frame of 0
  • I have 6 blue titles, set to come in with GSAP to specific corresponding video frames.

Up to this point all works as it should

Now I add my timeline thingy on the right with className "containerPosTimeline", and this is where I'm struggling to make it work like I want. The white timeline titles dont come in at the right time to correspond to the blue titles and the vieeo frames. Furthermore, that start timeline title should line up with the red line, and each timeline title should line up with that red line at the exact point their blue counterpart comes up. So too the end timeline title should line up with the red line.

  • The timeline has a green outline, so easier to see in the view here
  • I have a red line in the middle, which is meant to indicate the current position of the timeline
  • 1st issue, I'm not sure how to set the height of this thing. For now I basically took the video frames of 500 and divided it by 3, so I get 166, but in my css I made the inner height 176, otherwise I dont see the end title in the timeline
  • each title in the timeline, I similarly took the frame it should be and divided it by 3, and set its height to be that number from the top, to correspond with the video frames

I do realise this is seriously flawed, and it would be great if I could simply punch in the exact same numbers for the white timeline titles as for the blue main titles, and then simply stretch the timeline with another number taller or narrower, depending how big I want the thing to fit into the green. Essentailly so I can make green timeline 3 or 4 or 10 times smaller, depending how much of it I want to see in the green block at a time.

 

Hopefully I explained fully and clearly the current issues and where I'm trying to get to...




 

Link to comment
Share on other sites

Hi,

 

Unfortunately we don't have the time resources to solve complex issues or create convoluted custom logic for our users. It seems to me that the problems start here though:

0KfTJa2.pngIf you want to sync things with the red line, right at the start of your timeline things are wrong since the start blue text is visible but the start white text is far from the red line, so you have an offset right at the beginning of this. If I was you I would create a single timeline for the whole thing and use the position parameter for that. Other option could be create two different timelines that have the same duration and add them to a master timeline which is controlled by ScrollTrigger.

 

I would recommend going to the drawing board in order to just isolate both text animations and make them and use codepen or the GSAP Trial package to add GSDevtools in order to easily control and debug this. Once that is done plug it into the rest of your setup in order to sync everything up.

Link to comment
Share on other sites

I second what reodrigo says - but also, to add to this - In the last thread where we discussed your video frame sequence and how to make text come in using absolute positioning. I suggested drawing it out.

I *massively* recommend this here. Start by drawing it out on paper. Really wrap your head around what the problem is you're trying to solve before throwing any code at it.

It sounds like you've basically got two scrollers with idential information in them

1) the body and the titles

2) the preview panel and the labels

 

Quote

1st issue, I'm not sure how to set the height of this thing. For now I basically took the video frames of 500 and divided it by 3, so I get 166, but in my css I made the inner height 176, otherwise I dont see the end title in the timeline

The height of the preview panel doesn't really matter, it's the ratio's that matter. Think in percentages and everything will just be responsive. You'll just want to move the preview at the same pace as the scroller, and position the labels at the same percentage down their container as the titles are positioned down the body.

This answer went into that - 

 

  • Like 1
Link to comment
Share on other sites

On 9/13/2024 at 9:33 PM, Cassie said:

I second what reodrigo says - but also, to add to this - In the last thread where we discussed your video frame sequence and how to make text come in using absolute positioning. I suggested drawing it out.

I *massively* recommend this here. Start by drawing it out on paper. Really wrap your head around what the problem is you're trying to solve before throwing any code at it.

It sounds like you've basically got two scrollers with idential information in them

1) the body and the titles

2) the preview panel and the labels

 

The height of the preview panel doesn't really matter, it's the ratio's that matter. Think in percentages and everything will just be responsive. You'll just want to move the preview at the same pace as the scroller, and position the labels at the same percentage down their container as the titles are positioned down the body.

This answer went into that - 

 

Thanks @Cassie, I managed to get it working and looking like it should. It helped a lot to draw things out, and then to add temporary borders around each element so you can visually see what is going on, thanks a mil!

 

However now the next portion I'm having a struggle with. I wasnt sure to carry on here or make a new thread, as its still the same project, I decided to carry on here, hopefully thats ok?

 

With all that now working, the ScrollToPlugin is not working as expected:

 

https://stackblitz.com/edit/react-7nuqbc?file=src%2FApp.js

 

I suspect it has something to do with my function that handles the click, and its possibly added in the wrong place, and possibly not correct also.

Currently I get 2 issues:

  • scrollTo target doesn't exist. Using 0 - Since it cant find the label, it always scrolls to the start
  • There's a slight delay before it starts to scroll, I'm not sure why, perhaps because it couldnt find the label so it substitute it with 0?
  // My function to handle GSAP scrollTo ////////////////////////////////////////
  const handleClickJump = (label) => {
    gsap.to(window, {
      duration: 1,
      scrollTo: { y: '#container', offsetY: 0 },
      onComplete: () => {
        gsap.to(window, {
          duration: 1,
          scrollTo: { y: gsap.utils.selector(containerRef)(label) },
        });
      },
    });
  };
  ///////////////////////////////////////////////////////////////////////////////

 

If I move the function into the useGSAP hook, then I get an error and the page doesnt load at all...

 

EDIT:

I also tried instead of onclick on my divs, simply giving it an id and in a test I gave the 1st one an id of 'jump', then instead of the above function, I have this in my useGSAP, but I still get 'scrollTo target doesn't exist. Using 0'
 

    jump.addEventListener('click', () => {
      gsap.to(window, {
        scrollTo: { y: gsap.utils.selector('#container')('text-1') },
        duration: 1,
      });
    });

 

EDIT2:
I temporarily fixed the scrollto issue in an amateur way, skipping my labels, and simply directly working out the value to where the labels are suppose to be:

onClick={() => gsap.to(window, { duration: 2, scrollTo: 28000 * 0.2 }) }

But of course, this is amateur hour and would be nice to do it correctly with scrolling to my labels instead...
 

        <div className="inner">
          <p
            style={{ top: '0%' }}
            onClick={() => gsap.to(window, { duration: 2, scrollTo: 0 })}
          >
            0% of 500 is 0 --- |
          </p>
          <p
            style={{ top: '20%' }}
            onClick={() =>
              gsap.to(window, { duration: 2, scrollTo: 28000 * 0.2 })
            }
          >
            20% of 500 is 100 --- |
          </p>
          <p
            style={{ top: '40%' }}
            onClick={() =>
              gsap.to(window, { duration: 2, scrollTo: 28000 * 0.4 })
            }
          >
            40% of 500 is 200 --- |
          </p>
          <p
            style={{ top: '60%' }}
            onClick={() =>
              gsap.to(window, { duration: 2, scrollTo: 28000 * 0.6 })
            }
          >
            60% of 500 is 300 --- |
          </p>
          <p
            style={{ top: '80%' }}
            onClick={() =>
              gsap.to(window, { duration: 2, scrollTo: 28000 * 0.8 })
            }
          >
            80% of 500 is 400 --- |
          </p>
          <p
            style={{ top: '100%' }}
            onClick={() => gsap.to(window, { duration: 2, scrollTo: 28000 })}
          >
            100% of 500 is 500 --- |
          </p>
        </div>

If sombody could help me out and tell me what I did wrong with my scrollto code so my original scrollto works by scroll to my labels, instead of using this last amateur hack, I would greatly appreciate it...

Link to comment
Share on other sites

Hi,

 

I still think you are overcomplicating this far too much. For example what is the purpose of this:

const handleClickJump = (label) => {
  gsap.to(window, {
    duration: 1,
    scrollTo: { y: '#container', offsetY: 0 },
    onComplete: () => {
      gsap.to(window, {
        duration: 1,
        scrollTo: { y: gsap.utils.selector(containerRef)(label) },
      });
    },
  });
};

Here you are scrolling the page for one second to that element and then again the page to a particular label, any particular reason for that? Honestly it doesn't make any sense to move everything to a specific point to then move it to a different one again, this could be bad UX if you ask me. As a user I wouldn't like that the site scrolls the page to a place that I didn't expected before actually scrolling to the place I expect.

 

This is far simpler and works the way you seem to expect:

const tlRef = useRef();

const { contextSafe } = useGSAP(() => {
  let tl = gsap.timeline({
    scrollTrigger: {
      scrub: 1,
      start: 'top top',
      end: `+=28000`, // increase this to move the end marker further down and make the timeline last longer
      pin: '#container',
      markers: true,
    },
  });

  // This code is for my TimelinePosition thingy on the right /////////////////
  // let containerPosTimeline = document.querySelector('.containerPosTimeline');
  tl.to('.inner', {
    yPercent: -100,
    ease: 'none',
    duration: 500,
  });
  /////////////////////////////////////////////////////////////////////////////

  // Labels ///////////////////////////////////////////////////////////////////
  tl.addLabel(`text-1`, 0);
  tl.addLabel(`text-2`, 100);
  tl.addLabel(`text-3`, 200);
  tl.addLabel(`text-4`, 300);
  tl.addLabel(`text-5`, 400);
  tl.addLabel(`text-6`, 500);
  // ... rest of your code here
  tlRef.current = tl;
}, {});

const handleClickJump = contextSafe((time) => {
  const st = tlRef.current.scrollTrigger;
  gsap.to(window, {
    duration: 1,
    scrollTo: { y: st.start + (st.end - st.start) * (time / 500) },
  });
});

Here  is a fork of your demo:

https://stackblitz.com/edit/react-ftzwog?file=src%2FApp.js

 

Hopefully this helps

Happy Tweening!

Link to comment
Share on other sites

16 hours ago, Rodrigo said:

Hi,

 

I still think you are overcomplicating this far too much. For example what is the purpose of this:

const handleClickJump = (label) => {
  gsap.to(window, {
    duration: 1,
    scrollTo: { y: '#container', offsetY: 0 },
    onComplete: () => {
      gsap.to(window, {
        duration: 1,
        scrollTo: { y: gsap.utils.selector(containerRef)(label) },
      });
    },
  });
};

Here you are scrolling the page for one second to that element and then again the page to a particular label, any particular reason for that? Honestly it doesn't make any sense to move everything to a specific point to then move it to a different one again, this could be bad UX if you ask me. As a user I wouldn't like that the site scrolls the page to a place that I didn't expected before actually scrolling to the place I expect.

 

This is far simpler and works the way you seem to expect:

const tlRef = useRef();

const { contextSafe } = useGSAP(() => {
  let tl = gsap.timeline({
    scrollTrigger: {
      scrub: 1,
      start: 'top top',
      end: `+=28000`, // increase this to move the end marker further down and make the timeline last longer
      pin: '#container',
      markers: true,
    },
  });

  // This code is for my TimelinePosition thingy on the right /////////////////
  // let containerPosTimeline = document.querySelector('.containerPosTimeline');
  tl.to('.inner', {
    yPercent: -100,
    ease: 'none',
    duration: 500,
  });
  /////////////////////////////////////////////////////////////////////////////

  // Labels ///////////////////////////////////////////////////////////////////
  tl.addLabel(`text-1`, 0);
  tl.addLabel(`text-2`, 100);
  tl.addLabel(`text-3`, 200);
  tl.addLabel(`text-4`, 300);
  tl.addLabel(`text-5`, 400);
  tl.addLabel(`text-6`, 500);
  // ... rest of your code here
  tlRef.current = tl;
}, {});

const handleClickJump = contextSafe((time) => {
  const st = tlRef.current.scrollTrigger;
  gsap.to(window, {
    duration: 1,
    scrollTo: { y: st.start + (st.end - st.start) * (time / 500) },
  });
});

Here  is a fork of your demo:

https://stackblitz.com/edit/react-ftzwog?file=src%2FApp.js

 

Hopefully this helps

Happy Tweening!

Thanks @Rodrigo, using a fork of your fork, I adjusted things so there is a red dot to indicate the current position.

However, now there is a resize issue. If for example you click on "40", then the red dot indicator correctly goes to the 40% mark, however, if you now resize the browser window, it drifts and doesnt stay in the correct location. This also then affects it's postion if you click on other percentages. It only corrects itself after a full page refresh. I tried adding in some eventlistener for resize and refresh just the scrolltrigger, but I was not successful in fixing the drift issue:
Here's my new pen:
https://stackblitz.com/edit/react-m7brzt?file=src%2FApp.js

 

I suspect its not just desktop window resize that causes the issue, but possibly also when your keyboard is shown/hidden on mobile or the address bar on mobile is shown/hidden, since that it is sort of a window resize too

 

Link to comment
Share on other sites

I resized both demos and clicked on the links more than a few times and can't seem to replicate the issue, maybe I'm missing something obvious here 🤷‍♂️

 

Calling ScrollTrigger refresh after a screen resize is not actually needed, ScrollTrigger senses browser window resize events and debounces the resize event internally for better performance.

Link to comment
Share on other sites

34 minutes ago, Rodrigo said:

I resized both demos and clicked on the links more than a few times and can't seem to replicate the issue, maybe I'm missing something obvious here 🤷‍♂️

 

Calling ScrollTrigger refresh after a screen resize is not actually needed, ScrollTrigger senses browser window resize events and debounces the resize event internally for better performance.

While the browser is maximised, click on 40% and the red dot goes where its suppose to, like so:
image.thumb.png.375e78e563979577b614ba8597bee239.png

However, now make the browser window smaller, and the red dot drifts off the 40% mark like so:
image.thumb.png.5100ce40e377a1b9c5221d124d8acf28.png

Whats worse, now if you click on 60%, the red dot is way of the mark!

image.png

If however I refresh the screen, the red dot jumps back to 0, and now when you click on any % it goes to the correct place, that is, until you resize the window again
I tested this in Chrome as well as Firefox

Link to comment
Share on other sites

  • Solution

My bad, I only resized the width, not the height.

 

Just use invalidateOnRefresh on your ScrollTrigger config:

let tl = gsap.timeline({
  scrollTrigger: {
    scrub: 1,
    start: 'top top',
    end: `+=28000`, // increase this to move the end marker further down and make the timeline last longer
    pin: '#container',
    markers: true,
    invalidateOnRefresh: true, // <- HERE!
  },
});

https://gsap.com/docs/v3/Plugins/ScrollTrigger/?page=1#invalidateOnRefresh

 

That seems to do the trick, is worth noticing that in the demo I created doesn't have this problem.

 

Hopefully this helps

Happy Tweening!

  • Like 1
Link to comment
Share on other sites

8 minutes ago, Rodrigo said:

My bad, I only resized the width, not the height.

 

Just use invalidateOnRefresh on your ScrollTrigger config:

let tl = gsap.timeline({
  scrollTrigger: {
    scrub: 1,
    start: 'top top',
    end: `+=28000`, // increase this to move the end marker further down and make the timeline last longer
    pin: '#container',
    markers: true,
    invalidateOnRefresh: true, // <- HERE!
  },
});

https://gsap.com/docs/v3/Plugins/ScrollTrigger/?page=1#invalidateOnRefresh

 

That seems to do the trick, is worth noticing that in the demo I created doesn't have this problem.

 

Hopefully this helps

Happy Tweening!

so simple:

invalidateOnRefresh: true,

awesome, thanks a mil @Rodrigo!

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...