Dealing with touchMove events in iOS and Ember - javascript

I'm adding iOS support to an Ember application which uses Flame.js. Most Flame.js widgets don't have built-in support for touch events at the moment, so I'm working on adding it to the ones I need. I've got touchStart and touchEnd working just fine for clicking and certain state transitions.
However, touchMove has been a mess so far. I need it for dragging, and to do that I need to track where the movement began and where it is. So far I haven't been able to get all that information consistently from touchMove. Various resources suggest that I should be looking in the event.touches array for my data, but the jsFiddles I've built so far all throw TypeError when I try to get length on that array, which they claim is undefined. (The usual places to look, event.pageX, event.pageY, etc. are also undefined.)
I'm testing with an iPad and with Phantom Limb, and with the latter I was able to get at data by accessing originalEvent, but that doesn't work on the actual iPad, I think because the originalEvent attribute is an artifact of how Phantom Limb works. (My theory is that accessing originalEvent is accessing the original mouseMove that Phantom Limb transformed into a touchMove.)
Why is the event.touches array undefined for me? More to the point, where can I get touch position data so I can drag?
Here's my most representative jsFiddle. Click the button to get a Panel, which you whould be able reposition by dragging its title bar if this was working. The extensions of Flame.State in the titleView of App.TestPanel are overriding the original states from Flame itself.

I don't think it is just an artifact of PhantomLimb. We have a similar challenge, and use the following:
normalizeTouchEvent = function(event) {
if (!event.touches) {
event.touches = event.originalEvent.touches;
}
if (!event.pageX) {
event.pageX = event.originalEvent.pageX;
}
if (!event.pageY) {
event.pageY = event.originalEvent.pageY;
}
};

Related

React.JS onMouseOver animation failing

I've been trying to get a two image slider working with a mouseOver event in react.js. I've seen a similar component linked here: codepen.io/bradtraversy/pen/NaKxdP
The problem seems to be a huge delay with refreshing the image as well as onMouseOver events that occasionally fire with an offsetX coordinate that is way off from what it should be. Thanks for any help. A demonstration of the problem I'm experiencing is linked in the codesandbox below.
https://codesandbox.io/s/romantic-perlman-tzze4?file=/src/App.js
I can't exactly contest as to why the offsetX is acting the way it does and am unable to verify what exactly you mean by refreshing the image being slow but using your codesandbox code I replaced:
if (Math.abs(xCoor - e.nativeEvent.offsetX) >= 2)
setXCoor(e.nativeEvent.offsetX);
console.log(e.nativeEvent.offsetX);
With:
if (Math.abs(xCoor - e.nativeEvent.clientX) >= 2)
setXCoor(e.nativeEvent.clientX -5);
console.log(e.nativeEvent.clientX);
And I am at least able to fix the flashing when the offsetX sets back to the odd numbers.
Based on some other things I was reading while researching this, offsetX seems to provide inconsistent values randomly (as you have noticed). This answer delves into it more and may be able to help you along further setting something up that is more consistent.
I've attached an updated answer here:
https://codesandbox.io/s/zealous-johnson-73myy?file=/src/App.js
This uses a ref to the underlying DOM node to access the bounding rectangle and then use the clientX method as suggested by JFoxx64 to get offset within the element itself. I think his method is also just as valid, we just discovered them at the same time. Thanks!

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');
});

About Crafty - Isometric and gravity; also fourway

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..

iPhone web app - touch and gesture events

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.

How do I get clientX and clientY to work inside my "drag" event handler on Firefox?

Mozilla Firefox 3.x seems to have a bug when listening to the "ondrag" event. The event object doesn't report the position of the object being dragged, clientX, clientY, and other screen offsets are all set to zero.
This is quite problematic as I wanted to make a proxy element based on the element being dragged and using of course, clientX and clientY to adjust its position.
I know that there's cool stuff around such as setDragImage in HTML5 but I want to provide a generic abstraction for native DD between browsers.
Faulty code:
document.addEventListener('drag', function(e) {
console.log(e.clientX); // always Zero
}, false);
Note:
This problem doesn't happen on other events (dragstart, dragover) and the mousemove events cannot be captured while dragging something.
I found a solution, I've placed a listener on the "dragover" event at the document level, now I get the right X and Y properties that I can expose through a globally shared object.
The drag event in HTML 5 is not fully functional in todays browsers. To simulate a drag n drop situation you should use:
Add a onmousedown event, setting a var true.
Add a onmouseup event, setting that var false.
Add a onmousemove event, checking if that var is true, and if it is, move your div according to the coordinates.
This always worked for me. If you face any problems, get in touch again, I can provide some examples.
good luck!
I know that there's cool stuff around
such as setDragImage in HTML5 but I
want to provide a generic abstraction
for native DD between browsers.
But why do something like this, aren't there libraries like JQuery & Prototype available for cross browser drag & drop?
Or else if you want to implement a DD library of your own, you can take help of their methods or extend them, as both the libraries are following object oriented paradigm.
This will save much time.

Categories