Jump to content
Search Community

Is there a better way to fade in text blocks based on the timeline progress?

Vilas test
Moderator Tag

Go to solution Solved by Rodrigo,

Recommended Posts

I have 3 blocks of text which appear as you scroll and I am fading in each one depending on where in the timeline the progress is.

 

I am wondering if there is any more efficient way to do this as the code below looks very redundant

 

        tl.to(blocks,{
    
            ease: 'power1.inOut',
            duration: 100,
            onUpdate: function () {
                const progress = tl.progress();
                if (progress > 0 && progress <= 0.5) {
                    gsap.killTweensOf([block2, block3]); // Cancel ongoing animations on block2 and block3
                    gsap.to(block1, { opacity: 1, y: 0, duration: opacityDuration });
                    gsap.set(block2, { opacity: 0, y: 15, });
                    gsap.set(block3, { opacity: 0, y: 15, });
                } else if (progress > 0.5 && progress <= 0.7) {
                    gsap.killTweensOf([block1, block3]); // Cancel ongoing animations on block1 and block3
                    gsap.set(block1, { opacity: 0, y: 15, });
                    gsap.to(block2, { opacity: 1, y: 0, duration: opacityDuration });
                    gsap.set(block3, { opacity: 0, y: 15, });
                } else if (progress > 0.7 && progress <= 1) {
                    gsap.killTweensOf([block1, block2]); // Cancel ongoing animations on block1 and block2
                    gsap.set(block1, { opacity: 0, y: 15, });
                    gsap.set(block2, { opacity: 0, y: 15 });
                    gsap.to(block3, { opacity: 1, y: 0, duration: opacityDuration });
                }
            }
        }, '<');
Link to comment
Share on other sites

Without a minimal demo, it's very difficult to troubleshoot; the issue could be caused by CSS, markup, a third party library, a 3rd party script, 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 dependencies as possible. Start minimal and then incrementally add code bit by bit until it breaks. Usually people solve their own issues during this process! If not, at least we have a reduced test case which greatly increases your chances of getting a relevant answer.

 

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

that loads all the plugins. Just click "fork" at the bottom right and make your minimal demo

 

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'd advice you to not use all that convoluted conditional logic, keep in mind that this block:

if (progress > 0 && progress <= 0.5) {
  gsap.killTweensOf([block2, block3]); // Cancel ongoing animations on block2 and block3
  gsap.to(block1, { opacity: 1, y: 0, duration: opacityDuration });
  gsap.set(block2, { opacity: 0, y: 15, });
  gsap.set(block3, { opacity: 0, y: 15, });
}

Will run 60 times per second while the progress value is less than 0.50000001, that means you'll create a new to() instance that will override a previously created one and you will be creating set() instances that are no longer needed.

 

You should use the position parameter to add your instances at specific points of your timelne:

https://gsap.com/resources/position-parameter/

 

If your instance is 10 seconds, then you can do something like this:

const tl = gsap.timeline();
tl
  .to(blocks, { duration: 10 }, 0)
  .to(block1, {
    duration: opacityDuration,
    opacity: 1,
    y: 0,
  }, 0)
  .set(block2, { opacity: 0, y: 15, }, 0)
  .set(block3, { opacity: 0, y: 15, }, 0)
  .to(blocks, { duration: 10 }, 0.5)
  .to(block1, {
    duration: opacityDuration,
    opacity: 1,
    y: 0,
  }, 0)
  .set(block2, { opacity: 0, y: 15, }, 0.5)
  .set(block3, { opacity: 0, y: 15, }, 0.5)

Of course ideally you'd have to find a dynamic way to achieve that, maybe using a combination of the position parameter and the call() method:

https://gsap.com/docs/v3/GSAP/Timeline/call()

 

Hopefully this helps.

Happy Tweening!

Link to comment
Share on other sites

1 hour ago, Rodrigo said:

Hi,

 

Besides echoing the need for a minimal demo, I'd advice you to not use all that convoluted conditional logic, keep in mind that this block:

if (progress > 0 && progress <= 0.5) {
  gsap.killTweensOf([block2, block3]); // Cancel ongoing animations on block2 and block3
  gsap.to(block1, { opacity: 1, y: 0, duration: opacityDuration });
  gsap.set(block2, { opacity: 0, y: 15, });
  gsap.set(block3, { opacity: 0, y: 15, });
}

Will run 60 times per second while the progress value is less than 0.50000001, that means you'll create a new to() instance that will override a previously created one and you will be creating set() instances that are no longer needed.

 

You should use the position parameter to add your instances at specific points of your timelne:

https://gsap.com/resources/position-parameter/

 

If your instance is 10 seconds, then you can do something like this:

