应对移动设备上的触摸事件,劫持正常滚动 [英] fighting touch events on mobile devices, highjacking normal scroll
问题描述
我多次检查了代码,但找不到基于触摸设备的代码失败的原因:
I went through the code several times and I cannot find the reason it fails on touch based devices:
/**
* Initialize touch event listener.
*
* @returns {Plugin}
*/
touch: function () {
var self = this;
this._$body.bind('touchstart', function (event) {
var startEvent = event.originalEvent.touches[0];
event.preventDefault();
self._$body.bind('touchmove', function (event) {
var moveEvent = event.originalEvent.touches[0];
var diff = { x: startEvent.clientX - moveEvent.clientX, y: startEvent.clientY - moveEvent.clientY };
var nextStep;
event.preventDefault();
if ((diff.y <= -100 || diff.y >= 100) && Math.abs(diff.y) > Math.abs(diff.x)) {
nextStep = diff.y < 0 ? self._currentStep - 1 : self._currentStep + 1;
self.customScrollTo(nextStep);
}
return false;
});
return false;
});
return this;
},
演示(自签名的ssl,请放心!): https://sandbox.idev. ge/roomshotel/html5_v3/
demo (self signed ssl, don't worry!): https://sandbox.idev.ge/roomshotel/html5_v3/
问题:激活触摸后,滚动会直接跳到底部.
Problem: Scroll jumps straight to bottom when touch is activated.
预期结果:一键互动相当于滚动了1个部分.
Expected result: One touch interaction equals 1 section scrolled.
有什么想法吗?
推荐答案
我也认为touchmove
事件的回调在每次触摸时都会触发.通过从该函数返回false
,您只能取消该单个触摸移动事件,而不能取消所有随后的触摸移动事件.
I too think the touchmove
event's callback is being fired on every touch move. By returning false
from that function you only cancel that single touch move event and not all following touch move events.
您不能使用touchend
事件,因为您希望在指针移动100px后立即调用self.customScrollTo(nextStep);
.
You cannot use a touchend
event since you want to call self.customScrollTo(nextStep);
as soon as the pointer has travelled 100px.
您希望阻止在指针移动100px之后执行touchmove
回调,这可以通过多种方式完成,例如.
You want to prevent your touchmove
callback from being executed after the pointer has travelled 100px, this can be done in many ways, ie.
- 使用像
var trackPointer = true;
这样的标志变量,检查该标志 每次触发touchmove
时,当 指针已移动100像素. - 当指针移动100像素时,
将
startEvent
设置为null
并在touchmove
. - 当指针具有
touchmove
事件时解除绑定 跑了100像素.
- Using a flag variable like
var trackPointer = true;
, check this flag each timetouchmove
is being triggered and set this flag tofalse
when the pointer has travelled 100px. - When the pointer has travelled 100px,
set the
startEvent
tonull
and check this variable ontouchmove
. - Unbind the
touchmove
event when the pointer has travelled 100px.
NB:touchmove
事件被绑定每次 touchstart
都在此元素上触发,这些事件不会互相覆盖但会堆叠在一起!因此,您可能想考虑只绑定一次事件(即在DOM就绪时),或者在不再需要时取消绑定事件.
NB: The touchmove
event is being bound each time touchstart
is triggered on this element, these events do not overwrite each other but get stacked! So you might want to consider binding the event only once (ie. on DOM ready) or unbind the event when it's no longer necessary.
后者可能是最简单的,可以做到.在touchend
上(使用名称空间只是为了确保不取消绑定其他脚本绑定的相同事件):
The latter is probably the easiest and could be done ie. on touchend
(use namespaces just to be sure to not unbind the same events bound by other scripts):
// Unbind all touchmove.myNameSpace events on touchend.myNameSpace.
self._$body.bind('touchend.myNameSpace').function (event) {
self._$body.unbind('touchmove.myNameSpace');
});
,并且当指针移动了100px时:
and when the pointer has travelled 100px:
self.customScrollTo(nextStep);
// Unbind all touchmove.myNameSpace events.
self._$body.unbind('touchmove.myNameSpace');
由于当指针位于元素外部时不会触发"touchend"(我不确定touchmove
),因此您可能还想在绑定之前取消绑定:
Since 'touchend' is not triggered when the pointer is outside the element (I am not sure about touchmove
), you might also want to unbind right before binding:
event.preventDefault();
// Unbind all touchmove.myNameSpace events and (re)bind touchmove.myNameSpace event.
self._$body.unbind('touchmove.myNameSpace').bind('touchmove.myNameSpace', function (event) {
var moveEvent = event.originalEvent.touches[0];
因此您可以尝试(我尚未测试过):
So you could try (I have not tested it):
/**
* Initialize touch event listener.
*
* @returns {Plugin}
*/
touch: function () {
var self = this;
this._$body.bind('touchstart', function (event) {
var startEvent = event.originalEvent.touches[0];
event.preventDefault();
self._$body.unbind('touchmove.myNameSpace').bind('touchmove.myNameSpace', function (event) {
var moveEvent = event.originalEvent.touches[0];
var diff = { x: startEvent.clientX - moveEvent.clientX, y: startEvent.clientY - moveEvent.clientY };
var nextStep;
event.preventDefault(); // <- Not necessary since you completely cancel the event by returning false.
if ((diff.y <= -100 || diff.y >= 100) && Math.abs(diff.y) > Math.abs(diff.x)) {
nextStep = diff.y < 0 ? self._currentStep - 1 : self._currentStep + 1;
self.customScrollTo(nextStep);
// Unbind all touchmove.myNameSpace events.
self._$body.unbind('touchmove.myNameSpace');
}
return false;
});
return false;
});
// Unbind all touchmove.myNameSpace events on touchend.myNameSpace.
self._$body.bind('touchend.myNameSpace').function (event) {
self._$body.unbind('touchmove.myNameSpace');
});
return this;
},
PS:您可能希望使用HammerJS之类的库( https://github.com/hammerjs/Hammer.js ),使手势可以在浏览器上以及在非触摸设备上正常工作.
PS: You might want to use a library like HammerJS (https://github.com/hammerjs/hammer.js) to make gestures work cross browser and also on non-touch devices.
这篇关于应对移动设备上的触摸事件,劫持正常滚动的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!