544 lines
18 KiB
JavaScript
544 lines
18 KiB
JavaScript
|
var ua = navigator.userAgent.toLowerCase()
|
|||
|
//http://stackoverflow.com/questions/9038625/detect-if-device-is-ios
|
|||
|
function iOSversion() {
|
|||
|
//https://developer.apple.com/library/prerelease/mac/releasenotes/General/WhatsNewInSafari/Articles/Safari_9.html
|
|||
|
//http://mp.weixin.qq.com/s?__biz=MzA3MDQ4MzQzMg==&mid=256900619&idx=1&sn=b29f84cff0b8d7b9742e5d8b3cd8f218&scene=1&srcid=1009F9l4gh9nZ7rcQJEhmf7Q#rd
|
|||
|
if (/iPad|iPhone|iPod/i.test(ua) && !window.MSStream) {
|
|||
|
if ("backdropFilter" in document.documentElement.style) {
|
|||
|
return 9
|
|||
|
}
|
|||
|
if (!!window.indexedDB) {
|
|||
|
return 8
|
|||
|
}
|
|||
|
if (!!window.SpeechSynthesisUtterance) {
|
|||
|
return 7
|
|||
|
}
|
|||
|
if (!!window.webkitAudioContext) {
|
|||
|
return 6
|
|||
|
}
|
|||
|
if (!!window.matchMedia) {
|
|||
|
return 5
|
|||
|
}
|
|||
|
if (!!window.history && 'pushState' in window.history) {
|
|||
|
return 4
|
|||
|
}
|
|||
|
return 3
|
|||
|
}
|
|||
|
return NaN
|
|||
|
}
|
|||
|
|
|||
|
var deviceIsAndroid = ua.indexOf('android') > 0
|
|||
|
var deviceIsIOS = iOSversion()
|
|||
|
|
|||
|
var Recognizer = yua.gestureHooks = {
|
|||
|
pointers: {},
|
|||
|
//以AOP切入touchstart, touchmove, touchend, touchcancel回调
|
|||
|
start: function (event, callback) {
|
|||
|
|
|||
|
//touches是当前屏幕上所有触摸点的列表;
|
|||
|
//targetTouches是当前对象上所有触摸点的列表;
|
|||
|
//changedTouches是涉及当前事件的触摸点的列表。
|
|||
|
for (var i = 0; i < event.changedTouches.length; i++) {
|
|||
|
var touch = event.changedTouches[i],
|
|||
|
id = touch.identifier,
|
|||
|
pointer = {
|
|||
|
startTouch: mixLocations({}, touch),
|
|||
|
startTime: Date.now(),
|
|||
|
status: 'tapping',
|
|||
|
element: event.target,
|
|||
|
pressingHandler: Recognizer.pointers[id] && Recognizer.pointers[id].pressingHandler
|
|||
|
}
|
|||
|
Recognizer.pointers[id] = pointer;
|
|||
|
callback(pointer, touch)
|
|||
|
|
|||
|
}
|
|||
|
},
|
|||
|
move: function (event, callback) {
|
|||
|
for (var i = 0; i < event.changedTouches.length; i++) {
|
|||
|
var touch = event.changedTouches[i]
|
|||
|
var pointer = Recognizer.pointers[touch.identifier]
|
|||
|
if (!pointer) {
|
|||
|
return
|
|||
|
}
|
|||
|
|
|||
|
if (!("lastTouch" in pointer)) {
|
|||
|
pointer.lastTouch = pointer.startTouch
|
|||
|
pointer.lastTime = pointer.startTime
|
|||
|
pointer.deltaX = pointer.deltaY = pointer.duration = pointer.distance = 0
|
|||
|
}
|
|||
|
|
|||
|
var time = Date.now() - pointer.lastTime
|
|||
|
|
|||
|
if (time > 0) {
|
|||
|
|
|||
|
var RECORD_DURATION = 70
|
|||
|
if (time > RECORD_DURATION) {
|
|||
|
time = RECORD_DURATION
|
|||
|
}
|
|||
|
if (pointer.duration + time > RECORD_DURATION) {
|
|||
|
pointer.duration = RECORD_DURATION - time
|
|||
|
}
|
|||
|
|
|||
|
pointer.duration += time;
|
|||
|
pointer.lastTouch = mixLocations({}, touch)
|
|||
|
|
|||
|
pointer.lastTime = Date.now()
|
|||
|
|
|||
|
pointer.deltaX = touch.clientX - pointer.startTouch.clientX
|
|||
|
pointer.deltaY = touch.clientY - pointer.startTouch.clientY
|
|||
|
var x = pointer.deltaX * pointer.deltaX
|
|||
|
var y = pointer.deltaY * pointer.deltaY
|
|||
|
pointer.distance = Math.sqrt(x + y)
|
|||
|
pointer.isVertical = x < y
|
|||
|
|
|||
|
callback(pointer, touch)
|
|||
|
}
|
|||
|
}
|
|||
|
},
|
|||
|
end: function (event, callback) {
|
|||
|
for (var i = 0; i < event.changedTouches.length; i++) {
|
|||
|
var touch = event.changedTouches[i],
|
|||
|
id = touch.identifier,
|
|||
|
pointer = Recognizer.pointers[id]
|
|||
|
|
|||
|
if (!pointer)
|
|||
|
continue
|
|||
|
|
|||
|
callback(pointer, touch)
|
|||
|
|
|||
|
delete Recognizer.pointers[id]
|
|||
|
}
|
|||
|
},
|
|||
|
//人工触发合成事件
|
|||
|
fire: function (elem, type, props) {
|
|||
|
if (elem) {
|
|||
|
var event = document.createEvent('Events')
|
|||
|
event.initEvent(type, true, true)
|
|||
|
yua.mix(event, props)
|
|||
|
elem.dispatchEvent(event)
|
|||
|
}
|
|||
|
},
|
|||
|
//添加各种识别器
|
|||
|
add: function (name, recognizer) {
|
|||
|
function move(event) {
|
|||
|
recognizer.touchmove(event)
|
|||
|
}
|
|||
|
|
|||
|
function end(event) {
|
|||
|
recognizer.touchend(event)
|
|||
|
|
|||
|
document.removeEventListener('touchmove', move)
|
|||
|
|
|||
|
document.removeEventListener('touchend', end)
|
|||
|
|
|||
|
document.removeEventListener('touchcancel', cancel)
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
function cancel(event) {
|
|||
|
recognizer.touchcancel(event)
|
|||
|
|
|||
|
document.removeEventListener('touchmove', move)
|
|||
|
|
|||
|
document.removeEventListener('touchend', end)
|
|||
|
|
|||
|
document.removeEventListener('touchcancel', cancel)
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
recognizer.events.forEach(function (eventName) {
|
|||
|
yua.eventHooks[eventName] = {
|
|||
|
fix: function (el, fn) {
|
|||
|
if (!el['touch-' + name]) {
|
|||
|
el['touch-' + name] = '1'
|
|||
|
el.addEventListener('touchstart', function (event) {
|
|||
|
recognizer.touchstart(event)
|
|||
|
|
|||
|
document.addEventListener('touchmove', move)
|
|||
|
|
|||
|
document.addEventListener('touchend', end)
|
|||
|
|
|||
|
document.addEventListener('touchcancel', cancel)
|
|||
|
|
|||
|
})
|
|||
|
}
|
|||
|
return fn
|
|||
|
}
|
|||
|
}
|
|||
|
})
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
var locations = ['screenX', 'screenY', 'clientX', 'clientY', 'pageX', 'pageY']
|
|||
|
|
|||
|
// 复制 touch 对象上的有用属性到固定对象上
|
|||
|
function mixLocations(target, source) {
|
|||
|
if (source) {
|
|||
|
locations.forEach(function (key) {
|
|||
|
target[key] = source[key]
|
|||
|
})
|
|||
|
}
|
|||
|
return target
|
|||
|
}
|
|||
|
|
|||
|
var supportPointer = !!navigator.pointerEnabled || !!navigator.msPointerEnabled
|
|||
|
|
|||
|
if (supportPointer) { // 支持pointer的设备可用样式来取消click事件的300毫秒延迟
|
|||
|
root.style.msTouchAction = root.style.touchAction = 'none'
|
|||
|
}
|
|||
|
var tapRecognizer = {
|
|||
|
events: ['tap'],
|
|||
|
touchBoundary: 10,
|
|||
|
tapDelay: 200,
|
|||
|
needClick: function(target) {
|
|||
|
//判定是否使用原生的点击事件, 否则使用sendClick方法手动触发一个人工的点击事件
|
|||
|
switch (target.nodeName.toLowerCase()) {
|
|||
|
case 'button':
|
|||
|
case 'select':
|
|||
|
case 'textarea':
|
|||
|
if (target.disabled) {
|
|||
|
return true
|
|||
|
}
|
|||
|
|
|||
|
break;
|
|||
|
case 'input':
|
|||
|
// IOS6 pad 上选择文件,如果不是原生的click,弹出的选择界面尺寸错误
|
|||
|
if ((deviceIsIOS && target.type === 'file') || target.disabled) {
|
|||
|
return true
|
|||
|
}
|
|||
|
|
|||
|
break;
|
|||
|
case 'label':
|
|||
|
case 'iframe':
|
|||
|
case 'video':
|
|||
|
return true
|
|||
|
}
|
|||
|
|
|||
|
return false
|
|||
|
},
|
|||
|
needFocus: function(target) {
|
|||
|
switch (target.nodeName.toLowerCase()) {
|
|||
|
case 'textarea':
|
|||
|
case 'select': //实测android下select也需要
|
|||
|
return true;
|
|||
|
case 'input':
|
|||
|
switch (target.type) {
|
|||
|
case 'button':
|
|||
|
case 'checkbox':
|
|||
|
case 'file':
|
|||
|
case 'image':
|
|||
|
case 'radio':
|
|||
|
case 'submit':
|
|||
|
return false
|
|||
|
}
|
|||
|
//如果是只读或disabled状态,就无须获得焦点了
|
|||
|
return !target.disabled && !target.readOnly
|
|||
|
default:
|
|||
|
return false
|
|||
|
}
|
|||
|
},
|
|||
|
focus: function(targetElement) {
|
|||
|
var length;
|
|||
|
//在iOS7下, 对一些新表单元素(如date, datetime, time, month)调用focus方法会抛错,
|
|||
|
//幸好的是,我们可以改用setSelectionRange获取焦点, 将光标挪到文字的最后
|
|||
|
var type = targetElement.type
|
|||
|
if (deviceIsIOS && targetElement.setSelectionRange &&
|
|||
|
type.indexOf('date') !== 0 && type !== 'time' && type !== 'month') {
|
|||
|
length = targetElement.value.length
|
|||
|
targetElement.setSelectionRange(length, length)
|
|||
|
} else {
|
|||
|
targetElement.focus()
|
|||
|
}
|
|||
|
},
|
|||
|
findControl: function(labelElement) {
|
|||
|
// 获取label元素所对应的表单元素
|
|||
|
// 可以能过control属性, getElementById, 或用querySelector直接找其内部第一表单元素实现
|
|||
|
if (labelElement.control !== undefined) {
|
|||
|
return labelElement.control
|
|||
|
}
|
|||
|
|
|||
|
if (labelElement.htmlFor) {
|
|||
|
return document.getElementById(labelElement.htmlFor)
|
|||
|
}
|
|||
|
|
|||
|
return labelElement.querySelector('button, input:not([type=hidden]), keygen, meter, output, progress, select, textarea')
|
|||
|
},
|
|||
|
fixTarget: function(target) {
|
|||
|
if (target.nodeType === 3) {
|
|||
|
return target.parentNode
|
|||
|
}
|
|||
|
if (window.SVGElementInstance && (target instanceof SVGElementInstance)) {
|
|||
|
return target.correspondingUseElement;
|
|||
|
}
|
|||
|
|
|||
|
return target
|
|||
|
},
|
|||
|
updateScrollParent: function(targetElement) {
|
|||
|
//如果事件源元素位于某一个有滚动条的祖父元素中,那么保持其scrollParent与scrollTop值
|
|||
|
var scrollParent = targetElement.tapScrollParent
|
|||
|
|
|||
|
if (!scrollParent || !scrollParent.contains(targetElement)) {
|
|||
|
var parentElement = targetElement
|
|||
|
do {
|
|||
|
if (parentElement.scrollHeight > parentElement.offsetHeight) {
|
|||
|
scrollParent = parentElement
|
|||
|
targetElement.tapScrollParent = parentElement
|
|||
|
break
|
|||
|
}
|
|||
|
|
|||
|
parentElement = parentElement.parentElement
|
|||
|
} while (parentElement)
|
|||
|
}
|
|||
|
|
|||
|
if (scrollParent) {
|
|||
|
scrollParent.lastScrollTop = scrollParent.scrollTop
|
|||
|
}
|
|||
|
},
|
|||
|
touchHasMoved: function(event) {
|
|||
|
//判定是否发生移动,其阀值是10px
|
|||
|
var touch = event.changedTouches[0],
|
|||
|
boundary = tapRecognizer.touchBoundary
|
|||
|
return Math.abs(touch.pageX - tapRecognizer.pageX) > boundary ||
|
|||
|
Math.abs(touch.pageY - tapRecognizer.pageY) > boundary
|
|||
|
|
|||
|
},
|
|||
|
|
|||
|
findType: function(targetElement) {
|
|||
|
// 安卓chrome浏览器上,模拟的 click 事件不能让 select 打开,故使用 mousedown 事件
|
|||
|
return deviceIsAndroid && targetElement.tagName.toLowerCase() === 'select' ?
|
|||
|
'mousedown' : 'click'
|
|||
|
},
|
|||
|
sendClick: function(targetElement, event) {
|
|||
|
// 在click之前触发tap事件
|
|||
|
Recognizer.fire(targetElement, 'tap', {
|
|||
|
touchEvent: event
|
|||
|
})
|
|||
|
var clickEvent, touch
|
|||
|
//某些安卓设备必须先移除焦点,之后模拟的click事件才能让新元素获取焦点
|
|||
|
if (document.activeElement && document.activeElement !== targetElement) {
|
|||
|
document.activeElement.blur()
|
|||
|
}
|
|||
|
|
|||
|
touch = event.changedTouches[0]
|
|||
|
// 手动触发点击事件,此时必须使用document.createEvent('MouseEvents')来创建事件
|
|||
|
// 及使用initMouseEvent来初始化它
|
|||
|
clickEvent = document.createEvent('MouseEvents')
|
|||
|
clickEvent.initMouseEvent(tapRecognizer.findType(targetElement), true, true, window, 1, touch.screenX,
|
|||
|
touch.screenY, touch.clientX, touch.clientY, false, false, false, false, 0, null)
|
|||
|
clickEvent.touchEvent = event
|
|||
|
targetElement.dispatchEvent(clickEvent)
|
|||
|
},
|
|||
|
touchstart: function(event) {
|
|||
|
//忽略多点触摸
|
|||
|
if (event.targetTouches.length !== 1) {
|
|||
|
return true
|
|||
|
}
|
|||
|
//修正事件源对象
|
|||
|
var targetElement = tapRecognizer.fixTarget(event.target)
|
|||
|
var touch = event.targetTouches[0]
|
|||
|
if (deviceIsIOS) {
|
|||
|
// 判断是否是点击文字,进行选择等操作,如果是,不需要模拟click
|
|||
|
var selection = window.getSelection();
|
|||
|
if (selection.rangeCount && !selection.isCollapsed) {
|
|||
|
return true
|
|||
|
}
|
|||
|
var id = touch.identifier
|
|||
|
//当 alert 或 confirm 时,点击其他地方,会触发touch事件,identifier相同,此事件应该被忽略
|
|||
|
if (id && isFinite(tapRecognizer.lastTouchIdentifier) && tapRecognizer.lastTouchIdentifier === id) {
|
|||
|
event.preventDefault()
|
|||
|
return false
|
|||
|
}
|
|||
|
|
|||
|
tapRecognizer.lastTouchIdentifier = id
|
|||
|
|
|||
|
tapRecognizer.updateScrollParent(targetElement)
|
|||
|
}
|
|||
|
//收集触摸点的信息
|
|||
|
tapRecognizer.status = "tapping"
|
|||
|
tapRecognizer.startTime = Date.now()
|
|||
|
tapRecognizer.element = targetElement
|
|||
|
tapRecognizer.pageX = touch.pageX
|
|||
|
tapRecognizer.pageY = touch.pageY
|
|||
|
// 如果点击太快,阻止双击带来的放大收缩行为
|
|||
|
if ((tapRecognizer.startTime - tapRecognizer.lastTime) < tapRecognizer.tapDelay) {
|
|||
|
event.preventDefault()
|
|||
|
}
|
|||
|
},
|
|||
|
touchmove: function(event) {
|
|||
|
if (tapRecognizer.status !== "tapping") {
|
|||
|
return true
|
|||
|
}
|
|||
|
// 如果事件源元素发生改变,或者发生了移动,那么就取消触发点击事件
|
|||
|
if (tapRecognizer.element !== tapRecognizer.fixTarget(event.target) ||
|
|||
|
tapRecognizer.touchHasMoved(event)) {
|
|||
|
tapRecognizer.status = tapRecognizer.element = 0
|
|||
|
}
|
|||
|
|
|||
|
},
|
|||
|
touchend: function(event) {
|
|||
|
var targetElement = tapRecognizer.element
|
|||
|
var now = Date.now()
|
|||
|
//如果是touchstart与touchend相隔太久,可以认为是长按,那么就直接返回
|
|||
|
//或者是在touchstart, touchmove阶段,判定其不该触发点击事件,也直接返回
|
|||
|
if (!targetElement || now - tapRecognizer.startTime > tapRecognizer.tapDelay) {
|
|||
|
return true
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
tapRecognizer.lastTime = now
|
|||
|
|
|||
|
var startTime = tapRecognizer.startTime
|
|||
|
tapRecognizer.status = tapRecognizer.startTime = 0
|
|||
|
|
|||
|
targetTagName = targetElement.tagName.toLowerCase()
|
|||
|
if (targetTagName === 'label') {
|
|||
|
//尝试触发label上可能绑定的tap事件
|
|||
|
Recognizer.fire(targetElement, 'tap', {
|
|||
|
touchEvent: event
|
|||
|
})
|
|||
|
var forElement = tapRecognizer.findControl(targetElement)
|
|||
|
if (forElement) {
|
|||
|
tapRecognizer.focus(targetElement)
|
|||
|
targetElement = forElement
|
|||
|
}
|
|||
|
} else if (tapRecognizer.needFocus(targetElement)) {
|
|||
|
// 如果元素从touchstart到touchend经历时间过长,那么不应该触发点击事
|
|||
|
// 或者此元素是iframe中的input元素,那么它也无法获点焦点
|
|||
|
if ((now - startTime) > 100 || (deviceIsIOS && window.top !== window && targetTagName === 'input')) {
|
|||
|
tapRecognizer.element = 0
|
|||
|
return false
|
|||
|
}
|
|||
|
|
|||
|
tapRecognizer.focus(targetElement)
|
|||
|
deviceIsAndroid && tapRecognizer.sendClick(targetElement, event)
|
|||
|
|
|||
|
return false
|
|||
|
}
|
|||
|
|
|||
|
if (deviceIsIOS) {
|
|||
|
//如果它的父容器的滚动条发生改变,那么应该识别为划动或拖动事件,不应该触发点击事件
|
|||
|
var scrollParent = targetElement.tapScrollParent;
|
|||
|
if (scrollParent && scrollParent.lastScrollTop !== scrollParent.scrollTop) {
|
|||
|
return true
|
|||
|
}
|
|||
|
}
|
|||
|
//如果这不是一个需要使用原生click的元素,则屏蔽原生事件,避免触发两次click
|
|||
|
if (!tapRecognizer.needClick(targetElement)) {
|
|||
|
event.preventDefault()
|
|||
|
// 触发一次模拟的click
|
|||
|
tapRecognizer.sendClick(targetElement, event)
|
|||
|
}
|
|||
|
},
|
|||
|
touchcancel: function() {
|
|||
|
tapRecognizer.startTime = tapRecognizer.element = 0
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
Recognizer.add("tap", tapRecognizer)
|
|||
|
|
|||
|
var pressRecognizer = {
|
|||
|
events: ['longtap', 'doubletap'],
|
|||
|
cancelPress: function (pointer) {
|
|||
|
clearTimeout(pointer.pressingHandler)
|
|||
|
pointer.pressingHandler = null
|
|||
|
},
|
|||
|
touchstart: function (event) {
|
|||
|
Recognizer.start(event, function (pointer, touch) {
|
|||
|
pointer.pressingHandler = setTimeout(function () {
|
|||
|
if (pointer.status === 'tapping') {
|
|||
|
Recognizer.fire(event.target, 'longtap', {
|
|||
|
touch: touch,
|
|||
|
touchEvent: event
|
|||
|
})
|
|||
|
}
|
|||
|
pressRecognizer.cancelPress(pointer)
|
|||
|
}, 800)
|
|||
|
if (event.changedTouches.length !== 1) {
|
|||
|
pointer.status = 0
|
|||
|
}
|
|||
|
})
|
|||
|
|
|||
|
},
|
|||
|
touchmove: function (event) {
|
|||
|
Recognizer.move(event, function (pointer) {
|
|||
|
if (pointer.distance > 10 && pointer.pressingHandler) {
|
|||
|
pressRecognizer.cancelPress(pointer)
|
|||
|
if (pointer.status === 'tapping') {
|
|||
|
pointer.status = 'panning'
|
|||
|
}
|
|||
|
}
|
|||
|
})
|
|||
|
},
|
|||
|
touchend: function (event) {
|
|||
|
Recognizer.end(event, function (pointer, touch) {
|
|||
|
pressRecognizer.cancelPress(pointer)
|
|||
|
if (pointer.status === 'tapping') {
|
|||
|
pointer.lastTime = Date.now()
|
|||
|
if (pressRecognizer.lastTap && pointer.lastTime - pressRecognizer.lastTap.lastTime < 300) {
|
|||
|
Recognizer.fire(pointer.element, 'doubletap', {
|
|||
|
touch: touch,
|
|||
|
touchEvent: event
|
|||
|
})
|
|||
|
}
|
|||
|
|
|||
|
pressRecognizer.lastTap = pointer
|
|||
|
}
|
|||
|
})
|
|||
|
|
|||
|
},
|
|||
|
touchcancel: function (event) {
|
|||
|
Recognizer.end(event, function (pointer) {
|
|||
|
pressRecognizer.cancelPress(pointer)
|
|||
|
})
|
|||
|
}
|
|||
|
}
|
|||
|
Recognizer.add('press', pressRecognizer)
|
|||
|
|
|||
|
var swipeRecognizer = {
|
|||
|
events: ['swipe', 'swipeleft', 'swiperight', 'swipeup', 'swipedown'],
|
|||
|
getAngle: function (x, y ) {
|
|||
|
return Math.atan2(y, x) * 180 / Math.PI
|
|||
|
},
|
|||
|
getDirection: function (x, y) {
|
|||
|
var angle = swipeRecognizer.getAngle(x, y)
|
|||
|
if ((angle < -45) && (angle > -135)) {
|
|||
|
return "up"
|
|||
|
} else if ((angle >= 45) && (angle < 315)) {
|
|||
|
return "down"
|
|||
|
} else if ((angle > -45) && (angle <= 45)) {
|
|||
|
return "right"
|
|||
|
} else{
|
|||
|
return "left"
|
|||
|
}
|
|||
|
},
|
|||
|
touchstart: function (event) {
|
|||
|
Recognizer.start(event, noop)
|
|||
|
},
|
|||
|
touchmove: function (event) {
|
|||
|
Recognizer.move(event, noop)
|
|||
|
},
|
|||
|
touchend: function (event) {
|
|||
|
if(event.changedTouches.length !== 1){
|
|||
|
return
|
|||
|
}
|
|||
|
Recognizer.end(event, function (pointer, touch) {
|
|||
|
var isflick = (pointer.distance > 30 && pointer.distance / pointer.duration > 0.65)
|
|||
|
if (isflick) {
|
|||
|
var extra = {
|
|||
|
deltaX : pointer.deltaX,
|
|||
|
deltaY: pointer.deltaY,
|
|||
|
touch: touch,
|
|||
|
touchEvent: event,
|
|||
|
direction: swipeRecognizer.getDirection(pointer.deltaX, pointer.deltaY),
|
|||
|
isVertical: pointer.isVertical
|
|||
|
}
|
|||
|
var target = pointer.element
|
|||
|
Recognizer.fire(target, 'swipe', extra)
|
|||
|
Recognizer.fire(target, 'swipe' + extra.direction, extra)
|
|||
|
}
|
|||
|
})
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
swipeRecognizer.touchcancel = swipeRecognizer.touchend
|
|||
|
Recognizer.add('swipe', swipeRecognizer)
|