问题
I am developing an HTML5 Canvas project and while adding multitouch support for mobile devices, I seem to have run into an issue on Mobile Safari in iOS 7.1.
The main way that users interact with my app is by clicking and dragging. On the desktop, I add the appropriate mouse event listeners to my canvas...
c.addEventListener('mousedown', mouseDown, false);
c.addEventListener('mousemove', mouseMoved, false);
c.addEventListener('mouseup', mouseUp, false);
... have these event handlers calculate mouse position and call out to a more generic function that just accepts the X and Y position of the event...
function mouseDown(evt)
{
var pos = getMousePos(evt);
inputDown(pos);
}
function mouseUp(evt)
{
var pos = getMousePos(evt);
inputUp(pos);
}
function mouseMoved(evt)
{
var pos = getMousePos(evt);
inputMoved(pos);
}
function getMousePos(evt)
{
var rect = c.getBoundingClientRect();
return { 'x': evt.clientX - rect.left, 'y': evt.clientY - rect.top }
}
... and everything works great. I designed this level of abstraction to allow for easy expansion to other input methods. So now I attempt to use the Touch API to expand this. First I add my handlers to the canvas right next to the mouse handlers...
c.addEventListener("touchstart", touchDown, false);
c.addEventListener("touchmove", touchMoved, false);
c.addEventListener("touchend", touchUp, false);
c.addEventListener("touchcancel", touchUp, false);
... then add my thin event handlers to abstract away the touch events ...
function touchDown(evt)
{
evt.preventDefault();
// Ignore touches after the first.
if (activeTouch != null)
return;
if (evt.changedTouches.length > 0)
{
activeTouch = evt.changedTouches[0].identifier;
var pos = getTouchPos(evt);
inputDown(pos);
}
}
function touchUp(evt)
{
var pos = getTouchPos(evt);
activeTouch = null;
inputUp(pos);
}
function touchMoved(evt)
{
var pos = getTouchPos(evt);
inputMoved(pos);
}
function getTouchPos(evt)
{
var canX = 0;
var canY = 0;
for( var i = 0; i < evt.touches.length; i++ )
{
if (evt.touches[i].identifier == activeTouch)
{
canX = evt.touches[i].pageX - c.offsetLeft;
canY = evt.touches[i].pageY - c.offsetTop;
break;
}
}
return { 'x': canX, 'y': canY }
}
... and this is where I run into trouble! You can see things get a little hairy but inefficiencies aside, I think I am using everything correctly. The problem is that getTouchPos(evt)
seems unable to get the correct X, Y position of a touch on touchstart
. On my first touch, I get a position of 0,0. If I drag my finger, the touchmove
fires multiple times and the X,Y coordinates reported seem fine. If I lift my finger, touchend
fires and I also get a correct location. However, if I touch again, touchstart
fires and rather than where I am touching, I get the X,Y coordinates of where my last touch ended!
I've gone in with a debugger and sure enough, the stale/uninitialized values are right there in the Touch objects I get in the event data. Am I doing anything obviously wrong? I don't understand why I seem to be getting bad coordinates from touchstart
events. What can I do to work around this issue? Is there another source I can use to get the correct position? A subtle tweak to event order?
Here's a link to an online demo of the full code if you'd like to run some in-browser debug.
And thanks in advance for any help.
回答1:
I'm not sure if it's obvious, but it was a fun issue to debug. So basically, the confusion comes down to this guy:
function mouseMoved(evt) {
var pos = getMousePos(evt);
inputMoved(pos);
}
For your mouse events, everything works great, because you're bound to mousemove
.
c.addEventListener('mousemove', mouseMoved, false);
Which calls
function mouseMoved(evt) {
var pos = getMousePos(evt);
inputMoved(pos);
}
So, as you move your mouse around, your posX/posY
values are updated continuously. (Hopefully you see the issue now :)
For TouchEvents, when a user touches (but does not move), you only trigger touchstart
and touchend
. It isn't until a user drags, that touchmove
gets called, which in turn calls the function touchMoved
(that calls inputMoved
- which updates your posX/posY
values).
The easiest way to address this issue on your page is just to call inputMoved
in both touchstart
and touchmove
so your MassHaver
instance has the latest state.
来源:https://stackoverflow.com/questions/23586944/touch-events-touchstart-returns-incorrect-position-on-mobile-safari-workaroun