Jump to content

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

GSAP ScrollTrigger image sequence with horizontal scrubber

Recommended Posts

This has probably been asked before, and it's very likely I'm taking the wrong approach, but is it possible to make use of the ScrollTrigger mechanism to make a scrubber for an image sequence?



- There are additional notes on the pen that can't be seen in the sample here, so please visit CodePen for better understanding... hopefully.




See the Pen ZEOLNMP by shigimcp (@shigimcp) on CodePen

Link to comment
Share on other sites

Hey @shigimcp


This has actually been a frequently asked question in the past couple weeks.


These two threads might be of most interest to you:






Hope this helps. ( Love that pen of yours btw :D ) 





  • Like 4
Link to comment
Share on other sites

Thanks, @akapowl!


Those threads are actually what got me as far as my first post! They were a great help, THANKS!


Sooooo, I managed to get it working... sorta. 
I updated the [same] pen so you can check it out: 

See the Pen ZEOLNMP by shigimcp (@shigimcp) on CodePen


Only one snafu: updating the scroll position from the scrubber/slider and the jump buttons is a bit wonky.

  • scrubber/slider: the controls jump around because it's trying to update the scroll position for each slider increment
    - you can see this if you uncomment
    // mainAnim.scrollTrigger.scroll(scrollPos);
    (line 59) in the JS panel
    - maybe it'll help to delay the update until a mouseup event on the slider?
  • jump buttons: have to click twice to get the proper update values
    - having trouble tracking down why this is happening...
    - seems it has something to do with the same command as above
     (line 136) in the JS panel (if you comment this OUT, the jump buttons work as expected)


QUESTION: Is it possible to set .progress instead of setting .scroll?


ANY help is greatly appreciated!



p.s. Thanks for the pen love, Paul!
That's one of my pet projects I work on when I need to step away for a minute.
Little did I know what a hole I'd find myself in... LOL!

Link to comment
Share on other sites


Hey Shigi.


It took me quite some time to wrap my head around this, since there is a whole lot going on.

First things first:

You are loading scripts (and different versions of those) via the HTML section in your pen, as well as in the JS-Options of codepen. 

Try avoiding that to avoid issues. I deleted all the scripts loading in your HTML and transferred those files loading into the JS-Options of codepen instead. 

For example: an error is thrown, that the main.js file you load in your HTML is not accessable - I got rid off it.


Next thing is, I think your scrollPos calculation should consider the window height, because you'll notice on scrubbing the slider, your handle is off-position.


So I changed it to this instead:


(document.body.offsetHeight - window.innerHeight) * thisProgress


Another thing, that throws calculations off, especially those regarding button clicks is the default ease set to timelines / tweens, so I set ease to "none" on your ScrollTrigger timeline. Also, in this scenario, I would avoid using scrub: 0.5, or with any other value than 'true', because it will hinder your scrub-slider reaching the ends at the correct values (at least it did for me).



Going forth to the base functionality:

I am not 100% sure about this one, but I think the way you run that render function on scrubbing the slider, onUpdate of ScrollTrigger and on button click, will cause overwrites and result in problems when getting to the right values at the right time / event. 


I worked around this, by only running that render-functionality in the onUpdate-callback of your ScrollTrigger, and for the scrub-slider and the button-clicks, simply just using the scrollTo functionality of GSAP's ScrollTo-Plugin (to make sure ScrollTrigger gets updated with those functions), like so.


gsap.set(window, { scrollTo: (document.body.offsetHeight - window.innerHeight) * thisProgress });




All in all, this solution works pretty well with everything you have included so far.



See the Pen a95fe948ecd6a9805c333c6533980851 by akapowl (@akapowl) on CodePen



Hope this makes sense to you, and helps with where you want to go with this.






  • Like 4
Link to comment
Share on other sites



This. Is. BRILLIANT!!!


And you explained everything PERFECTLY! 
(It all seems so obvious now! DERP! LOL!)


This is especially helpful since this actually gives me more insight into my next steps, adding other triggered timelines.
(I'll probably be back with more questions. LOL!)


THANKS AGAIN for all your help!


p.s. I probably shouldn't share this, but I can't help myself... 
I literally got up and did a happy dance. I'm not sure how it came off, but the dog looks worried now.

  • Haha 2
Link to comment
Share on other sites

I applied your corrections to my pen...
It's beautiful! *** sniff! cry! sniff! sniff! ***

Link to comment
Share on other sites



I see, you already made some changes to that original pen of yours.


For future reference and better understanding of other users on what was discussed in this thread, it would be awesome, if you could leave your original pen in its original state. When you want to test changes, it would be great if you could fork it (by clicking the fork button bottom right in codepen), before applying/testing those changes.


I'm glad I could help. And you should throw that dog of yours a bone, so it knows you didn't go insane in front of the desk or something ;) 




  • Like 2
Link to comment
Share on other sites


Good point and duly noted, @akapowl!


I reverted the original pen back to the original code (I hoard versions like a rabid squirrel), and I forked the solution.


See the Pen YzWQLjv by shigimcp (@shigimcp) on CodePen


THANKS for the heads-up!


p.s. Gave the dog a treat and took a long walk, so she's good now. LOL!


  • Like 1
Link to comment
Share on other sites



Added play, pause, etc. buttons.

