Jump to content
GreenSock

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

Missing Something Silly w/ Time Manipulation

Go to solution Solved by OSUblake,

Recommended Posts

Hey y'all 👋

 

Looping through a set of numbers infinitely by tweening `totalTime` on a timeline. But, how do I show the current state while it's `paused`?

 

I'm setting `TL.totalTime(1)` here but nothing shows. Do I need to do something with `immediateRender`? I'm going to be scrubbing this timeline with another control so it will remain paused. I've tried the `invalidate().restart()` manoeuvre but I'm not having much luck...

 

import gsap from 'https://cdn.skypack.dev/gsap'

const TL = gsap.timeline({
  paused: true,
})

const DIGITS = gsap.utils.toArray('.digit')

const PADDED_DIGITS = [...DIGITS, DIGITS[0]]

for (let i = 0; i < PADDED_DIGITS.length; i++) {
  const DIGIT = PADDED_DIGITS[i]
  const DIGI_TIMELINE = gsap.timeline({
    paused: false,
    onComplete: () => gsap.set(DIGIT, { clearProps: true })
  })
  DIGI_TIMELINE
    .set(DIGIT, {
      yPercent: 0,
    })
    .to(DIGIT, {
      yPercent: 100,
      delay: i,
      duration: 1
    })
    .to(DIGIT, {
      yPercent: 200,
      duration: 1
    })
    TL.add(DIGI_TIMELINE, '- 0.5')
}

const MANIPULATOR = gsap.timeline({
  delay: 2,
  paused: true,
})
  .fromTo(TL, {
    totalTime: 1,
  }, {
    totalTime: 11,
    duration: 4,
    ease: 'none',
    repeat: -1,
  })

TL.totalTime(1)

Thanks in advance! Sure I'm missing something obvious.

 

ʕ ᵒ ᴥ ᵒʔ

 

Setting `MANIPULATOR` to `paused: false` plays the timeline as expected. But, I'd like to see the playhead state when it's paused.

See the Pen OJOPdQw by jh3y (@jh3y) on CodePen

Link to comment
Share on other sites

Think I've worked out a way.

 

Needed another timeline to scrub the playhead of `MANIPULATOR`. Then I'm using a `delayedCall` to get the ball rolling

 

const SCRUB = gsap.to(MANIPULATOR, {
  totalTime: 0,
  duration: 0,
  paused: true,
  ease: 'none'
})


SCRUB.vars.totalTime = 4
SCRUB.invalidate().restart()

gsap.delayedCall(2, () => MANIPULATOR.play())

There is likely a cleaner way to do this. The idea being that I'm going to be scrubbing a timeline composed of a few timelines and need to show the current state. I'll keep tinkering 🤓

Link to comment
Share on other sites

  • Solution

Hey jh3y,

 

You can force it to render like this.

MANIPULATOR.progress(1).progress(0);

 

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

 

  • Like 2
Link to comment
Share on other sites

Yes!! `progress` was what I was looking for!

 

Thank you for reminding me of that one @OSUblake 🙏

 

\ʕ •ᴥ•ʔ/

  • Like 2
Link to comment
Share on other sites

I love that you don't mind asking questions here, @jh3y! It shows that advanced users don't need to be bashful. 🙌

 

