Jump to content
Search Community

ScrollTo maybe scrolling after onComplete

Tom test
Moderator Tag

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

Here's the StackOverflow question before this post: http://stackoverflow.com/questions/39711783/the-window-detects-scrolling-inexplicably/39713742#39713742

 

I have a fairly simple function that detects a window scroll. Fires a timeout event (500ms) when the user stops scrolling. This ends then listener and timeout. I then animate(GSAP) the window to a certain point. Once the animation is complete, the listener is fired up again. Sometimes... a scroll is detected again, so the whole function is fired twice. Please look into console to see this happening.
 
code here:
 
    
    var timeout;
    var onScroll = function() {
      console.log('scrolling...');
      if(timeout) {
       clearTimeout(timeout);
       timeout = null;
      }
      
      timeout = setTimeout(function () {
        console.log('done scrolling... stop listening...');
        $(window).off( "scroll", onScroll);
        clearTimeout(timeout);
       timeout = null;
        
        // aniamte scroll
        console.log('start animating scroll...');
        TweenMax.to($(window), 0.1, {scrollTo:{y:500}, onComplete:function(){
          $(window).on( "scroll", onScroll);
          console.log('done animating scroll. Start litening again.');
        }});
        
      }, 500);
    }
    $(window).on( "scroll", onScroll);
 
example:
 
How is this happening? Should I be asking quantum theorist?
 

See the Pen RGVLBK?editors=0011 by rgbjoy (@rgbjoy) on CodePen

Link to comment
Share on other sites

Hello Tom and welcome to the GreenSock Forum!

 

This doesn't look like  a GSAP bug but has to do in the way you are binding your scroll event. You might want to adjust the code your using to do debounce so it fires less. Maybe also add some throttling to make sure the scroll event does not get fired more than once when running your code. Your using setTimeout() when you could be using requestAnimationFrame instead. GSAP uses requestAniamtionFrame when animating.

 

Please standby while we look into this, Thanks! :)

Link to comment
Share on other sites

Thanks for the demo.

 

I'm not 100% sure, but I'm doubtful that this is something due to ScrollToPlugin. It appears that somehow a scroll event is happening sometime after the scroll animation is finished. As a workaround it seems you can add a slight delay after the animation completes before re-assigning the scroll handler function like:

 

 

 TweenMax.to($(window), 0.1, {scrollTo:{y:500}, onComplete:function(){
      setTimeout(function(){
        
        $(window).on( "scroll", onScroll);
        console.log('done animating scroll. Start listening again.');
        
      }, 100)
      
    }});

 

http://codepen.io/GreenSock/pen/rrwNoO?editors=0011

 

If we find something we can fix we will let you know.

  • Like 2
Link to comment
Share on other sites

Tom, along with Carl's great advice.

 

The example in the link below shows a better way to limit firing of your scroll event by using requestAnimationFrame instead of using clearTimeout() with setTimeout() like you have above.

 

Scroll optimization with window.requestAnimationFrame:

 

https://developer.mozilla.org/en-US/docs/Web/Events/scroll

// Scroll optimization with window.requestAnimationFrame
var last_known_scroll_position = 0;
var ticking = false;

function doSomething(scroll_pos) {
  // do something with the scroll position
}

window.addEventListener('scroll', function(e) {
  last_known_scroll_position = window.scrollY;
  if (!ticking) {
    window.requestAnimationFrame(function() {
      doSomething(last_known_scroll_position);
      ticking = false;
    });
  }
  ticking = true;
});

Based on this technique here by Paul Lewis for debouncing and throttling the scroll event to prevent layout thrashing and jank using requestAnimationFrame. (it could also be used for the resize event):

 

http://www.html5rocks.com/en/tutorials/speed/animations/

 

:)

  • Like 1
Link to comment
Share on other sites

Thank you Carl and Jonathan for looking into this.

 

Just to clarify, I'm using the timeout in the tick to start animating only when the user stops scrolling for more than 500ms. Here's a fork using the requestAnimationFrame tech and using a different timeout approach. 

See the Pen zKzGzy?editors=0011 by rgbjoy (@rgbjoy) on CodePen

 

var scrollPos = 0;
var ticking = false;
var timeout;


function scrolling(scroll_pos) {
  console.log('Scrolling to ' + window.pageYOffset );
  
  clearTimeout( $.data( window, "scrollCheck" ) );
  $.data( window, "scrollCheck", setTimeout(function() {
    window.removeEventListener('scroll', tick);
    clearTimeout( $.data( window, "scrollCheck" ) );
    console.log('DONE scrolling. STOP listening.');
    
    // aniamte scroll
    console.log('Animating...');
    TweenMax.to($(window), 0.1, {scrollTo:{y:500}, onComplete:function(){
      window.addEventListener('scroll', tick);
      console.log('DONE animating. Start litening. Scrolled to: ' + window.pageYOffset );
    }});
    
  }, 500));
}


window.addEventListener('scroll', tick);


function tick() {
  scrollPos = window.scrollY;
  if (!ticking) {
    window.requestAnimationFrame(function() {
      scrolling(scrollPos);
      ticking = false;
    });
  }
  ticking = true;
}

I mean it seems like there's a weird thing going on. Maybe a rounding error that doesn't settle and moves the scrollbar again? I just expect onComplete to be the end of the road of my program. It's weird it happens on occasion as well...

Link to comment
Share on other sites

You could also try using requestAnimationFrame instead of that 100ms setTimeout()

 

So you could change this

setTimeout(function(){
     console.log('DONE animating. Start litening. Scrolled to: ' + window.pageYOffset );
     window.addEventListener('scroll', tick);
},100);

Into this using window.requestAnimationFrame

The below fires after last tick when onComplete is fired, so it fires in sync with the browser on the next repaint

window.requestAnimationFrame(function(){
     console.log('DONE animating. Start litening. Scrolled to: ' + window.pageYOffset );
     window.addEventListener('scroll', tick);
});

The above might work the same, but you will have to test.

 

But either way this weird behavior is more of how the scroll events are fired. Since there are a lot of inconsistent behavior when using the scroll callback in each browser which can cause it to be blocked in the UI, causing lag. That is why Carl's solution to add a 100ms using setTimeout() works, to account for the lag.

 

:)

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