Борьба с сенсорными событиями на мобильных устройствах, угон нормального прокрутки
Я несколько раз просматривал код и не могу найти причину его сбоя на сенсорных устройствах:
/**
* 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/
Проблема: прокрутка прыгает прямо на дно при активации касания.
Ожидаемый результат: взаимодействие в одно касание равняется 1 прокручиваемому разделу.
Какие-нибудь мысли?
4 ответа
Я тоже думаю touchmove
обратный вызов события запускается при каждом касании. Возвращая false
с помощью этой функции вы отменяете только одно событие перемещения касания, а не все последующие события перемещения касания.
Вы не можете использовать touchend
событие, так как вы хотите позвонить self.customScrollTo(nextStep);
как только указатель пройдёт 100px.
Вы хотите предотвратить touchmove
обратный вызов выполняется после того, как указатель прошел 100px, это можно сделать разными способами, т.е.
- Использование флаговой переменной вроде
var trackPointer = true;
проверяйте этот флаг каждый разtouchmove
срабатывает и установите этот флагfalse
когда указатель прошел 100px. - Когда указатель переместился на 100 пикселей, установите
startEvent
вnull
и проверьте эту переменную наtouchmove
, - Уберите
touchmove
событие, когда указатель переместился на 100 пикселей.
NB: touchmove
событие связывается каждый раз touchstart
на этом элементе эти события не перезаписывают друг друга, а складываются! Таким образом, вы можете захотеть связать событие только один раз (т. Е. В DOM ready) или отменить привязку события, когда оно больше не нужно.
Последнее, вероятно, самое простое и может быть сделано, т.е. на touchend
(используйте пространства имен только для того, чтобы не связывать те же события, связанные с другими скриптами):
// Unbind all touchmove.myNameSpace events on touchend.myNameSpace.
self._$body.bind('touchend.myNameSpace').function (event) {
self._$body.unbind('touchmove.myNameSpace');
});
и когда указатель переместился на 100 пикселей:
self.customScrollTo(nextStep);
// Unbind all touchmove.myNameSpace events.
self._$body.unbind('touchmove.myNameSpace');
Так как "touchend" не срабатывает, когда указатель находится вне элемента (я не уверен насчет touchmove
), вы также можете удалить привязку прямо перед привязкой:
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];
Так что вы можете попробовать (я не проверял это):
/**
* 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), чтобы жесты работали в браузере, а также на устройствах без сенсорного экрана.
Сначала я предложу вам использовать Modernizr для обнаружения прикосновения
if (Modernizr.touch) {
$("body").swipe({
//Generic swipe handler for all directions
swipe: function (event, direction, distance, duration, fingerCount, fingerData) {
if(distance >= 100) //Check the units in px or unit
{
if (direction === "up") {
self.customScrollTo(nextStep);
} else if (direction === "down") {
self.customScrollTo(prevStep);
}
}
}
});
}
Я просто ставлю логику выше. "Я" - это только ваш объект.
Для того, чтобы событие смахивания работало, вы должны добавить плагин jquery https://github.com/mattbryson/TouchSwipe-Jquery-Plugin
Я думаю, что событие touchmove запускается несколько раз, вместо этого попробуйте использовать touchend.
Относительно: http://css-tricks.com/the-javascript-behind-touch-friendly-sliders/ Я проверил ваш код в Google Chrome Live Javascript Edit. и он работал со следующей модификацией в событии touchmove.
setTimeout(function(){ self.customScrollTo(nextStep); }, 250);
/**
* Initialize touch event listener.
*
* @returns {Plugin}
*/
touch: function () {
var self = this;
var flag = false;
this._$body.bind('touchstart', function (event) {
var startEvent = event.originalEvent.touches[0];
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;
if (((diff.y <= -100 || diff.y >= 100) && Math.abs(diff.y) > Math.abs(diff.x)) && flag == true) {
nextStep = diff.y < 0 ? self._currentStep - 1 : self._currentStep + 1;
self.customScrollTo(nextStep);
startEvent = event.originalEvent.touches[0]; // Just added this line here.
}
event.preventDefault();
return false;
});
return false;
});
return this;
},
Я думаю, что требуется простое редактирование. После того, как вы превысили "diff" на 100 или -100, просто обновите значение переменной "startEvent". Поэтому я добавил заявление
startEvent = event.originalEvent.touches[0];
Поэтому, когда событие "self.customScrollTo(nextStep)" инициируется, значением "startEvent" является новое значение, которое является текущей позицией точки касания. И, следовательно, когда снова происходит событие TouchMove. Он снова получит новый набор значений для расчета. Пожалуйста, проверьте этот код и скажите мне, работает ли он или нет. Даже если это не правильно.