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.
Related
I am using AngularJS 1.5 and aim to create some draggable divs and images.
I have a bunch of absolutely positioned divs and images that have ng-style. There is a directive that listens to a mousedown and subsequent mousemove, and when as the element is dragged, applies a style (for example {left: 20px, top: 100px}) based on the cursor position, updates a model carrying top, left, width and height properties, which ng-style uses. This operates very smoothly on Chrome, a little slower on Firefox, and abysmally slow on IE11. I am trying to get IE11 to perform adequately.
After looking at the performance profiles, it appears that IE is not calling paint on as frequently as Chrome. Chrome processes the one mousemove event in approximately 6ms (see below), and paints roughly every 16ms for 60fps.
For some reason, using the same code, IE11 calculates a bunch of mousemove events without painting the screen, causing FPS to be abysmal. How do I increase the frequency of painting the screen?
Does anyone have any ideas as to how to increase the painting frequency?
Chrome Profile:
One mousemove event processed in ~5ms, painting occurs every 16ms.
IE11 Profile:
One mousemove event processed in ~10ms, but many are called without painting, and painting occurs every 100-200ms, resulting in poor FPS.
As an experiment, I am trying to replicate the Sprite functionality of AS3 in JavaScript without using the canvas object. I thought that using absolutely positioned divs and manipulating their css properties would be a no brainer, however in Chrome the animation introduces strange artifacts (seemingly because of redraw issues).
I can not find what I am doing wrong? The code is, in fact, quite simple. Here are some points that I tried which didn't help:
Using relatively positioned divs (as opposed to absolutely positioned.)
Using margins (as opposed to top & left properties.)
Appending objects directly to body (as opposed to appending to a container div.)
Using setTimeout (as opposed to requestAnimationFrame)
You can see a simplified fiddle here: http://jsfiddle.net/BVJYJ/2/
EDIT: http://jsfiddle.net/BVJYJ/4/
And here you can see the artifacts on my browser:
This may be a bug in my setup (Windows 7 64 bit, Chrome 21.0.1180.75). No other browsers exhibit this behaviour. I'd greatly appreciate if someone could comment on what I could be doing wrong. I'm more curious about the reason behind this rather than a workaround btw. That said, every explanation is welcome. :)
EDIT: There was a bug in the sample code which resulted in using setTimeout even when I was under the impression that RAF was used. requestAnimationFrame solves the issue with basic transformation but the issue remains with CSS transformations such as rotation.
I had the same problem with my liteAccordion plugin. It can be fixed by setting the backface visibility to hidden on the element you're animating, as you can see here: http://jsfiddle.net/ZPQBp/1/
Some research shows that setTimeout could cause issues due to various reasons. You really should use requestAnimationFrame:
Timers are not accurate to the millisecond. Here are some common timer
resolutions1:
Internet Explorer 8 and earlier have a timer resolution of 15.625ms
Internet Explorer 9 and later have a timer resolution of 4ms. Firefox
and Safari have a timer resolution of ~10ms.
Chrome has a timer resolution of 4ms.
Internet Explorer prior to version 9 has a timer resolution of 15.625
ms1, so any value between 0 and 15 could be either 0 or 15 but
nothing else. Internet Explorer 9 improved timer resolution to 4 ms,
but that’s still not very specific when it comes to animations.
Chrome’s timer resolution is 4ms while Firefox and Safari’s is 10ms.
So even if you set your interval for optimum display, you’re still
only getting close to the timing you want.
Reference: http://www.nczonline.net/blog/2011/05/03/better-javascript-animations-with-requestanimationframe/
Also
setTimeout doesn’t take into account what else is happening in the
browser. The page could be hidden behind a tab, hogging your CPU when
it doesn’t need to, or the animation itself could have been scrolled
off the page making the update call again unnecessary. Chrome does
throttle setInterval and setTimeout to 1fps in hidden tabs, but this
isn’t to be relied upon for all browsers.
Secondly, setTimeout only updates the screen when it wants to, not
when the computer is able to. That means your poor browser has to
juggle redrawing the animation whilst redrawing the whole screen, and
if your animation frame rate is not in synchronised with the redrawing
of your screen, it could take up more processing power. That means
higher CPU usage and your computer’s fan kicking in, or draining the
battery on your mobile device. Nicolas Zakas does an excellent job
explaining the impact timer resolution has on animation in a related
article.
Reference: http://creativejs.com/resources/requestanimationframe/
It has something to do with subpixel positioning. If you round off to the nearest pixel you won't see those rendering errors:
thisRef.block.style.left = Math.round((x + (mouseX - ox - x) * .125)) + "px";
thisRef.block.style.top = Math.round((y + (mouseY - oy - y) * .125)) + "px";
i am developing a game with HTML5 canvas and JavaScript.
To time the game, i take the system milliseconds from now, subtract the system milliseconds from when the last frame occurred, which leaves me with the milliseconds the last frame took to render.
This works fine, but if i open a new tab in Firefox, the canvas seems to get paused by Firefox, and when i come back, the current system time is many seconds later then the system time the last frame occurred and everything jumps, because its movement is relative to the last frame time.
I need some way to either detect, that the canvas is not running at the moment, or that Firefox (and of course other browsers) has switchted to another tab, so i can pause my game, and when returning, update the 'last frame time' to prevent jumps.
Any ideas?
You could also use the window onblur event to detect when the tab is changed.
That's just one cause of clock jump. You're still vulnerable to forced process sleep, high system load, computer sleep mode, hibernation, and a dozen other scenarios.
You need to cap the time delta. In my projects (hobby games), I've found 80ms to be a functional maximum.
(Also, collision detection is much harder without a cap.)
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
// ...
});