Just need to figure out how to slow down the animation.

Feel free to comment / offer pointers... PLEASE! LOL!


New [forked] pen (I'm learnin' @akapowl! 🤪)

See the Pen YzWENBP by shigimcp (@shigimcp) on CodePen




Link to comment
Share on other sites


Hey Shigi


Good job ;) !


What you could do, is to set the set the timescale of the timeline to whatever you like.

You could either do it before you apply all those click-functions with the same 'speed' for all, like so




or, if you wanted to have a different speed for the reverse, for example, you could set it differently for each of those click-functions, like so maybe


$('#play').click(function () { animTimeline.timeScale(0.15).play(); });
$('#pause').click(function () { animTimeline.timeScale(0.15).pause(); });
$('#resume').click(function () { animTimeline.timeScale(0.15).resume(); });
$('#reverse').click(function () { animTimeline.timeScale(2.0).reverse(); });
$('#restart').click(function () { animTimeline.timeScale(0.15).restart(); });



If you choose to set it differently for each, make sure to apply a timescale for every single one though, because I noticed otherwise, if one has no timescale applied, that one will not work properly.



Or at least, I thought, that was the case.

But the restart does sometimes restart the timeline and sometimes only just set it back to 0.


Any idea/explanation why this could be the case in this setup @ZachSaucier ?



...aaaaaand I already got it myself :D 

It was related to that .pause() I added in the onLeaveBack-callback of the scrollTrigger ( which you can read about further down this post... ).

To prevent overwrites of the instructions given to the timeline, I added a variable to the restart-click-function, so onLeaveBack it now checks if the restart button was clicked and only if it wasn't, it adds the .pause(). 

Hope I did not oversee anything this time around ...I'm out of coffee :D 




Another thing I noticed, is, when you hit the play button (or the other way round for the reverse button), wait for the end and then try to scroll, you will notice that the timeline will keep on playing, so whenever you scroll, it will go on playing to the end in whatever direction it is playing. 


I worked around this, by applying animTimeline.pause() in the onEnter- and the onLeaveBack-callback of your scrollTrigger.


Maybe there is a more clever way of doing this, but for me this works pretty well altogether.


Check it out:


See the Pen e8320f954e8fdb43dcb544878f4c3605 by akapowl (@akapowl) on CodePen




And to round things off, also note, that I changed your old syntax for this below to the new one


var animTimeline = new TimelineMax({


var animTimeline = gsap.timeline({





  • Like 4
Link to comment
Share on other sites

YOOOOOOOOOOOOO!!! @akapowl!!! You. Are. A GOD!!!


*As sung to OutKast's "Hey Ya!", "Shake it like a Polaroid picture!"


This earworm is my gift to you for all of your amazing help.
You are welcome.




THANK YOU for catching all of this! You're making catching up with GSAP 3 a BLAST!

I incorporated all of your upgrades, and it's working like a dream!


And just because I'm having a really good time learning about ScrollTrigger, I quickly slapped together a speech timeline and added a few frames to the animation just for kicks. 
NOTE: I'm convinced there's a more efficient way to deal with this using ScrollTrigger.create(); I just have to sit down for a second - yes, I'm dancing again - to look at the variables (triggerStart01 - 04).


(There! Earworm! It's yours forever now!)



See the Pen yLJKjdK by shigimcp (@shigimcp) on CodePen




  • Haha 1
Link to comment
Share on other sites

A more practical use of the same code (with some minor changes)...


See the Pen GRqdpmd by shigimcp (@shigimcp) on CodePen

I could probably add more bells n whistles to this one, but I think it's done.
Next step: make this work in React... 🤪


THANKS AGAIN, @akapowl!


EDIT: WAIT! MAKE IT RESPONSIVE! Yeah... responsive... 🤦‍♀️

  • Like 1
Link to comment
Share on other sites

It's responsive!
And it was much easier than I thought it would be!

ScrollTrigger.matchMedia() is EVERYTHING!!!

ISSUES... just because.

1. // ------------------------- CONTROLS: JUMP BUTTONS -------------------------

I cheated... [insert cheeky, cheese-eating grin here]

- It *looks* like the anims jump *directly* to the callouts, BUT if you increase the durations of these 2 tweens

gsap.to(window, { scrollTo: (document.body.offsetHeight - window.innerHeight) * thisProgress, duration: 0.5 });
gsap.to(window, { scrollTo: thisCalloutPos, duration: 0.25 });

you will be able to see the anims in between (if you jump from one end to the other).
i.e. the tween durations above are too fast to allow the middle CALLOUT anims to fully play out. #SneakySneaky

NOTE: it doesn't *always* work, but it's good about 95% of the time. 🤷‍♂️

2. // ========================= CALLOUTS =========================

I am *CONVINCED* that I can somehow consolidate/simplify the CALLOUTs; that's a lot of code for a pretty basic concept that is repeated save a few parameters...

I feel like I'm gonna break this just one more time. LOL!


See the Pen zYBjzQZ by shigimcp (@shigimcp) on CodePen

Link to comment
Share on other sites

13 hours ago, shigimcp said:

I am *CONVINCED* that I can somehow consolidate/simplify the CALLOUTs; that's a lot of code for a pretty basic concept that is repeated save a few parameters...

Check out my article about animating efficiently.

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.