98 votes

des événements de défilement: requestAnimationFrame VS requestIdleCallback VS passive des écouteurs d'événement

Comme nous le savons, il est souvent conseillé de antirebond de faire défiler les auditeurs de sorte que le UX est mieux lorsque l'utilisateur fait défiler.

Cependant, j'ai souvent trouvé des bibliothèques et des articles où des gens influents, comme Paul Lewis recommandons l'utilisation d' requestAnimationFrame. Cependant, comme la plate-forme web progresser rapidement, il pourrait être possible que certains conseils obtenir obsolète au fil du temps.

Le problème que je vois est qu'il est très différent des cas d'utilisation pour la manipulation des événements de défilement, comme la construction d'une parallaxe site web, ou de la manipulation infini défilement et la pagination.

Je vois 3 les principaux outils qui peuvent faire une différence en terme de UX:

Donc, j'aimerais savoir, par cas d'utilisation (je n'ai que 2 mais vous pouvez venir avec les autres), ce genre d'outil dois-je utiliser dès maintenant pour avoir une très bonne défilement de l'expérience?

Pour être plus précis, ma question principale serait plus liée à l'infini défilement des vues et de la pagination (qui n'ont généralement pas de déclencher des animations visuelles, mais nous voulons un bon défilement de l'expérience), est-il préférable de remplacer requestAnimationFrame avec un combo de requestIdleCallback + passif de défilement gestionnaire d'événement ? Je me demande aussi si elle a un sens pour utiliser requestIdleCallback pour l'appel d'une API ou de la manipulation de l'API de réponse à laisser le défilement fonctionne mieux ou est-ce quelque chose que le navigateur peut déjà gérer pour nous?

264voto

alexander farkas Points 3450

Bien que cette question est un peu plus âgé, je veux répondre à ça parce que je vois souvent des scripts, où beaucoup de ces techniques sont utilisées à mauvais escient.

En général, tous votre demande d'outils (rAF, rIC et passive auditeurs) sont d'excellents outils et de ne pas disparaître bientôt. Mais vous devez savoir pourquoi les utiliser.

Avant de commencer: Dans le cas où vous générez de défilement synchronisé/défilement lié effets comme des effets de parallax/collant les éléments, la limitation de l'aide d' rIC, setTimeout n'a pas de sens parce que vous voulez réagir immédiatement.

requestAnimationFrame

rAF vous donne le point à l'intérieur du cadre de vie, cycle de droit avant que le navigateur veut calculer le nouveau style et mise en page du document. C'est pourquoi il est parfait à utiliser pour les animations. D'abord il ne sera pas appelé plus souvent ou moins souvent que le navigateur calcule page (à droite de la fréquence). Deuxièmement, elle est appelée juste avant que le navigateur ne calculer la mise en page (bon moment). En fait, à l'aide de rAF pour toutes les modifications de mise en page (DOM ou CSSOM changements) fait beaucoup de sens. rAF est synchronisée avec la V-SYNC comme toute autre disposition rendu les choses liées dans le navigateur.

à l'aide de rAF pour les gaz/anti-rebond

La valeur par défaut exemple de Paul Lewis ressemble à ceci:

var scheduledAnimationFrame;
function readAndUpdatePage(){
  console.log('read and update');
  scheduledAnimationFrame = false;
}

function onScroll (evt) {

  // Store the scroll value for laterz.
  lastScrollY = window.scrollY;

  // Prevent multiple rAF callbacks.
  if (scheduledAnimationFrame){
    return;
  }

  scheduledAnimationFrame = true;
  requestAnimationFrame(readAndUpdatePage);
}

window.addEventListener('scroll', onScroll);

Ce motif est très souvent utilisé, copié, bien qu'il semble à peu jusqu'pas de sens dans la pratique. (Et je me demande pourquoi aucun développeur ne voit ce problème évident.) En général, théoriquement, il fait beaucoup de sens à gaz tout au moins de la rAF, car il n'est pas logique de demander des modifications de mise en page à partir du navigateur le plus souvent le navigateur rend la mise en page.

Cependant, l' scroll événement est déclenché chaque fois que le navigateur rend un parchemin de changement de position. Cela signifie un scroll événement est synchronisé avec le rendu de la page. Littéralement la même chose qu' rAF est de vous donner. Cela signifie qu'il ne ferait aucun sens de l'accélérateur quelque chose par quelque chose, c'est déjà étranglé par exactement la même chose, par définition.

Dans la pratique, vous pouvez vérifier ce que je viens de dire par l'ajout d'un console.log et de vérifier comment souvent ce motif "empêche plusieurs rAF rappels" (réponse n'en est aucune, sinon, ce serait un bug lié au navigateur).

  // Prevent multiple rAF callbacks.
  if (scheduledAnimationFrame){
    console.log('prevented rAF callback');
    return;
  }

Comme vous le verrez, ce code n'est jamais exécutée, il est tout simplement mort de code.

Mais il y a une tendance similaire qui font sens pour une raison différente. Il ressemble à ceci:

//declare box, element, pos
function writeLayout(){
    element.classList.add('is-foo');
}

window.addEventListener('scroll', ()=> {
    box = element.getBoundingClientRect();

    if(box.top > pos){
        requestAnimationFrame(writeLayout);
    }
});

Avec ce modèle, vous pouvez réduire ou même de supprimer la disposition en se débattant. L'idée est simple: à l'intérieur de votre défiler écouteur vous lire de mise en page et de décider si vous avez besoin de modifier le DOM et ensuite d'appeler la fonction qui modifie le DOM à l'aide de la rAF. Pourquoi est-ce utile? L' rAF permet de s'assurer que vous placez votre mise en page à l'annulation (à l'ende de l'image). Cela signifie tout autre code qui est appelé à l'intérieur du même cadre de travaux sur une disposition valide et peut fonctionner avec de super rapides à disposition de méthodes de lecture.

Ce modèle est en fait si grande, que je suggère la méthode helper suivante (écrit en ES5):

/**
 * @param fn {Function}
 * @param [throttle] {Boolean|undefined}
 * @return {Function}
 *
 * @example
 * //generate rAFed function
 * jQuery.fn.addClassRaf = bindRaf(jQuery.fn.addClass);
 *
 * //use rAFed function
 * $('div').addClassRaf('is-stuck');
 */
function bindRaf(fn, throttle){
    var isRunning, that, args;

    var run = function(){
        isRunning = false;
        fn.apply(that, args);
    };

    return function(){
        that = this;
        args = arguments;

        if(isRunning && throttle){return;}

        isRunning = true;
        requestAnimationFrame(run);
    };
}

requestIdleCallback

Est à partir de l'API similaire à l' rAF mais donne quelque chose de totalement différent. Il vous donne un peu de temps de repos à l'intérieur d'un cadre. (Normalement le point après que le navigateur a calculé mise en page et fait de la peinture, mais il y a encore un peu de temps jusqu'à ce que le v-sync qui se passe.) Même si la page est lag de la vue des utilisateurs, il y a peut être des images, où le navigateur est au ralenti. Bien qu' rIC peut vous donner de max. 50ms. La plupart du temps, vous avez entre 0,5 et 10 ms pour accomplir votre tâche. Dû au fait, à quel point dans le cadre du cycle de vie rIC rappels sont appelés, vous ne devez pas modifier le DOM (utiliser rAF pour cette).

À la fin, il fait beaucoup de sens à gaz, scroll écouteur pour lazyloading, infini défilement et telles que l'utilisation d' rIC. Pour ces types d'interfaces utilisateur vous pouvez même l'accélérateur plus et ajouter un setTimeout , en face de lui. (si vous ne 100ms attendre et puis un rIC) (par exemple la Vie d'un anti-rebond et de gaz.)

Voici également un article sur rAF, qui comprend deux schémas qui pourraient aider à comprendre les différents points à l'intérieur d'un "cadre" cycle de vie".

Passive écouteur d'événement

Passive des écouteurs d'événement ont été inventés pour améliorer le défilement de la performance. Les navigateurs modernes déplacé en page de défilement (rendu) dans le thread principal de la composition du fil. (voir https://hacks.mozilla.org/2016/02/smoother-scrolling-in-firefox-46-with-apz/)

Mais il y a des événements qui produisent de défilement, ce qui peut être évité par le script (ce qui arrive dans le thread principal et ne peut donc revenir à l'amélioration de la performance).

Ce qui signifie que dès que l'un de ces événements les auditeurs sont tenus, le navigateur n'a plus à attendre de ces auditeur à être exécuté avant que le navigateur peut calculer le défilement. Ces événements sont principalement touchstart, touchmove, touchend, wheel et, en théorie, à un certain degré, keypress et keydown. L' scroll événement en lui-même n'est pas l'un de ces événements. L' scroll événement n'a aucune action par défaut, qui peut être prévenue par le script.

Cela signifie que si vous n'utilisez pas l' preventDefault votre touchstart, touchmove, touchend et/ou wheel, toujours utiliser la passivité des écouteurs d'événement et vous devriez être bien.

Dans le cas où vous utilisez preventDefault, vérifier si vous pouvez le remplacer par du CSS touch-action de la propriété ou à la baisse, au moins, dans votre arborescence DOM (par exemple aucun cas, la délégation de ces événements). Dans le cas d' wheel d'auditeurs, vous pourriez être en mesure de lier/délier sur mouseenter/mouseleave.

Dans le cas de tout autre événement: Il ne fait pas de sens pour une utilisation passive des écouteurs d'événement pour améliorer les performances. Le plus important à noter: L' scroll événement ne peut pas être annulée et, par conséquent, il ne jamais fait sens pour l'utilisation passive des écouteurs d'événement pour scroll.

Dans le cas d'un infini défilement vous n'avez pas besoin d' touchmove, vous avez seulement besoin d' scroll, de manière passive, les écouteurs d'événement ne sont pas encore applicables.

Résumé

Pour répondre à votre question

  • pour lazyloading, une infinité de vue de l'utilisation d'une combinaison d' setTimeout + requestIdleCallback pour vos écouteurs d'événements et d'utiliser rAF pour toute mise en page, écrit DOM (mutations).
  • pour l'instant les effets de toujours utiliser rAF pour toute mise en page, écrit DOM (mutations).

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X