Stopping work when canvas isn't visible - javascript

I have some javascript that crunches some numbers and draws to an HTML5 canvas, creating an animation. When the canvas isn't visible, the expensive and continuous number crunching / drawing is pointless.
So, when the user can't see the canvas (e.g. because they switched tabs or minimized the browser or scrolled down or various other possibilities), I'd like to put a hold on the computation.
In javascript, what's a good way to pause a periodic timer when a canvas isn't visible to the user? (And resume the timer when the canvas is visible again.)

In your other-browser-tab-focused scenario, requestAnimationFrame is ideal -- it auto-pauses its loop on tab blur and auto-resumes on tab focus.
In your scroll-canvas-offscreen scenario you can test your canvas's .scrollTop (with & without .height) vs the window's .scrollTop (with & without height).
To get the benefit of both test, put the .scrollTop test inside the requestAnimationFrame loop.

Related

Smoothing Page Offset Values During On Scroll Event

I'm working on a parallax effect for a project and while I have all the animation working great the scroll-driven effect often appears to stutter depending on the input device.
I've ended up using Request Animation Frame (RAF) to check the page offset every animation loop instead of using the basic `.scroll() event for performance reasons. However using either, if I log out the offset/scroll position values the events receive they are often very far apart for a scroll wheel/ball versus a track pad. Because the animation is scroll/offset driven this causes stuttering.
For instance a track pad triggers many events with only a few pixels difference between each event's offset value while a scroll wheel/ball triggers fewer events with 5-15 pixels difference (depending on the scroll speed) leading to a less smooth animation.
You can fool around with this yourself on this guys fiddle that goes like this:
$(window).scroll(example);
function example() {
var tempScrollTop = $(window).scrollTop();
console.log("Scroll from Top: " + tempScrollTop.toString());
};
Anyone know a good library or method for smoothing out these scroll events and their offsets? I know it's possible because I've seen parallax sites where the animations are nice and smooth regardless of input but I can't figure out how it's managed. Thanks.

Capture horizontal touch movement while allowing native vertical scroll

I'm trying to explore how feasible it is to create an HTML5 interaction where a touch can be conditionally captured by the initial direction of the motion.
The goal is to capture and track the motion of the touch only if it initially moves in a horizontal direction. Combined with a responsive page layout which only scrolls vertically, we should be able to use horizontal swipe motion to do something cool by tracking it.
The problem is in the seeming impossibility (especially on iOS) of performing touch sequence capture conditionally.
In order to track a touch (and by this I mean specifically obtaining the stream of x,y coordinates which represent the position of the finger over time), touchstart has to be preventDefaulted in order to prevent the page from engaging native momentum-scroll. Native momentum-scroll, while in operation, suspends all JS execution (setTimeout, rAF, jQuery animate et al...) and even CSS keyframe/animation/transition execution.
I would really like to know if there's a way to somehow condition the preventDefault() of the original touchstart event. It's completely possible for JS to store that event object, and then not call it until some later time (when it is determined that we are indeed interested in preventing the native scroll from starting).
But this is certain to fail because the default behavior of not running preventDefault() on that event is to engage native-scroll, which then will block JS execution for the entire remaining duration of the scroll. Failing to return the event listener function attached to touchstart does not appear to be an option. It would freeze everything.
The tentative answer, then, without any additional insight, is that all swipes must be captured if we want to be able to capture it at all, and scrolling has to be "faked" via external means à la (iScroll)[http://cubiq.org/iscroll-5-ready-for-beta-test] (personally I would want to explore some sort kind of combination of rAF and window.scrollTo and would be surprised if iScroll 5 does not employ these APIs)
Incidentally the description for the 4th demo there got me really excited, but it turns out that all it does is create a page section that iScrolls horizontally while the rest of the page behaves regularly, which is entirely mundane.

Executing all JavaScript code before repainting a browser window

I'd like to resize & move a browser window using JavaScript. resizeTo() and moveTo() seem to be my friends here:
window.resizeTo(x,y);
window.moveTo(x,y);
This works, but visually, it's a bit clunky. First, the window is moved to the desired location and then the window gets repainted on the display. Finally, the window is resized to the desired dimensions and gets repainted on the display once more. This all happens within a couple hundred milliseconds but the two discrete step are noticeable and it looks awkward.
What I really want is for these two methods to be atomic, such that they both return before the browser window (UI and all,) gets repainted on the display. Can this more cohesive presentation of window repositioning and resizing be achieved using JavaScript?
Use the setTimeout trick to allow the UI to "catch-up".
window.setTimeout(function() {window.resizeTo(x,y)},0);
window.setTimeout(function() {window.moveTo(x,y)},0);

Reading Chrome Frame Rate results in DevTools

As far as I know the white (empty) bar represents idle time.
I know I should focus so that the application would run (at best) > 60fps and at least >30fps
This is my bar:
As you can see, I'm repainting on scroll ( changing the background position of an image ).
The middle line marks 60fps in this case, and if you pay attention closely, there is a top line (which is 30fps).
I've compared my results with others out there, and it seems that I have rather large idle times. Is that okay ? I know that paint, javascript and anything else is bad, but what purpose does displaying idle times serve ? Should I attempt to get rid of it, if so, what should I watch out for ?
Also, there is a spike every now and then because of "Image Decode", is there anything I can do about it ?
Image Decode
You can reduce the size of the image, which will make the decodes shorter. You can also trick the browser into doing these decodes earlier, perhaps at a time when there's no activity and therefore won't hurt your FPS.
I would also kill these big images temporarily to see if that moves you from 30fps to 60fps.
Scroll handler
You should minimize the amount of work you're doing in your scroll handler. At most you should be grabbing one scroll metric from the DOM and a timestamp perhaps. Then do your adjustments to style in a requestAnimationFrame loop: http://www.hesslerdesign.com/blog/javascript/optimizing-javascript-performance-handling-coordinate-updates/
Heavy paints
background-position isn't optimized well in WebKit or Blink, especially for this sort of usecase. I would put the image into a div and just slide its position using a transform: translate(x,y) (and promote it to its own layer if possible.) If sucessful, you won't repaint the entire screen on every scroll.
30 FPS Aint bad
A solid 30FPS actually looks better than something that's jumping between 60 and 45 all the time. Curious.. Are you in Canary or on a retina screen?
In general, you're doing pretty okay on your hardware, but I would try to reduce those image decodes. And you can go further with the above if you want.

iphone/ipad large canvas vs small canvas + div animation

Since I dont have either of these devices handy to test, which method would be faster on an iphone/ipad?
One large canvas (overlayed over a bg of the same size), that is cleared, and has new pixel data drawn to it each frame, at a certain position
or
A small canvas that updates each frame, contained in a div that is animated across a background
As it turns out iphone/ipad aren't even capable of running this small animation either way. I will have to look at alternative solutions for mobile/tablet devices.
Yes, I've found the iPhone too slow in all my canvas experiment.
Even moving a simple div around (CSS3 styled, no canvas) is barely accettable ( my test with the new motion API: http://www.omiod.com/i.htm )

Categories

Resources