"This site appears to use a scroll-linked positioning effect. This may not work well with asynchronous panning"
The warning, I think, goes on to say:
...see https://developer.mozilla.org/docs/Mozilla/Performance/ScrollLinkedEffects for further details and to join the discussion on related tools and features!
But in case that page is unclear, here's the gist of it.
The idea of "asynchronous panning" is this: when the page is scrolled, the browser calls your scroll
handler, but it also asynchronously paints the page at the new scroll-point. This is done to make scrolling appear responsive (@ 60 FPS) even when the main thread is busy for more than 16ms.
This means that the effects your handler implements are not guaranteed to be in sync with the current scrolling position. I.e. the scrolling is smooth, but your divs rotate with a smaller FPS -- appearing janky, non-smooth. Update, missed the transition
effect in your example -- the rotation itself will also be smooth, but it might start later than the page starts to scroll.
I don't think you can implement the exact effect you have with the currently available technologies without having this problem.
example
(Note that to see the APZ in action, you need to be running the Firefox version with it enabled. In particular this requires Firefox to be running in a multiprocess ("e10s") mode, which is still not in the release builds at this time.)
window.onscroll = function() {
var wScroll = document.documentElement.scrollTop;
document.getElementById("gear-css").style.transform = 'rotate(' + Math.round(wScroll / 2) + 'deg)';
document.getElementById("gear-js") .style.transform = 'rotate(' + Math.round(wScroll / 2) + 'deg)';
document.getElementById("gear-js").textContent = leftPad(wScroll+'', '0', 4);
setTimeout(slowdown(500), 0);
};
function leftPad(s, ch, maxLen) { return ch.repeat(maxLen - s.length) + s; }
function slowdown(timeMs) {
return function() {
var start = Date.now();
var j = "";
for (var i = 0; (Date.now() - start < timeMs); i++)
j = j+(start+"")*i;
}
}
window.onload = function() {
for (let i = 0; i < 15; i++) {
var p = document.createElement("p");
p.innerText = `Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod
tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,
quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse
cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non
proident, sunt in culpa qui officia deserunt mollit anim id est laborum.`;
document.documentElement.appendChild(p);
}
}
#gear-css, #gear-js {
border: solid black 1px;
}
#gear-css {
transition: transform 1s ease-out
}
<div style="position: fixed; top: 0; right: 0; padding: 3em;">
<div id="gear-css">ooo</div>
<div id="gear-js">ooo</div>
</div>
I know that this question was asked some time ago, however, the performance issue still exists. Below is an alternative solution that does not rely upon a scrolling event listener. This minimises jank and lag caused by the separate scrolling thread common among web browsers, updating the css at regular intervals rather than when the window is scrolled. This means that the dev console warning will not display. I personally wouldn't be too worried about the warning or using scrolling events for small things like turning a gear or changing a css class, however, if the user experience directly relies upon the effect, it will destroy the usability of the page.
var gear;
var lastPosition;
var refreshRate = 60; // fps; reduce for less overhead
var animation = "rotate(*deg)"; // css style to apply [Wildcard is *]
var link = 0.5; //what to multiply the scrollTop by
function replace(){
if(lastPosition != document.body.scrollTop){ // Prevents unnecessary recursion
lastPosition = document.body.scrollTop; // updates the last position to current
gear.style.transform = animation.replace("*", Math.round(lastPosition * link)); // applies new style to the gear
}
}
function setup(){
lastPosition = document.body.scrollTop;
gear = document.querySelector(".gear");
setInterval(replace, 1000 / refreshRate); // 1000 / 60 = 16.666... Invokes function "replace" to update for each frame
}
window.addEventListener("DOMContentLoaded", setup);
A working example can be used on my GitHub. I have tested it on Firefox, Chrome and Edge (works).
Other alternatives to avoiding the warning are to use css sticky elements or use the element.classList.add() and element.classList.remove() methods limnked to a window.onscroll event.
Note: Be careful about using css transitions where the length of the transition is longer than the interval at which the CSS style will be updated by the script (such as with scrolling event based changes). Webkit based browsers and EdgeHTML will behave in unexpected ways to this, usually staying in their initial position until the element stops being updated by the script. Unless this is the effect you were intending, in which case it doesn't work the same way in firefox.
Servo Webrender, when integrated into Firefox will solve these problems to an extent (or at least improve the performance quite considerably). Although there will still be other browsers to maintain compatibility with.