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');
});
Related
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.
Link 1 - http://horebmultimedia.com/Sam3/
Link 2 - http://horebmultimedia.com/Sam5/
In the above links, i have added a set of numbers added in separate containers in each file and u can find the FPS on the top right. The issue is when i mouse over in this Link 1 and click any numbers, as u see the FPS is getting slower & slower, making the world to rotate slower on the left side.
While on this link, Link 2, I added only one mouse over and 5 mouse over, but there is not much difference in FPS, why it lags so much when i have 37 containers. I can give my code if u need to resolve.
I had a rough look at your code, but digging through an entire project is not a fantastic way to debug an optimization problem.
The first thing to consider is if you have mouseOver enabled on your stage, I would recommend a liberal use of mouseChildren=false on interactive elements, and mouseEnabled=mouseChildren=false on anything not interactive. The rollover could be a big cause, as it requires everything to be drawn 20 times per second (in your usage). Text and vectors can be expensive to redraw.
// Non-interactive elements (block all mouse interactions)
element.mouseEnabled = element.mouseChildren = false;
// Interactive elements (reduce mouse-checking children individually)
element.mouseChildren = false;
If they don't change, you might consider caching text elements, or button graphics. I think I saw some caching in the source - but its generally a good thing to consider.
--
With that said, debugging optimization can be tough.. If removing all the buttons brings your performance up, consider how your buttons are being constructed, and what their cost is.
* Mouse over is expensive
* Vectors and text can be expensive
* Caching can help when used right, but can be expensive if it happens too often.
* Review what is happening on tick(). Sometimes, code is running constantly, which doesn't need to.
--
A few other notes:
This does not do what you think: _oButton.off("mousedown"); -- You need to pass the result of the on() call. If you are just cleaning up, call _oButton.removeAllEventListeners().
You don't need to set the cursor on mouseover. The cursor will only change when it rolls over -- so just set it once, and then get rid of your buttonover stuff.
It might make sense to just extend EventDispatcher for your custom classes, which gives you things like the on() method, which supports a data param. I might recommend this in place of your addEventListener stuff in CTextButton
Note that RAF does not support a framerate property (it just uses the browser's RAF rate, which is usually 60fps). Use createjs.Ticker.timingMode instead of the deprecated useRAF.
Hope that helps a little.
Configuration is as follows :
_ four independent zones, top_left, left, top_right and right.
_ the scroll should be linked horizontally for a top and its area below
_ the scroll should be linked vertically for each zone and its sibling (on an horizontal axis)
This configuration is described in the picture linked below. Same color means synchronized scroll. A zone to be synchronized with is referred as "scroll-buddy" for the remaining of this question.
I have tried several methods, all of which focuses on user experience using platform-browser-native scroll solutions. In this situation, frameworks like iScroll should be considered as a last resort.
The most obvious solution is to set the scrollTop/scrollLeft of the appropriate scroll-buddy whenever onScroll is called. This is expensive as reading scrollTop/scrollLeft forces the browser to layout. However, performances can be greatly improved by calling requestionAnimationFrame() on this operation. The biggest limitation is probably due to the different ways a browser has to handle scroll events. The result is sparse, at best, with a laggy/"always trying to catch up" synchronization on mobile devices (chrome/safari) due to the specific implementation of inertial scroll.
I have also tried to intercept, clone and redispatch user events to the appropriate scroll-buddy. I am unsuccessful at this point, plus the inertial scrolling is a floating point operation, which results could be very sligthly different even when triggered by the same user event. This could cause a little delta between two scrolling buddies and could be very well perceived on very large scroll zones. A periodic sync is always possible but at this point I simply could not make it work.
I have also considered putting everything in the same scrollable zone but the complexity of this solution is quite remarkable.
My question therefore is very simple : could anybody come up with a better strategy for this problem ? Or simply provide me with an implementation of the first solution that could do the trick, maybe by using something I missed.
I have two questions about Crafty (I've also asked in their google group community, but it seems very few people look at that).
I've followed this tutorial http://buildnewgames.com/introduction-to-crafty/ and also took a look at the "isometric" demo in crafty's website of a bunch of blocks (http://craftyjs.com/demos/isometric/). And I've been trying some stuff by combining what I've learned in both.
(Q1) When I use the fourway component (used in the tutorial a lot), if I hold the left arrow key for example and CTRL-TAB out the current tab while holding left, and then go back (not necessarily holding left anymore), then the my character seems to get stuck in moving to the "left" direction. It also happens for the other 3 directions. Is that a known issue? Is there anyway to fix it without changing crafty?
It happens here with firefox 29 and chrome 34. My code is pretty much the one in the final version presented at the tutorial's end (it's not the same, but even when it was the same I already had this issue).
By the way, when this happens, if I CTRL-TAB out and back again holding that left key, things go back to normal (the movement stops).
(Q2) The isometric-ish features interprets Z as being height, and the gravity component uses Y for height. Isn't this a problem? Can I, maybe, for example, tell gravity to use something else, other than y, for height?
Regarding (Q1), the movement is managed by keydown and keyup events. If you change the tab when the start of a movement was triggered, the fourway component never gets any keyup event to stop again. You could use a workaround like the following:
Crafty.settings.modify("autoPause", true);
Enabling autoPause (somewhere in your init function) will pause your game when the browser tab crafty is running in is inactive. You can then react to this event by triggering keyup events or preventing the player component to move like this:
player.bind('Pause', function() {
this.disableControl();
});
player.bind('Unpause', function() {
this.enableControl();
});
You might want to stop animation there too if you handle that in your player component..
looking at the (quite outdated) documentation on the apple website and across the internet for the touch and gesture javascript events, I can't help but notice that the gesture event which is returned during the function being called, only contains the values 'scale' and 'rotation'. Have I missed something or are they the only two values returned?
It's hard to see the values of a returned object on the iPhone as they are annoyingly only shown as their type and name. e.g when logging the returned object in the console, it just shows up as '[object TouchEvent]'.
What I am trying to achieve is to run a function once a two finger swipe occurs, then whether the swipe was left or right, change the page accordingly.
I have tried a more complex touchevent and the (what I thought would be easier) gestureevent, but the gestureevent only returns the scale and rotation (apparently), and the touchevent seems quite complex in the way I am doing it.
Does anyone know if the object returned is just the scale and rotation, and if so, do you know how I can get the same effect with the touchevent instead?
You should use touchevent that provides you trajectory [of movements] to detect is this trajectory falls into "swipe" category using your own definition of swipe.
Usually "swipe" gesture is context specific: e.g. it should start in particular DOM element and probably end there. It means that for particular DOM element some gesture is not a "swipe" but for its container for example it is a swipe. So you cannot generate bubbling event for that in general.
Zoom and rotation gestures can be detected without knowing context - so system generates them for you.
There are ready to use frameworks and libraries that have swipe gesture detectors. At least for some popular containers like items in vertical list and so on.