Is it useful to make the drag of mousedragging happening inside requestAnimationFrame? - javascript

I have a dragging library which moves the element on every mousemove.
As we all know, mousemove fires quite often and thus forces the browser into repaints.
This could be solved, by do the actual moving in requestAnimationFrame.
Is it a useful thing to do? Will it increase performance and decrease paint events?
Are there any issues I didnt think off?

Depends on the browser and the use case.
It is now asked by the specs that browsers do threshold themselves UI events (among which the mouse events).
Implementations are encouraged to determine the optimal frequency rate to balance responsiveness with performance.
Chrome and Firefox do this, firing these events only at screen refresh rate, just before requestAniamtionFrame callbacks fire.
So in these browsers, you won't win anything by doing so, but you won't lose much either.
Safari still fires as many such events as the device emits, so in this browser you'll win by maintaining your own threshold.
(Note that if you wish to unleash this threshold, you'll need to use pointerevents instead.)
Now, this is useful to avoid uselessly calculating things that will get discarded by next calls before it can get painted.
The painting will always be throttled to the refresh rate of your screen (just after the requestAnimationFrame callbacks fire).
So it's up to you to determine if you wish to apply that threshold or not.
For instance it can make sense to update a list of points as fast as possible, but to wait the requestAnimationFrame callback to actually make DOM changes or draw anything.
From the little you said about your case, it seems though that you may indeed win by waiting for requestAnimationFrame callbacks, only because you may be modifying the box model of your CSSOM.

Related

Using requestAnimationFrame inside requestIdleCallback

If I animate changes in the DOM from JS (e.g. changing the value of a node's textContent from 1 to 500), would it be better to use requestAnimationFrame or requestAnimationFrame within a requestIdleCallback?
If you want to animate some code, then use only requestAnimationFrame, if you want to perform an action only when the browser has nothing else to do anymore, then use requestIdleCallback.
To simplify things, let's consider that both requestAnimationFrame and requestIdleCallback are setTimeout calls, with variables timeout arguments.
requestAnimationFrame would then be setTimeout(fn, time_until_next_screen_refresh). fn will be called right before the page is painted to the screen. This is currently the best timer we have to make animations since it ensures we'll only do the visual modifications only once per actual frame, at the speed of what the monitor is able to render, thus at every frame, if our code is fast enough.
requestIdleCallback would be setTimeout(fn, time_until_idle). fn will be called as soon as the browser has nothing more to do. This can be right away, or in a few event loops iterations.
So while both have a similar API, they provide completely different features, and the only viable one for doing an animation is requestAnimationFrame, since requestIdleCallback has basically no link whatsoever with the rendering.

Handling only single-touch events with jQuery

I'm handling touch events on a <canvas> area (using jQuery). I'd like to only handle single touch events, leaving multiple touch events uncancelled (e.g. so you can pinch-and-zoom).
The problem is that no matter how simultaneous the touches are, I get a touchstart with one point, followed by a touchmove with one point, and finally a touchstart with 2 points.
If I cancel the first touchdown event with preventDefault(), there's no zooming, even if I don't cancel after the touchmove nor the second touchdown.
If I don't preventDefault() on that first event, then touchmove events will scroll the page up and down in addition to my handling of the touch movement.
(testing on Android 8.1.0 - Samsung Galaxy Tab A)
I've looked around at various postings and web searches, e.g.:
jquery preventdefault on first touch only
or How to prevent default handling of touch events? or Javascript support for touch without blocking browsers pinch support
... and while not specific to this situation, I get the feeling that I'm out of luck. Seems a shortcoming of touch event handling, however, insofar as it makes multiple touches impossible to detect on the first event.
------------ EDIT -----------
Thanks for the suggestions folks! I do hope someone finds the answer useful, but unfortunately it doesn't fit my purposes, and I thought I should explain why (superfluous, perhaps, but may provide someone more insight).
To give some background, this is for a reusable module that provides an API for elements, including drawing 'sprites' and handling mouse/touch events like 'down', 'up', 'drag', etc. As such, I need to consider pros and cons carefully in the context of reusability and clarity.
The solutions mentioned here, and all others that I've found or indeed can conceive of, require a delay. There are two problems with this:
The minor problem is that any delay-based implementation of "multiple touch" is subjective. Multiple-touching is not timed out — you can theoretically touch with one finger, take a leisurely sip of your coffee (presumably with your other hand), then touch with another finger, and still be able to (e.g.) zoom. If this were the only problem, I could probably live with a pre-determined time-out, but it would be based on my perception of users' habits. (I could forsee, for instance, someone touching a 'sprite' over a dense background like a geographical map, realizing there's some detail they want to focus on, and then trying to zoom in.)
If I did delay on down, say by choosing a 300ms delay, it becomes a bit of a rabbit hole. A lot can happen in about a third of a second; likely, they could start a 'sprite' drag. There are then two choices:
If I wait to make sure it's a single touch, I miss (or cache) at least one 'move' event, and all dragging would then show a slight hesitation at the start. A third of a second is well within the bounds of perceptibility, so this is unacceptable.
Or, I could detect slight movement and assume that it's the start of a motion gesture like dragging. I'd then have to raise the API's 'down' and 'move' events simultaneously, a distasteful twiddle but again tolerable. More ambiguous is the threshold for determining it's actual motion. A very steady touch can easily get 4-6 pixels of movement on a touch screen, and I've seen well over 12 pixels for shaky touches. The gap could well be large enough to show an unseemly jitter.
As you can imagine, this is already a very processor-intensive module, especially problematic on mobile devices. Considering the increased code complexity and size (and further divergence of mouse vs touch event code paths), the possible need to introduce several tweakable settings that may be rather opaque to the user, and the fact that any solution is a compromise (point 1), I've decided that the least bad choice is to live with this limitation. The highest priorities here are smooth graphics handling and lean code (both size and processor intensiveness).
So at this point I'm willing to forego multiple touch gestures on the element. Zooming outside the element works fine, and is not unexpected behaviour — witness Google Maps.
For simpler applications, it should often be acceptable to delay detection of 'touchdown' to check for further touches.
Add a timer for your second tap in the middle of your first tap function.
For example:
$(myzone).touchstart(function(e){
e.preventDefault();
$(myzone).bind('touchstart',myeventTouch)
action = setTimeout(function(e){
$(myzone).unbind('touchstart',myeventTouch)
clearTimeout(action);
}, 500);
})
function myeventTouch(){
//When double tap do..
}
If you don't want to do this, you can also add a jQuery plugin in your page, for example I searched for one and found jquery.doubletap.js https://gist.github.com/attenzione/7098476
Use this:
$(SELECTOR).on('doubletap',function(event){
alert('doubletap');
});

How to control the framerate in KineticJS?

How can I control the rendering loop frame rate in KineticJS? The docs for Kinetic.Animation show a frame rate being passed to the render callback, and Kinetic.Tween seems to have no frame rate logic, but I don't see anyway to force, say, a 30fps when 60fps is possible.
Loads of context for the curious follows, but the question is that simple. If anyone reads on, other advice is welcome. If you already know the answer, don't waste your time reading on!
I'm developing a music app that combines some DOM-based GUI controls (current iteration using jQuery Mobile) and Canvas-based GUI controls (using KineticJS). The latter involve some animation. Because the animated elements are triggered by music playback, I'm using Kinetic.Tween to avoid the complexity of remembering how long a given note has been playing (which Kinetic.Animation would require doing).
This approach works great at 60fps in Chrome (on a fast machine) but is just slow enough on iOS 6.1 Safari (iPad 2) that manipulating controls while animations are happening gets a little janky. I'm not using WebGL (unless KineticJS or Chrome does this by default for canvas?), and that's not an option when I package for native UIWebView.
As I'm getting beyond prototype into wanting to make more committed tech decisions, I see the following options, in order of perceived goodness:
Figure out how to cap the frame rate. Because my animations heavily use alpha fades but do not involve motion, I believe I could get away with 20-30fps and look fine. Could also scale this up on faster devices.
Don't respond immediately to touch inputs, but add them to a queue which I poll at a constant interval and only use the freshest for things like touchmove. This has no impact on my non-interactive animated elements, but tackles the problem from the other direction, trying to reduce the load of user interaction. This would require making Kinetic controls static and manually tracking touch coordinates (not terrible effort if it actually helped).
Rewrite DOM-based GUI to canvas-based (KineticJS); rewrite WebAudio-based engine to HTML5 audio; leverage CocoonJS or Ejecta for GPU-acceleration. This means having to hand-code stuff like file choosers and nav menus and such (bad). Losing WebAudio is pretty serious as it eliminates features like DSP effects and very fine-grained, low-latency timing (which is working just fine on an iPad 2).
Rewrite the app to separate DOM based GUI and WebAudio from Canvas-based elements, leverage CocoonJS. I'm not sure if/how well this works out, but the fact that CocoonJS passes JavaScript code as strings between the 2 components makes me very skittish about how solid this idea is. It's probably doable, but best case I'm very tied to CocoonJS moving forwards. I don't like architecting this way, but maybe it's not as bad as it sounds?
Make animations less juicy. This is least good not because of its design impact but because, as it is, I'm only animating ~20 simple shapes at any time in my central view component, however they include transparency and span an area ~1000x300. Other components like sliders are similarly bare-bones. In other words, it's not very juicy right now.
Overcome severe allergy to Objective-C; forget about the browser, Android, and that other mobile OS. Have a fast app that performs natively and has shiny Apple-approved widgets. My biggest problem with this approach is not wanting to be stuck in Objective-C reality for years, skillset-wise. I just don't like it.
Buy an iPad 3 or later. Since I already am pretending Android doesn't exist (I don't have any devices to test), why not pretend no one still has iPad 2? I think this is passing the buck -- if I can get acceptable performance on iPad 2, I will feel confident about the app's performance as I add more features.
I may be overlooking options or otherwise naive about how to tackle this. Some would say what I'm trying to build is just silly. But it's working pretty well just not ready for prime time on the iPad 2.
Yes, you can control the Kinetic.Animation framerate
The Kinetic.Animation sends in a frame object which has a frame.time property.
That .time is a running timer that you can use to throttle your animation speed.
Here's an example that throttles the Kinetic.Animation: http://jsfiddle.net/m1erickson/Hn3cC/
var lastTime;
var frameDelay=1000;
var loop = new Kinetic.Animation(function(frame) {
var time = frame.time
if(!lastTime){lastTime=time;}
var elapsed = time-lastTime;
if(elapsed>=frameDelay){
// frameDelay has expired, so animate stuff now
// set lastTime for the next loop
lastTime=time;
}
}, layer);
loop.start();
Working from #markE's suggestions, I tried a few things and found a solution. It's ultimately not rocket science, but sharing what I figured out:
First, tried the hack of doubling Tween durations and targets, using a timer to stop them at 50%. This kinda sorta worked but was hard to get to look good and was pretty error prone in coding bogus targets like negative opacity or height or whatnot.
Second, having read the source to Tween and looked at docs again for Animation, decided I could locally use Animation instances instead of Tween instances, and allow the closure scope to hang onto the relevant note properties. Eventually got this working smoothly and finally realized a big Duh! which is that throttling the frame rate of several independently animating things does not in any way throttle the overall frame rate.
Lastly, decided to give my component a render() method that calls itself in a loop with requestAnimationFrame, exits immediately if called before my clamp time, and inside render() I update all objects in the Kinetic canvas and call layer.drawScene(). Because there is now only one animation, this drops frame rate to whatever I need and the app is fast on iPad 2 (looks exactly the same to my eyes too).
So Kinetic is still helping for its higher level canvas API, and so far my other control widgets are still easy code using Kinetic to handle user input and dragging, now performing much better as the big beast component is not eating up the CPU.
The short answer to my original question is that no, you can't lock the overall frame rate for very complex animations, but as Mark said, you can for anything that fits in a single Animation instance.
Note that I could have still used Animation without giving it a layer or explicitly calling any draw() methods, but since I'd still have to write all the logic to determine individual element's current visual state, there was no gain to doing this. What would be very useful would be if Tween could accept a parameter to not automatically render. This would simplify code like mine, as I could shorthand the animation on individual objects but still choose when to actually do the heavy lifting of rendering everything. Seeing how much this whole exercise gained in performance on the iPad 2, might be worth adding this option to the framework.

