Jump to content
GreenSock

martis

page scroll event vs ticker

Warning: Please note

This thread was started before GSAP 3 was released. Some information, especially the syntax, may be out of date for GSAP 3. Please see the GSAP 3 migration guide and release notes for more information about how to update the code to GSAP 3's syntax. 

Recommended Posts

Hey guys,

 

I currently have a page scroll event that is firing off a timeline update, but it seems like it is firing quite often.

 

I was wondering if someone had a more optimized solution that uses either ticker or delayedCall that utilizes the requestAnimationFrame to drive my timeline? I notice the Ticker only updates when the tween engine is in progress, I need the event to fire off whether or not the tween engine is currently running to check page scroll. Thanks!

 

onScroll:

// Setup Scroll Events
function setupScrollEvents() {
	$(window).scroll(function(event) {
			processTimeline();
        });
}

Vs something like...

// Setup Ticker Events
function setupTickerEvents() {
	TweenMax.delayedCall(0, checkIfPageScrolled);
}
Link to comment
Share on other sites

Hello.. I would opt for the scroll event versus the ticker event in this case. Since the ticker event will keep firing even when not scrolling... versus the scroll event, which would only fire when scrolling.

 

This way you are not firing events when they are not needed. Also you could use native Javascript like this:

// Setup Scroll Events
function setupScrollEvents() {
	processTimeline();
}

window.onscroll = setupScrollEvents;

Link to comment
Share on other sites

I'd be very careful with using onscroll events because they can be a major cause of "jank" as it sounds like you know quite well. The key here is to NOT update stuff on the screen inside the onscroll - you should only do that inside a requestAnimationFrame (or setTimeout() if requestAnimationFrame isn't available) to optimize performance. Since GSAP's ticker is driven by requestAnimationFrame by default, I'd opt for using that. It also ensures everything in synchronized with the rest of your animations. 

 

For those who don't know, the reason onscroll can be "evil" is because:

  1. it can fire a BUNCH of times (more than the frame rate) during scrolling. If the browser is only refreshing the screen 60 times per second, anything more frequent than that is utterly wasteful. 
  2. If you change DOM elements inside an onscroll, it causes the browser to have to redraw them. Redrawing them can force a style recalc and reflow which can alter the scroll position, causing an onscroll to fire. That's BAD because it's bouncing back and forth doing a bunch of extra work. 

jonathan's point is certainly valid - typically it's not ideal to constantly run code when it's not necessary, but this is a unique case where the alternative is worse. It's really not very expensive to run a function on each tick and do some very light conditional logic. Most of the time it'll be false anyway and you won't do any updates. So the added strain on the CPU is likely imperceptible. However, if you perform updates inside an onscroll, even though OVERALL that function may get called fewer times over the course of your user's interaction with your app, but the down side is that when you DO have to run those updates, it's much more expensive. 

 

The goal is to deliver the best experience to your end user, so if overall CPU usage increases 1% (just a guess) by adding a "tick" listener but you get 60fps buttery-smooth performance during the scrolls because they're only occurring when the screen updates, I personally think that's better than saving that 1% "tick" drain and getting 30fps or 20fps (or whatever) during the scrolls by using an onscroll handler. 

 

I'd recommend doing some tests of your own, but in my opinion, the "tick" listener would be a better option for you. 

  • Like 3
Link to comment
Share on other sites

Thanks for the detailed reply Jack,

 

I noticed the tick listener only fires when there is an active change in the tween engine...

 

Can you post a simple example of how to have a persistent tick listener, even if no engine update occurs?

Link to comment
Share on other sites

No no, the ticker should keep firing even when there are no tweens happening, as long as you've added a listener. It only "powers down" if there are no other listeners and there are no tweens. For example, this should give you a console log about 60 times per second:

TweenLite.ticker.addEventListener("tick", function() {
	console.log(TweenLite.ticker.time);
});

(regardless of whether or not there are any tweens happening). 

 

Is that not working for you? And are you using the latest version? 

Link to comment
Share on other sites

That worked, thanks Jack!

 

Gonna use this approach to prevent over flooding of scroll events :)

Link to comment
Share on other sites

  • 3 months later...

I've been doing some experimenting with using the tick event rather than scroll event due to the performance boosts mentioned above. I've discovered some interesting things which I'd like some opinions on if possible?

 

See the Pen Ejqhl by x0b (@x0b) on CodePen

 

The code pen above demonstrates using the tick event, the page scroll position is linked to the timeline time and once the page is automatically scrolled to the bottom using the scrollTO plugin the timeline 'onComplete' should fire. For convenience I've added some outputs at the bottom, you can see what time in seconds the timeline should be tweening to on the right, but it never gets there as you can see by the timeline's current time output on the left.

 

See the Pen Efjxp by x0b (@x0b) on CodePen

 

This next code pen uses the scroll event instead of the tick (the only difference) and works as intended. 

 

Any ideas why the timeline tween can never get to the end in the first version? Surely the tween should still get to the required position?

 

Incidentally, if you run the second scroll event version in the 'edit' mode on codepen, it sometimes doesn't complete. I am not sure why this is either.

 

Any help greatly appreciated :)

Link to comment
Share on other sites

The problem is in this logic:

TweenLite.ticker.addEventListener("tick", function() {
    scrollPos = getScrollTop() / scrollDivider;
    console.log(scrollPos);
    TweenMax.to(mainTimeline, 0.25, {"time": scrollPos, ease: Linear.easeNone});
});

You are saying that on every single tick, you want to make a brand new tween to scrollPos over .25 seconds. In a system that allowed for infinite number precision, this will never be able to reach scrollPos. It will forever be 0.25 seconds away from reaching it's end point.

 

Let's take the scrollPos calculation out of it and do this:

TweenLite.ticker.addEventListener("tick", function() {
    TweenMax.to(mainTimeline, 0.25, {time: 10, ease: Linear.easeNone});
});

You'll see the time shoot right up to ~9 and then ever so slowly increase one decimal place at a time to eventually stop at something like 9.999999999999998. At this point, at every single tick you are saying "Tween from 9.999999999999998 to 10 over 0.25 seconds". I'll guess and say the FPS is 60 so there should be around 15 ticks to go from 9.999999999999998 to 10. On the first update, the only way that time will increase is if the step is at least 0.000000000000001 (or rounds to that), but thats 50% of the range between the tween start and end, and we need to spread this out over 15 ticks. It's not going to ever reach 10 (well if you change tabs for a second and come back it should complete - the ticks will slow down and the time between ticks becomes great enough to allow the 0.000000000000001 increases).

 

I've no idea what this stuff is intended to do though (I never saw anything other than boxes moving, so not sure what is meant to be scrolling...) so I'm not sure what to suggest to fix it. I would definitely advise against creating a new tween every tick/scroll event though. If you're just after a more efficient scroll event, maybe something like throttle/debounce will help.

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