Just a few thoughts/comments for what it's worth (maybe not much)

  1. You never need to set paused: false or delay: 0 on a timeline/tween (those are the defaults)
  2. Instead of using an onComplete to clearProps, you could just put that in your final tween. No biggie - maybe it was easier for you workflow-wise to use the onComplete.
  3. I'm not sure what you were trying to accomplish by using '- 0.5' as the position parameter. As it stands now, that's merely acting as a label (a very oddly-named one): 
    // since it's  a string...
    TL.add(DIGI_TIMELINE, '- 0.5')
    
    // ...it's acting the same as something like:
    TL.add(DIGI_TIMELINE, 'someLabel')
    
    // if you want it -0.5 seconds from the end:
    TL.add(DIGI_TIMELINE, "-=0.5")

    You could simplify things:

    // OLD
    const DIGI_TIMELINE = gsap.timeline({
      paused: false,
      onComplete: () => gsap.set(DIGIT, { clearProps: true })
    })
    DIGI_TIMELINE
      .set(DIGIT, {
      yPercent: 0,
    })
      .to(DIGIT, {
      yPercent: 100,
      delay: i,
      duration: 1
    })
      .to(DIGIT, {
      yPercent: 200,
      duration: 1
    })
    TL.add(DIGI_TIMELINE, '- 0.5');
    
    // NEW
    const DIGI_TIMELINE = gsap.timeline()
      .set(DIGIT, {
        yPercent: 0,
      })
      .to(DIGIT, {
        yPercent: 200, // since it's linear, there's no need for the prior tween to 100
        duration: 2,
        clearProps: "all"
      }, i)
    TL.add(DIGI_TIMELINE, 0);

     

  4. FYI, that tween wasn't rendering at the initial state because you had set paused: true on the timeline thus that's an indicator to GSAP that you don't want it rendering right now. But like Blake said, you can just .progress(1).progress(0) to force the playhead/render. Plus that also forces everything to get initialized up-front, so it'll be able to skip those tasks when it runs.

We love seeing all the cool stuff you've been putting out there and tweeting about. Keep up the good work inspiring people!

  • Like 1
Link to comment
Share on other sites

19 hours ago, GreenSock said:

I love that you don't mind asking questions here, @jh3y! It shows that advanced users don't need to be bashful. 🙌

 

Just a few thoughts/comments for what it's worth (maybe not much)

  1. You never need to set paused: false or delay: 0 on a timeline/tween (those are the defaults)
  2. Instead of using an onComplete to clearProps, you could just put that in your final tween. No biggie - maybe it was easier for you workflow-wise to use the onComplete.
  3. I'm not sure what you were trying to accomplish by using '- 0.5' as the position parameter. As it stands now, that's merely acting as a label (a very oddly-named one): 
    // since it's  a string...
    TL.add(DIGI_TIMELINE, '- 0.5')
    
    // ...it's acting the same as something like:
    TL.add(DIGI_TIMELINE, 'someLabel')
    
    // if you want it -0.5 seconds from the end:
    TL.add(DIGI_TIMELINE, "-=0.5")

    You could simplify things:

    // OLD
    const DIGI_TIMELINE = gsap.timeline({
      paused: false,
      onComplete: () => gsap.set(DIGIT, { clearProps: true })
    })
    DIGI_TIMELINE
      .set(DIGIT, {
      yPercent: 0,
    })
      .to(DIGIT, {
      yPercent: 100,
      delay: i,
      duration: 1
    })
      .to(DIGIT, {
      yPercent: 200,
      duration: 1
    })
    TL.add(DIGI_TIMELINE, '- 0.5');
    
    // NEW
    const DIGI_TIMELINE = gsap.timeline()
      .set(DIGIT, {
        yPercent: 0,
      })
      .to(DIGIT, {
        yPercent: 200, // since it's linear, there's no need for the prior tween to 100
        duration: 2,
        clearProps: "all"
      }, i)
    TL.add(DIGI_TIMELINE, 0);

     

  4. FYI, that tween wasn't rendering at the initial state because you had set paused: true on the timeline thus that's an indicator to GSAP that you don't want it rendering right now. But like Blake said, you can just .progress(1).progress(0) to force the playhead/render. Plus that also forces everything to get initialized up-front, so it'll be able to skip those tasks when it runs.

We love seeing all the cool stuff you've been putting out there and tweeting about. Keep up the good work inspiring people!

Not at all! I love some humble pie ☺️ It's the best way to learn. I'll always take suggestions and discussions.

 