const tl = gsap.timeline();
tl
  .to(blocks, { duration: 10 }, 0)
  .to(block1, {
    duration: opacityDuration,
    opacity: 1,
    y: 0,
  }, 0)
  .set(block2, { opacity: 0, y: 15, }, 0)
  .set(block3, { opacity: 0, y: 15, }, 0)
  .to(blocks, { duration: 10 }, 0.5)
  .to(block1, {
    duration: opacityDuration,
    opacity: 1,
    y: 0,
  }, 0)
  .set(block2, { opacity: 0, y: 15, }, 0.5)
  .set(block3, { opacity: 0, y: 15, }, 0.5)

Of course ideally you'd have to find a dynamic way to achieve that, maybe using a combination of the position parameter and the call() method:

https://gsap.com/docs/v3/GSAP/Timeline/call()

 

Hopefully this helps.

Happy Tweening!

Hi Rodrigo,

 

I tried to illustrate it in the following codepen but I think i didn't get it quite right.

 

So the idea is that there is a pinned container which is connected to a scroll trigger and at precise moments in the timeline, for example when the progress is 0.3 then the block 1 should appear. If the progress goes to 0.74 then block 2 should appear.

All 3 blocks are in the same position just only 1 visible at a time.


See the Pen abxrRYe?editors=1111 by Valentin-Ilas (@Valentin-Ilas) on CodePen

 

 

Link to comment
Share on other sites

10 minutes ago, Vilas said:

for example when the progress is 0.3 then the block 1 should appear. If the progress goes to 0.74 then block 2 should appear.

All 3 blocks are in the same position just only 1 visible at a time.

That is exactly what the position parameter can do.

 

Another way is to keep the logic you have now, but find a way to run it just once and not everytime the GSAP instance updates, that's too wasteful.

 

Happy Tweening!

Link to comment
Share on other sites

2 hours ago, Rodrigo said:

That is exactly what the position parameter can do.

 

Another way is to keep the logic you have now, but find a way to run it just once and not everytime the GSAP instance updates, that's too wasteful.

 

Happy Tweening!

I've been trying to use the position parameter and I feel like I'm close but not quite there yet

I noticed some interesting behaviour:

if scrub is set to true, then the a duration seems to be needed. So i added duration: 100 which seems to work more as a percentage

If i use the position parameter on each block, with values such as 20, 30 50 etc, they seem to trigger based on the percentage as well.

Now what i can't seem to be able to do is to make the fade-in  more animated instead of running it instantly and how to control the duration of the fade in.

 

Please see the example below:

 

See the Pen abxrRYe by Valentin-Ilas (@Valentin-Ilas) on CodePen

Link to comment
Share on other sites

3 hours ago, Rodrigo said:

That is exactly what the position parameter can do.

 

Another way is to keep the logic you have now, but find a way to run it just once and not everytime the GSAP instance updates, that's too wasteful.

 

Happy Tweening!

After more tinkering, i think i found a solution:

See the Pen ZEZNVRX?editors=1111 by Valentin-Ilas (@Valentin-Ilas) on CodePen

 

Basically I'm using tl.add() to run animations independently from the scrub on the parent container. Then i can specify for each one where during the progress it should run. Seems to work backwards too. 

 

But is this approach efficient? or do you see any performance issues?

Link to comment
Share on other sites

  • Solution
1 hour ago, Vilas said:

But is this approach efficient? or do you see any performance issues?

I doubt you'll run into performance problems with this TBH, since you have a rather small collection of elements.

 

IMHO though this seems simpler to understand, follow and maintain:

const blockElements = gsap.utils.toArray(".block");
const block1 = blocks.querySelector(".block-1");
const block2 = blocks.querySelector(".block-2");
const block3 = blocks.querySelector(".block-3");

const blockSettings = [
  { element: block1, position: 30 },
  { element: block2, position: 50 },
  { element: block3, position: 60 }
];

blockSettings.forEach((b, i) => {
  const block = blockElements[i];
  const otherBlocks = blockElements.filter((block, j) => i !== j);
  tl.add(() => {
      gsap.to(block, { opacity: 1, duration: 1, scale: 1 });
      gsap.to(otherBlocks, { opacity: 0, duration: 1, scale: 1.2 });
  }, b.position);
});

You don't have to resort to that conditional block and anything that results in simpler and shorter code, is going to make more sense if you have to look at it in 6 months.

 

Here is a fork of your demo:

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

 

Is worth mentioning that using the add() to add an anonymous function is the same as using call():

const tl = gsap.timeline();

tl.add(() => {}, position);

// Exactly the same
tl.call(() => {}, [/*parameters*/], position);

Hopefully this helps.

Happy Tweening!

  • Like 1
Link to comment
Share on other sites

I'd go even simpler with yoyo tweens and repeatDelay. Something like this.

 

See the Pen 63a3e8ecfaf75286961b6e2ec36abed7 by PointC (@PointC) on CodePen

 

Just my 2 cents. YMMV. Happy tweening.

:)

 

 

Just FYI - the original pen that I forked had tweens with a 0.1 → 1 second duration on a timeline that was 100 seconds long. With scrub set to true they were animating super fast because they were only a tiny fraction of the duration. .1% - 1%.

  • Like 2
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...