Jump to content
Tom

ScrollTo maybe scrolling after onComplete

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: https://codepen.io/rgbjoy/pen/RGVLBK?editors=0011
 
How is this happening? Should I be asking quantum theorist?
 

Share this post


Link to post
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! :)

Share this post


Link to post
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

Share this post


Link to post
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

Share this post


Link to post
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...

Share this post


Link to post
Share on other sites

Yeah that works but just seems like a weird hack. But thanks again for all the help and taking the time.

Share this post


Link to post
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

Share this post


Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...

  • Recently Browsing   0 members

    No registered users viewing this page.