Scheduling update "threads" in JS / WebGL

Currently, I am rendering WebGL content using requestAnimationFrame which runs at (ideally) 60 FPS. I'm also concurrently scheduling an "update" process, which handles AI, physics, and so on using setTimeout. I use the latter because I only really need to update objects roughly 30 times per second, and it's not really part of the draw sequence; it seemed like a good idea to save the remaining CPU for actual render passes, since most of my animations are fairly hardware intensive.
My question is one of best practices. setTimeout and setInterval are not particularly kind to battery life and CPU consumption, especially when the browser is not in focus. On the other hand, using requestAnimationFrame (or tying the updates directly into the existing render phase) will potentially enforce far more updates every second than are strictly necessary, and may stop updating altogether when the browser is not in focus or at other times the browser deems unnecessary for "animation".
What is the best course of action for updating, but not rendering content?
setTimeout and setInterval are not particularly kind to battery life and CPU consumption
Let's be honest: Neither is requestAnimationFrame. The difference is that RAF automatically turns off when you leave the tab. That behavior can be emulated with setTimeout if you use the Page Visibility API, though, so in reality the power consumption problems between the two are about on par if used intelligently.
Beyond that, though, setTimeout\Interval is perfectly appropriate for use in your case. The only thing that you may want to be aware of is that you'll be hard pressed to get it perfectly in sync with the render loop. You'll have cases where you may draw one too many times before your animation update hits, which can lead to minor stuttering. If you're rendering at 60hz and updating at 30hz it shouldn't be a big issue, but you'll want to be aware of it.
If staying perfectly in sync with the render loop is important to you, you could simply have a if(framecount % 2) { updateLogic(); } at the top of your RAF callback, which effectively limits your updates to 30hz (every other frame) and it's always in sync with the draw.

How would we go about measuring if it is more performant to call e.stopPropagation() or not?

I want to measure if it is more beneficial to call e.stopPropagation() for the times when I do not need th event to propagate anymore (like almost 100% of the times), or will doing so actually incur an unnecessary overhead which offsets any benefit of stopping the propagation to the point that it is actually more beneficial NOT to call e.stopPropagation() altogether?
Basically what I'm interested in is, how would I go about measuring this kind of stuff?
If you are not catching/processing this event on a higher level that doesn't matter if you stopPropagation or not.
There is a tool call dynatrace Ajax Edition that gives you a pretty rich performance measurement toolkit. You can check it out at: http://ajax.dynatrace.com/ajax/en/ Included are loading metrics, event based metrics, a timeline view of event performance and more. Works with IE and FF.

Categories