[译]跨浏览器的多点触控与鼠标事件处理
原作者Ted Johnson, Graphics Program Manager Lead, Internet Explorer
译者sunnylqm,转发请注明。
本文主要解释了Web开发者如何通过使用IE10中新引入的pointer事件模型、iOS上的touch事件模型以及W3C标准的扩展鼠标事件模型来编写普适的跨浏览器的触控事件处理代码。
题外话:我很幸运的拥有一台三爽的700T开发者平板。通过它可以很好的体验IE Test Drive中的多点触控demosTouch Effects和Lasso Birds。你可能也注意到了,Lasso Birds在除了IE10以外的设备和浏览器上也能很好的运行,比如,它的多点触控在iOS设备上也能运作。本文中,我以Lasso Birds的一些代码模式为例,将其普适拓展至兼容比较老的浏览器。
(原文有附带一个触控绘图的demo,以下代码将基于这个demo讲解。)
代码部分
用鼠标来绘图的基本流程简单明了:
var drawingStarted = false; function DoEvent(eventObject) { if (eventObject.type == "mousedown") { drawingStarted = true; startDraw(eventObject.pageX, eventObject.pageY); } else if (eventObject.type == "mousemove") { if (drawingStarted) { extendDraw(eventObject.pageX, eventObject.pageY); } } else if (eventObject.type == "mouseup") { drawingStarted = false; endDraw(); } }
唯一需要改动以支持IE10的pointer事件的地方就是得注意多点触控可能同时发生(多个点由不同的pointerId区分)。IE10的 pointer模型会触发每个触点的单独的MSPointerDown, MSPointerMove和MSPointerUp事件。
var drawingStarted = {}; function DoEvent(eventObject) { eventObject.preventManipulation(); // 如果不加上这句, 则屏幕的拖动会代替绘图的动作 var pointerId = eventObject.pointerId; if (eventObject.type == "MSPointerDown") { drawingStarted[pointerId] = true; startDraw(pointerId, eventObject.pageX, eventObject.pageY); } else if (eventObject.type == "MSPointerMove") { if (drawingStarted[pointerId]) { extendDraw(pointerId, eventObject.pageX, eventObject.pageY); } } else if (eventObject.type == "MSPointerUp") { delete drawingStarted[pointerId]; endDraw(pointerId); } }
要兼容Apple的iOS的touch事件模型则需要你遍历每一个 touchstart, touchmove, 和touchend事件中的changedTouches。因为在iOS事件模型中,同一个事件中可能包含同时产生的不同状态的触控点。像IE10的 pointer模型一样,我们也需要用唯一的id来区分不同的触控点。
var drawingStarted = {}; function DoEvent(eventObject) { eventObject.preventDefault(); // 如果不加上这句, 则屏幕的拖动会代替绘图的动作 for (var i = 0; i < eventObject.changedTouches.length; ++i) { var touchPoint = eventObject.changedTouches[i]; var touchPointId = touchPoint.identifier; if (eventObject.type == "touchstart") { drawingStarted[touchPointId] = true; startDraw(touchPointId, touchPoint.pageX, touchPoint.pageY); } else if (eventObject.type == "touchmove") { if (drawingStarted[touchPointId]) { extendDraw(touchPointId, touchPoint.pageX, touchPoint.pageY); } } else if (eventObject.type == "touchend") { delete drawingStarted[touchPointId]; endDraw(touchPointId); } } }
合并以上三种代码需要注意事件名称和触控点id名称的区别,以及鼠标事件模型中并没有触控点id。
在以下合并后的代码中,我也加入了对”move”事件是否实际发生的检查。这是因为在IE10的pointer模型中,如果一个触控点始终被压下但没有移 动,即便产生完全相同的x和y坐标,MSPointerMove事件也会被持续触发。通过过滤这些冗余的“移动”,可以消除这些对 extendDraw()的无谓调用。我是这样实现这个检查的:将上一次的start或move事件中的xy坐标存储到一个lastXY的对象中,然后在 后续的事件中检查这个lastXY。lastXY此时代替了前两段示例代码中的drawingStarted对象。
var lastXY = { }; function DoEvent(eventObject) { // 阻止拖动和缩放使得绘图能够正常进行 if (eventObject.preventManipulation) eventObject.preventManipulation(); else eventObject.preventDefault(); // 如果存在changedTouches数组,则使用它,否则使用eventObject创建一组 var touchPoints = (typeof eventObject.changedTouches != 'undefined') ? eventObject.changedTouches : [eventObject]; for (var i = 0; i < touchPoints.length; ++i) { var touchPoint = touchPoints[i]; // 获取唯一的touchPoint id,如果不存在,则使用1作为默认值 var touchPointId = (typeof touchPoint.identifier != 'undefined') ? touchPoint.identifier : (typeof touchPoint.pointerId != 'undefined') ? touchPoint.pointerId : 1; if (eventObject.type.match(/(down|start)$/i)) { // 处理mousedown, MSPointerDown,以及touchstart事件 lastXY[touchPointId] = { x: touchPoint.pageX, y: touchPoint.pageY }; startDraw(touchPointId, touchPoint.pageX, touchPoint.pageY); } else if (eventObject.type.match(/move$/i)) { // 处理mousemove, MSPointerMove,以及touchmove事件 if (lastXY[touchPointId] && !(lastXY[touchPointId].x == touchPoint.pageX && lastXY[touchPointId].y == touchPoint.pageY)) { lastXY[touchPointId] = { x: touchPoint.pageX, y: touchPoint.pageY }; extendDraw(touchPointId, touchPoint.pageX, touchPoint.pageY); } } else if (eventObject.type.match(/(up|end)$/i)) { // 处理mouseup, MSPointerUp,以及touchend事件 delete lastXY[touchPointId]; endDraw(touchPointId); } } }
上面这个例子略去了注册和接受事件以及确保它们被应用在绘图目标上的代码。要使这些代码真正能在所有的浏览器——包括IE9之前的浏览器上跑起来,则还需要一些额外的工作。感兴趣的朋友可以阅读这个跨浏览器的多点触控绘图类的最终代码。
通过同时为鼠标和触控设备编码,web开发者可以确保他们的站点工作在所有的浏览器上——无论是PC,平板还是手机。