7
\$\begingroup\$

I've recently made a slightly similar question with respect to the rendering performance, however my question this time is a bit different and with respect to which kind of JavaScript approach is the "best". The previous question was with regard to which css approach gives the best performance on animations.

With "best" I mean:

  • Being the most "future proof" approach
  • Best performance w. respect to the given object/goal
  • Being less demanding for the device running the code and rendering the site

Admittedly I'm no professional when it comes this (at all), and therefore I'm asking in here.

The objective:

I have a position:fixed button that activates/shows the navigation menu on mobile devices. When scrolling down this button shrinks and when scrolling up again it enlarges (returns to the original size).

Approaches:

I've taken on several approaches for this, and a quick overview of approaches can be seen here:

  • Using Headroom.js to add/remove class to the element
  • Using requestAnimationFrame (rAF) to calculate scroll and thereby toggle class
  • Using setInterval to calculate scroll and toggle class (same functions as in the rAF, just with setInterval instead, which is activated on scroll.
  • A combination of setInterval (which is activated on scroll) to calculate the scroll and toggling class (enlarging/shrinking) button using rAF.

So far my findings are somewhat inconclusive, but it seems that the setInterval(), which is activated on scroll by using a var didScroll = true approach, is at least more performant than headroom.js for my application.

The actual animation only happens whenever the user switches direction (going from scrolling down to up and vice versa), which leads me to think that using a 100% rAF approach might be too much, as the if-statements is checked a lot of times.

To the above statement saying that using rAF would run the if-statements a lot i forgot that my rAF solution would only check the if-statements if the window had scrolled. Although this means that the rAF solution would constantly check if (lastPosition == window.pageYOffset). Here it should also be noted that the setInterval checks every 100ms if didScroll = true

The questions:

  1. Do you think it's better to add the class using rAF as in the 2nd code, or is this "overkill" ?
  2. In case of using an approach of pure .scroll()/window.onsroll how much would the if-statements effect the performance, as they would run a lot - or put another way, are these if-statements computational "heavy" compared to others?
  3. Any suggestions to optimize the code - or suggestions on completely different approaches?

The code:

Beneath you can see the code for the 2 approaches which I'm currently considering the most (as the topic suggests).

Using setInterval which only runs on scroll:

var lastPosition = 0;
var didScroll = false;
var offset = 10;
var changed = 0;
var direction = 0;
var size = 1;
var thisClass = "shrink";
var nav = jQuery("#shownav");

window.onscroll = function(){ 
didScroll = true;
};

setInterval(function() {
    if(didScroll) {
        didScroll = false;

var scroll = window.pageYOffset;

if(direction == 0 && scroll > lastPosition){
changed = scroll+offset;
direction = 1;
} else if (direction == 1 && scroll < lastPosition){
changed = scroll-offset;
direction = 0;
}

 if(size == 1 && direction == 1 && scroll > changed){
 $(nav).addClass(thisClass);
 size = 0;
 } else if (size == 0 && direction == 0 && scroll < changed){
 $(nav).removeClass(thisClass);
 size = 1;
 }
lastPosition = scroll;
  }
  }, 100);

Using a combination of setInterval activated on scroll and rAF to activate the animation:

var lastPosition = 0;
var didScroll = false;
var offset = 10;
var changed = 0;
var direction = 0;
var size = 1;
var thisClass = "shrink";
var nav = jQuery("#shownav");

window.onscroll = function(){ 
didScroll = true;
};

setInterval(function() {
    if(didScroll) {
        didScroll = false;


var scroll = window.pageYOffset;

if(direction == 0 && scroll > lastPosition){
changed = scroll+offset;
direction = 1;
} else if (direction == 1 && scroll < lastPosition){
changed = scroll-offset;
direction = 0;
}

 if(size == 1 && direction == 1 && scroll > changed){
window.requestAnimationFrame(shrink);
 } else if (size == 0 && direction == 0 && scroll < changed){
window.requestAnimationFrame(enlarge);
 }
lastPosition = scroll;
  };
  }, 100);
function shrink(){
 $(nav).addClass(thisClass);
 size = 0;
};
function enlarge(){
 $(nav).removeClass(thisClass);
 size = 1;
};

Note that the requestAnimationFrame is based on the polyfill by Paul Irish / Erik Möller and can be found in this gist.

Note: the offset variable as well as the direction is included to ensure that the user scrolls at least 10px from the spot in which they changed direction, before toggling the class.

\$\endgroup\$
1
\$\begingroup\$

Well, this is slightly embarrassing as it seems that I hadn't collected all relevant information first - but it might benefit others to know (which is partly why I'm posting this as an answer).

In my example above, when using rAF I'm adding a class to the element and handling the "animation" through css transition. However, what I didn't know is that apparently requestAnimationFrame doesn't do anything beneficial for css animations - at least according to this question/answer from stackexchange.

Therefore it seems that the 2nd code above is rendered rather redundant, and seemingly only results in extra code being called/used without any real benefit. Judging by this I'm going to use the setInterval (using setTimeout now, see edit below) approach for now, unless any further information becomes available which contradicts this conclusion.

Edit: In addition to this I also stumbled upon the following approach, using setTimeout instead of setInterval. Using this means that there won't be made a check every 0,1 second as in the setInterval method.

timeout = false;

window.onscroll = function () {
    if (!timeout) {
        timeout = setTimeout(function () {            
            clearTimeout(timeout);
            timeout = false;

                // functions

        }, 100);
    }
};

The credit for this method goes to Andrew for his answer in this stackexchange post

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.