1. Totally! 💯 Sorry, the code is a little rough and I'd left them in as I was playing with variations trying to get it working with things like `delay: 2` etc. Sorry for that!

2. Ahh yes, that would make sense too. I guess in my head I see each timeline like its own little block/animation component 😅

3. Yes! Love this. Now the position param threw me to start with because I was in a bit of a rush. Without a label it broke and of course I needed `0` 😅. The simplified version is much nicer and again this was kind of my bad, sorry. I'd left it rough as I was playing with different eases for the effect I want. Ahh, I see that `clearProps` usage now and this is much nicer too!

4. This is a rad tip. Kinda shows the magnitude of what GSAP covers because although I've used `progress` in the past, I'd completely forgotten about it haha. Trying to spend more time documenting things I make this year for this reason 😅

 

Appreciate the kind words massively! I'll keep creating for sure. Love sharing and challenging ha. If I don't hear from you, have a great weekend!

 

\ʕ •ᴥ•ʔ/

  • Like 3
Link to comment
Share on other sites

23 hours ago, OSUblake said:

Hey jh3y,

 

You can force it to render like this.

MANIPULATOR.progress(1).progress(0);


Ah! This is a good knowledge snippet to store away. 🧠

  • Like 1
Link to comment
Share on other sites

Always nice to see you in the forums Jhey.  ☺️

  • Like 1
Link to comment
Share on other sites

Hey y'all!

 

On 1/28/2022 at 4:34 PM, Cassie said:

Always nice to see you in the forums Jhey.  ☺️

Thank you @Cassie 🙏

 

One more query on this demo 😁 I've got it pretty much there where I want it. There's one thing I can't seem to make work nicely. It's setting `aria-hidden` on the numbers as they slide in and out 🤔

 

I've been trying this but it is a little inconsistent for me. Sometimes it works, sometimes it leaves some with the wrong values. Would it make sense to maybe do this outside of the timeline and work out which digits need the attribute applied when doing the scrub instead? (Maybe I've answered my own question there 😅)

 

const DIGI_TIMELINE = gsap
      .timeline()
      .set(DIGIT, {
        yPercent: 0,
        attr: {
          'aria-hidden': 'true',
        },
      })
      .to(DIGIT, {
        yPercent: 100,
        delay: i * COEFF,
        duration: 1,
        onStart: () => {
          gsap.set(DIGIT, {
            attr: {
              'aria-hidden': 'false',
            },
          })
        },
      })
      .to(DIGIT, {
        delay: COEFF - 1,
        yPercent: 200,
        duration: 1,
        clearProps: 'all',
        onStart: () => {
          gsap.set(DIGIT, {
            attr: {
              'aria-hidden': 'true',
            },
          })
        },
      })

Anyways, hope you've all had an awesome weekend. Here's the demo! It's part of a series I'm doing where it's about debunking Instagram Reels/TikTok where someone shows how easy it is to make a design in Figma but it doesn't show the reality on the dev side of making said thing 😅

 

See the Pen XWzmGad by jh3y (@jh3y) on CodePen

 

ʕ´•ᴥ•`ʔ

 

Link to comment
Share on other sites

Hello!

FWIW I've made a counter a little like this before for a website that was super hot on a11y. I ended up outputting the value to a visually hidden div which was a live (polite) aria region.

 https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/ARIA_Live_Regions

Reason being that changes to aria-hidden/aria-true don't get announced and spans were getting read out one by one rather than as a whole number.

I'll poke this demo around a bit tomorrow though if no one gets to it, because at a glance it looks like it should be working fine! 👀

 

  • Like 1
  • Thanks 1
Link to comment
Share on other sites

Hey! 

 

Actually, this solution makes a lot more sense. Thank you. I wanted to cover it for the demo/article and updating in the `inputs` callback would fit better I think 🙏

 

Thanks! \ʕ •ᴥ•ʔ/

  • Like 1
Link to comment
Share on other sites

Hooray! Happy to help. ☺️

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