Yes i have read this and this thread - this is more specific question of performance. (or i didn't understand the answers)
As many others I learnt that 'scroll' events should not be used for animations but debounced as they trigger too often. The best way to do this hence requestAnimationFrame.
But recently I wondered the same for mouseMove and all other events and decided to just log how often each are triggering, and see how much more often mousemove and scroll events trigger than RAF running along side. And to my surprise the answer was - not at all.
see codepen here.
Now is this the wrong measure? - I thought the problem is that scroll events trigger more often than our framerate and needs to be debounced in some way. but that does not seem to be the case? or does this only happen in certain situations when repaint / scroll are resource intensive? if so can anyone give any examples?
But the top answer in thread above claimed "However the scroll event is triggered every time the browser renders a scroll position change. This means a scroll event is synchronized with the rendering of the page."
And even mdn scroll docs say that "input events and animation frames are fired at about the same rate, and therefore the optimization below is often unnecessary". - so when is it necessary?
What is really the difference between running a simple animation in each, say of an image moving left to right as you scroll.
/* RAF */
let last;
const loop = () => {
if(window.scrollY!==last){
// run animation here ...
// element.style.transform = `...`
}
last = window.scrollY
requestAnimationFrame(loop);
};
requestAnimationFrame(loop);
Versus onscroll
/* onscroll */
const onscroll = ()=>{
// run animation here ...
// element.style.transform = `...`
}
window.addEventListener('scroll',onscroll);
raf has certain advantages, i get that, but also it runs this logic on EVERY FRAME, even when we are not scrolling. so if my page grows and there are more linked animations, they will all accumulate in raf?
If scroll event is (kind of) linked and at least never firing more often than raf, then isnt this more efficient, as it only fires on a scroll event, rather than running (some of the logic) on every frame?
Is the difference in what is being delayed by running animation functions that start getting too long? i understand if onscroll callback is too long it can slow down the scrolling. but the same logic in raf will slow down our framerate?
Or is the answer simply it depends?
For a larger example then, say we are animating a lot of different items on scroll, in my project i used intersection observer to only run logic on elements that are visible, we can add and remove the event listener on the intersection events each.
Or would we used logic as above in raf and check each element in the raf loop?
To end with a specific question then - is my measure of comparing number of triggers on scroll and animation frame wrong? what are the dis-/advantages of each method then and when should it be used, in this case for animations on scroll. is there really a significant difference?
Related
This question already has an answer here:
What are passive event listeners?
(1 answer)
Closed last year.
I have one issue that is a recurring thing every time I work in Javascript, which is that my code seems to be cutting in and out, depending on whether I scroll, resize the screen, reload, etc. It even seems like the speed of which I scroll seems to be a factor (even though I am, in this situation, trying to create a scroll effect, which does work as long as I scroll slowly). I have tried to figure out work arounds almost constantly since starting coding JS, but I can’t even figure out why any of this is happening, and I don’t even know what to good to find the answers.
Generally, when I create functions, I try to use the following structure
document.addEventListener("click", function functionName() {
/* function content */
});
However, this really only seems to work with click effects, and maybe hover. for scroll effects, because resizing the screen will cause weird things to happen, I have tried using the following structure
function functionName() { /* function content */ };
document.addEventListener('scroll', functionName);
window.addEventListener('load', functionName);
window.addEventListener('resize', functionName);
This generally works better, and generally prevents screen resizing from interfering too much with scroll effects. However, it makes the code what I am seeing very jumpy and glitchy.
Is there a better way to do this, so that I am a scroll effect will always appear how it should, regardless of whether the screen resizes loads or scrolls, etc.? also, it there a way to make the code work better without having three separate event listeners to activate a single function?
The challenge you are running into for other events is that they can be called 10s or 100s of times per second. What you need is to throttle the functions you are calling, especially if they are expensive.
I suggest you look at makeThrottleFn in my xuu library. You might just install it and test like so:
const throttleFn = xuu._makeThrottleFn_({
_fn_ : onResizeFn, _delay_ms_: 250
});
// And now use this for windowresize
window.addEventListener('resize', throttleFn);
Of course there are many other libraries that offer throttling, so feel free to go shopping.
Using document.addEventListener("click") is going to run the function anytime the screen is clicked or touched. And applies to the whole document. Meaning anywhere you click the event listener is going to run.
Using the scroll event is running the block of code multiple times. This will create undesired results unless you put in something to stop it.
So using multiple document.addEventListeners with "click" and "scroll" will most likely overlap in some cases causing wierd things to happen with both functions running multiple time or randomly.
Ever since FF 52 was introduced, I am having the same lagging issues when executing on scroll events, especially when using the mouse wheel - the same thing occurs on IE Edge as well, but it was considered a minority, now with the addition of FF some kind of a solution has to be found.
I have created a fiddle online that replicates the problem - the issue is visible on FF and IE edge, while it works smoothly across webkit browsers.
Here is the simple scroll function I am using:
$('.scrollable').on('scroll', function() {
scrollY = $(this).scrollTop();
$(this).find('td:first-child span').attr('style', 'transform:translateY(' + -scrollY + 'px)');
})
https://jsfiddle.net/nfmLa7mn/3/
If you scroll with the mouse wheel the issue is more visible. It's a small lag but it's there. In more complex layouts the issue is more pronounced.
Am I the only one bothered by this? I haven't seen any other similar topics online. Is there any way I am not aware of that can fire scroll events in a better way? Or is there any other way around this issue?
Maybe this is due to the fact that Firefox handles scroll asynchronously.
https://developer.mozilla.org/en-US/docs/Mozilla/Performance/Scroll-linked_effects
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 or transform property.) [...]
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.
I have a circuit simulator that redraws as you drag things around on an HTML5 canvas.
I've noticed that, sometimes, it fails to redraw during drags. It holds the last drawn frame until I stop moving the mouse, then starts showing updated content.
At first I thought this might be some kind of issue with the code I use the throttle the draws below 60fps. Maybe timer events were being swamped out by higher priority UI events or something like that. But I profiled the code in Chrome, and the profiler confirms the draw code is being called and finishing in a reasonable amount of time.
Here's a screenshot from an example profile I collected while dragging with the issue happening. Note the draw code finishing within 5ms, with plenty of idle time:
So mostly I'm stumped and need ideas of what to check.
What are some common reasons for redraws not to show when they are being triggered by user actions?
Use requestAnimationFrame instead of setTimeout.
During my testing (Chrome, Windows 10), I found that if I just spun the mouse wheel up and down quickly then I could prevent setTimeout callbacks from running until I stopped. That makes setTimeout a really bad idea when it comes to canvas animations. I switched the throttling code to using a requestAnimationFrame cycle, that just kept rescheduling itself until the cooldown period was over, and lag decreased noticeably.
Check for other draw loops.
I noticed that the profiler's stack trace is showing the wrong redraw code. The program had a dialog that contains a canvas that sometimes needs to animate. That code was spamming requestAnimationFrame despite the dialog not showing. Somehow this was interfering with the main draw loop, though I don't know exactly why. All the draws were happening in the hidden dialog.
Once I fixed the dialog code spamming requestAnimationFrame, the pause-while-dragging issue disappeared.
I'm trying to build a parallax site, which will move few elements while scrolling the site.
But instead of using a scroll event listener I'm using requestAnimationFrame, after reading this post by Paul Irish, and this video which said that scroll listener is a bit buggy. My questions are:
It looks quite smooth in Chrome, but it's flickering badly in Firefox. Did I do something wrong here?
Does my code actually taking up more resources than using normal scroll event listener? I can hear my laptop fan blazing every time I'm playing with this code.
My file is located at http://www.socialbuzz.com.au/index.html, and please scroll to the bottom of the page to see the element that's being manipulated from javascript.
You should have a the scroll event trigger a requestAnimationFrame loop. Do not have requestAnimationFrame triggered by the scroll event itself. You should have something like a var scrolling = true; While this is happening run your requestAnimationFrame loop which references event data from the scroll event. You'll need to debounce the scroll event to turn to loop off once you are finished, it's a chore but the results are worth it. Hope this helps.
You are not performing an animation; that is, you are not modifying your graphics continuously without user interaction. Your manipulation of the page happens only when the user scrolls and does not continue after the user stops scrolling. Therefore, there is no benefit to using requestAnimationFrame, and you should stick to a simple event handler.
The purpose of requestAnimationFrame is to provide optimal behavior for continuous animation; none of its advantages apply here. A looped requestAnimationFrame which does nothing inside the loop, as you currently have, is entirely appropriate if each step of the loop updates the graphics in some way, but since here nothing changes when the user is not scrolling, the loop does nothing but waste CPU time.
An example of when you should use requestAnimationFrame is if you had elements which deliberately lagged behind the scrolling and caught up after a moment. The catch-up animation should be done in a requestAnimationFrame loop which is started from the scroll event handler, and that loop should stop itself when the catch-up finishes.
I have had a similar experience and after much playing around with mouse move listeners and setInterval to increase the frequency of the animation steps, I have gone back to just using onscroll and find that on FF10 and FF 15 it is working great.
Maybe my requirements are not the same as yours - it is an element that tracks the scrollbar so onscroll was the cue to change the position of the box. It lagged behind and was jerky on FF, but worked fine on WebKit and IE. What I found was that onscroll did not fire as often on FF as on Chrome/IE.
When I initially tried this it would be on FF 5 or 6 though. Using a mouse move listener or a frequent interval, I was able to increase the frequency with which my handle scroll function go called - but this actually had the effect of making the positioning appear choppier. Just using onscroll seems to be working for me now on 10 ESR and 15, maybe they fixed something.
here's my question, i'm developing a personal website that has a huge animation in the background, with clouds moving around in an infinite loop.
Everything is done with jquery extended with jquery timers and sprites.
|
First it fills out the cloud starting position matrix (random)
|
it set's the actual position of each cloud.
|
start moving the clouds with .animate() function and start a timer to fire again that animation until the clouds reach the left border.
|
repeat forever :)
Anyway this method consume a "little" memory and CPU, i'm trying to optimize the code,
and i was wondering if there's a method to call a function when the browser switch to a different page, to stop the animation.
Thanks.
Plus, if anyone would help in optimizing code, i will appreciate so much! :)
I'll post the link to the website if anyone could help with that.
Thanks Again
First, make sure you animate a single DOM-element, not a bunch of individual clouds. Put your clouds into a container and move the latter.
Second, take a look at CSS3 transitions. They are much smoother (GPU-accelerated), than JavaScript-based animation. Also, super-easy to learn and use. Just describe a CSS class and add it to your clouds upon initial position setting.
As for the determining if the browser tab is in the background, requestAnimationFrame, used by jQuery as animation ticker which is a 60 FPS ticker, can do that for you automatically.
It appears, jQuery is no longer using requestAnimationFrame (they used to at some point, but then removed it). So, here's an animate function employing requestAnimationFrame (with a setTimeout shim for IE) you can steal ideas from (or just grab the function itself).
You could listen to the window's blur and focus events:
$(window).on('blur', function() {
// window went into background, stop animations here
// ...
});
$(window).on('focus', function() {
// window became active, start animations here
// ...
});