summaryrefslogtreecommitdiffstats
path: root/docs/performance/scroll-linked_effects.md
diff options
context:
space:
mode:
Diffstat (limited to 'docs/performance/scroll-linked_effects.md')
-rw-r--r--docs/performance/scroll-linked_effects.md177
1 files changed, 177 insertions, 0 deletions
diff --git a/docs/performance/scroll-linked_effects.md b/docs/performance/scroll-linked_effects.md
new file mode 100644
index 0000000000..d17740f20b
--- /dev/null
+++ b/docs/performance/scroll-linked_effects.md
@@ -0,0 +1,177 @@
+# Scroll-linked effects
+
+The definition of a scroll-linked effect is an effect implemented on a
+webpage where something changes based on the scroll position, for
+example updating a positioning property with the aim of producing a
+parallax scrolling effect. This article discusses scroll-linked effects,
+their effect on performance, related tools, and possible mitigation
+techniques.
+
+## Scrolling effects explained
+
+Often scrolling effects are implemented by listening for the `scroll`
+event and then updating elements on the page in some way (usually the
+CSS
+[`position`]((https://developer.mozilla.org/en-US/docs/Web/CSS/position "The position CSS property sets how an element is positioned in a document. The top, right, bottom, and left properties determine the final location of positioned elements.")
+or
+[`transform`](https://developer.mozilla.org/en-US/docs/Web/CSS/transform "The transform CSS property lets you rotate, scale, skew, or translate an element. It modifies the coordinate space of the CSS visual formatting model.")
+property.) You can find a sampling of such effects at [CSS Scroll API:
+Use
+Cases](https://github.com/RByers/css-houdini-drafts/blob/master/css-scroll-api/UseCases.md).
+
+These effects work well in browsers where the scrolling is done
+synchronously on the browser's main thread. However, most browsers now
+support some sort of asynchronous scrolling in order to provide a
+consistent 60 frames per second experience to the user. 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.
+
+``` {.brush: .html}
+<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:
+
+``` {.brush: .html}
+<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
+
+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.
+
+``` {.brush: .html}
+<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" class="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.
+
+``` {.brush: .html}
+<body style="height: 5000px">
+ <style>
+ body, /* blink currently has bug that requires declaration on `body` */
+ html {
+ scroll-snap-type: y proximity;
+ }
+ .snaptarget {
+ scroll-snap-align: start;
+ position: relative;
+ top: 200px;
+ height: 200px;
+ background-color: green;
+ }
+ </style>
+ <div class="snaptarget"></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](https://staktrace.com/spout/entry.php?id=834) 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](https://w3c.github.io/web-animations/): A new API
+ for precisely controlling web animations in JavaScript, with an
+ [additional
+ proposal](https://wiki.mozilla.org/Platform/Layout/Extended_Timelines)
+ to map scroll position to time and use that as a timeline for the
+ animation.
+- [CompositorWorker](https://docs.google.com/document/d/18GGuTRGnafai17PDWjCHHAvFRsCfYUDYsi720sVPkws/edit?pli=1#heading=h.iy9r1phg1ux4):
+ Allows JavaScript to be run on the compositor thread in small
+ chunks, provided it doesn't cause the framerate to drop.
+- [Scroll
+ Customization](https://docs.google.com/document/d/1VnvAqeWFG9JFZfgG5evBqrLGDZYRE5w6G5jEDORekPY/edit?pli=1):
+ 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](https://lists.w3.org/Archives/Public/public-houdini/)
+mailing list.