Cette traduction est incomplète. Aidez à traduire cet article depuis l'anglais.

La définition d'un effet lié au scroll est un effet implémenté dans une page web où quelque chose change en fonction de la position de scroll, par exemple une propriété de position, dans le but de créer un effet parallax. Cet article parle des effets liés au scroll, leur répercussion sur les perfomances, les outils connexes, et les possibles atténuations techniques.

Effets de scroll expliqués

Souvent, les effets de scroll sont implémentés en écoutant l'événement scroll et ainsi, en quelque sorte, mettre à jour les éléments de la page  (généralement les propriété CSS position ou transform.) Vous pouvez trouver un exemple de ces effets ici : CSS Scroll API: Use Cases.

Ces effets fonctionnent très bien avec les navigateurs ou les scroll est fait de manière synchrone sur le thread principal du navigateur. Toutefois, la plupart des navigateurs supportent actuellement une sorte de scroll asynchrone afin de prroposer une expérience cohérente en 60 images par secondes à l'utilisateur. In the asynchronous scrolling model, the visual scroll position is updated in the compositor thread and is visible to the user before the scroll event is updated in the DOM and fired on the main thread. This means that the effects implemented will lag a little bit behind what the user sees the scroll position to be. This can cause the effect to be laggy, janky, or jittery — in short, something we want to avoid.

Below are a couple of examples of effects that would not work well with asynchronous scrolling, along with equivalent versions that would work well:

Example 1: Sticky positioning

Here is an implementation of a sticky-positioning effect, where the "toolbar" div will stick to the top of the screen as you scroll down.

<body style="height: 5000px" onscroll="document.getElementById('toolbar').style.top = Math.max(100, window.scrollY) + 'px'">
 <div id="toolbar" style="position: absolute; top: 100px; width: 100px; height: 20px; background-color: green"></div>
</body>

This implementation of sticky positioning relies on the scroll event listener to reposition the "toolbar" div. As the scroll event listener runs in the JavaScript on the browser's main thread, it will be asynchronous relative to the user-visible scrolling. Therefore, with asynchronous scrolling, the event handler will be delayed relative to the user-visible scroll, and so the div will not stay visually fixed as intended. Instead, it will move with the user's scrolling, and then "snap" back into position when the scroll event handler runs. This constant moving and snapping will result in a jittery visual effect. One way to implement this without the scroll event listener is to use the CSS property designed for this purpose:

<body style="height: 5000px">
 <div id="toolbar" style="position: sticky; top: 0px; margin-top: 100px; width: 100px; height: 20px; background-color: green"></div>
</body>

This version works well with asynchronous scrolling because position of the "toolbar" div is updated by the browser as the user scrolls.

Example 2: Scroll snapping

This feature has been removed from the Web standards. Though some browsers may still support it, it is in the process of being dropped. Avoid using it and update existing code if possible. https://developer.mozilla.org/en-US/docs/Web/CSS/scroll-snap-coordinate#Browser_compatibility

Below is an implementation of scroll snapping, where the scroll position snaps to a particular destination when the user's scrolling stops near that destination.

<body style="height: 5000px">
 <script>
    function snap(destination) {
        if (Math.abs(destination - window.scrollY) < 3) {
            scrollTo(window.scrollX, destination);
        } else if (Math.abs(destination - window.scrollY) < 200) {
            scrollTo(window.scrollX, window.scrollY + ((destination - window.scrollY) / 2));
            setTimeout(snap, 20, destination);
        }
    }
    var timeoutId = null;
    addEventListener("scroll", function() {
        if (timeoutId) clearTimeout(timeoutId);
        timeoutId = setTimeout(snap, 200, parseInt(document.getElementById('snaptarget').style.top));
    }, true);
 </script>
 <div id="snaptarget" style="position: relative; top: 200px; width: 100%; height: 200px; background-color: green"></div>
</body>

In this example, there is a scroll event listener which detects if the scroll position is within 200 pixels of the top of the "snaptarget" div. If it is, then it triggers an animation to "snap" the scroll position to the top of the div. As this animation is driven by JavaScript on the browser's main thread, it can be interrupted by other JavaScript running in other tabs or other windows. Therefore, the animation can end up looking janky and not as smooth as intended. Instead, using the CSS snap-points property will allow the browser to run the animation asynchronously, providing a smooth visual effect to the user.

<body style="height: 5000px">
 <style>
    body {
        scroll-snap-type: proximity;
        scroll-snap-destination: 0 0;
    }
    #snaptarget {
        scroll-snap-coordinate: 0 -8px;
    }
 </style>
 <div id="snaptarget" style="position: relative; top: 200px; width: 100%; height: 200px; background-color: green"></div>
</body>

This version can work smoothly in the browser even if there is slow-running Javascript on the browser's main thread.

Other effects

In many cases, scroll-linked effects can be reimplemented using CSS and made to run on the compositor thread. However, in some cases the current APIs offered by the browser do not allow this. In all cases, however, Firefox will display a warning to the developer console (starting in version 46) if it detects the presence of a scroll-linked effect on a page. Pages that use scrolling effects without listening for scroll events in JavaScript will not get this warning. See the Asynchronous scrolling in Firefox blog post for some more examples of effects that can be implemented using CSS to avoid jank.

Future improvements

Going forward, we would like to support more effects in the compositor. In order to do so, we need you (yes, you!) to tell us more about the kinds of scroll-linked effects you are trying to implement, so that we can find good ways to support them in the compositor. Currently there are a few proposals for APIs that would allow such effects, and they all have their advantages and disadvantages. The proposals currently under consideration are:

  • Web Animations: A new API for precisely controlling web animations in JavaScript, with an additional proposal to map scroll position to time and use that as a timeline for the animation.
  • CompositorWorker: Allows JavaScript to be run on the compositor thread in small chunks, provided it doesn't cause the framerate to drop.
  • Scroll Customization: Introduces a new API for content to dictate how a scroll delta is applied and consumed. As of this writing, Mozilla does not plan to support this proposal, but it is included for completeness.

Call to action

If you have thoughts or opinions on:

  • Any of the above proposals in the context of scroll-linked effects.
  • Scroll-linked effects you are trying to implement.
  • Any other related issues or ideas.

Please get in touch with us! You can join the discussion on the public-houdini mailing list.

Étiquettes et contributeurs liés au document

 Contributeurs à cette page : Fluorinx
 Dernière mise à jour par : Fluorinx,