Обнаружение левого/правого пальца на сенсорных устройствах, но разрешить прокрутку вверх/вниз
Мне нужно обнаружить и отреагировать на левые/правые пробелы, но хочу дать пользователю возможность прокрутки на одном и том же элементе, так как он перемещает палец только влево/вправо с максимальным перемещением вверх/вниз из X пикселей, он не должен прокручиваться, но когда он превышает X, он должен прокручиваться.
Итак, что я сделал:
var startX, startY, $this = $(this);
function touchmove(event) {
var touches = event.originalEvent.touches;
if (touches && touches.length) {
var deltaX = touches[0].pageX - startX;
var deltaY = touches[0].pageY - startY;
if (Math.abs(deltaY) > 50) {
$this.html('X: ' + deltaX + '<br> Y: ' + deltaY + '<br>TRUE');
$this.unbind('touchmove', touchmove);
return true;
} else {
$this.html('X: ' + deltaX + '<br> Y: ' + deltaY);
event.preventDefault();
}
}
}
function touchstart(event) {
var touches = event.originalEvent.touches;
if (touches && touches.length) {
startX = touches[0].pageX;
startY = touches[0].pageY;
$this.bind('touchmove', touchmove);
}
//event.preventDefault();
}
Но я не восстанавливаю возможность прокрутки в случае "if"...
Спасибо за любые советы.
Ответы
Ответ 1
Я написал свой собственный обработчик сенсорных событий. Может быть, это поможет вам
он проверяет:
быстрый щелчок: "фк"
проведите пальцем влево: "swl"
проведите вправо: "SWR"
проведите вверх: 'Swu'
проведите вниз: "SWD"
каждая проверка инициализирует его соответствующее событие. но вы можете прокручивать и делать все, что вы делаете в обычном режиме. у вас просто есть несколько новых событий.
вам нужно SWL SWR, я также предлагаю использовать fc (fastclick) для событий щелчка... это намного быстрее, чем обычный щелчок.
window.onload = function() {
(function(d) {
var
ce = function(e, n) {
var a = document.createEvent("CustomEvent");
a.initCustomEvent(n, true, true, e.target);
e.target.dispatchEvent(a);
a = null;
return false
},
nm = true,
sp = {
x: 0,
y: 0
},
ep = {
x: 0,
y: 0
},
touch = {
touchstart: function(e) {
sp = {
x: e.touches[0].pageX,
y: e.touches[0].pageY
}
},
touchmove: function(e) {
nm = false;
ep = {
x: e.touches[0].pageX,
y: e.touches[0].pageY
}
},
touchend: function(e) {
if (nm) {
ce(e, 'fc')
} else {
var x = ep.x - sp.x,
xr = Math.abs(x),
y = ep.y - sp.y,
yr = Math.abs(y);
if (Math.max(xr, yr) > 20) {
ce(e, (xr > yr ? (x < 0 ? 'swl' : 'swr') : (y < 0 ? 'swu' : 'swd')))
}
};
nm = true
},
touchcancel: function(e) {
nm = false
}
};
for (var a in touch) {
d.addEventListener(a, touch[a], false);
}
})(document);
//EXAMPLE OF USE
var h = function(e) {
console.log(e.type, e)
};
document.body.addEventListener('fc', h, false); // 0-50ms vs 500ms with normal click
document.body.addEventListener('swl', h, false);
document.body.addEventListener('swr', h, false);
document.body.addEventListener('swu', h, false);
document.body.addEventListener('swd', h, false);
}
в этом случае h - мой обработчик для каждого типа события, и я добавляю обработчики в тело.
за то, что я понимаю твой вопрос, ты просто должен написать
YOURELEMENT.addEventListener('swr',YOURSWIPERIGHTFUNCTION,false);
YOURELEMENT.addEventListener('swl',YOURSWIPELEFTFUNCTION,false);
для обработки нескольких элементов и одной и той же функции... просто добавьте один обработчик.
так что если у вас есть
<ul id="ul"><li>1</li><li>2</li><li>3</li></ul>
ты сделаешь:
var deleteli=function(e){
var li=e.target;
console.log('deleting '+li.textContent);
}
document.getElementById('ul').addEventListener('swl',deleteli,false);
то же самое для fc & swr
В ios есть ошибка: не используйте alert().. она будет выполнена 2 раза.
Ответ 2
В принятом ответе есть "ошибка". Если вы не используете Chrome на Android, но, например, в браузере или в веб-браузере (например, для html5-гибридного приложения), прокрутка не обнаруживается.
Я узнал, что событие не срабатывает из-за обычного поведения прокрутки. Поэтому добавление "e.preventDefault();" в touchmove зафиксировал бы это или исправление от Эрика Фуллера в принятом ответе.
Это хорошо отрезанное, но в мобильном WebApp или веб-сайте это может привести к плохому прокрутке прокрутки, потому что сенсорные события наблюдаются все время.
Итак, я решил построить что-то новое. Это не так удобно, как иметь новых слушателей событий, но это достаточно удобно для моих потребностей, и это выполняется.
function detectswipe(el,func) {
swipe_det = new Object();
swipe_det.sX = 0;
swipe_det.sY = 0;
swipe_det.eX = 0;
swipe_det.eY = 0;
var min_x = 20; //min x swipe for horizontal swipe
var max_x = 40; //max x difference for vertical swipe
var min_y = 40; //min y swipe for vertical swipe
var max_y = 50; //max y difference for horizontal swipe
var direc = "";
ele = document.getElementById(el);
ele.addEventListener('touchstart',function(e){
var t = e.touches[0];
swipe_det.sX = t.screenX;
swipe_det.sY = t.screenY;
},false);
ele.addEventListener('touchmove',function(e){
e.preventDefault();
var t = e.touches[0];
swipe_det.eX = t.screenX;
swipe_det.eY = t.screenY;
},false);
ele.addEventListener('touchend',function(e){
//horizontal detection
if ((((swipe_det.eX - min_x > swipe_det.sX) || (swipe_det.eX + min_x < swipe_det.sX)) && ((swipe_det.eY < swipe_det.sY + max_y) && (swipe_det.sY > swipe_det.eY - max_y)))) {
if(swipe_det.eX > swipe_det.sX) direc = "r";
else direc = "l";
}
//vertical detection
if ((((swipe_det.eY - min_y > swipe_det.sY) || (swipe_det.eY + min_y < swipe_det.sY)) && ((swipe_det.eX < swipe_det.sX + max_x) && (swipe_det.sX > swipe_det.eX - max_x)))) {
if(swipe_det.eY > swipe_det.sY) direc = "d";
else direc = "u";
}
if (direc != "") {
if(typeof func == 'function') func(el,direc);
}
direc = "";
},false);
}
myfunction(el,d) {
alert("you swiped on element with id '"+el+"' to "+d+" direction");
}
Чтобы использовать функцию, просто используйте ее как
detectswipe('an_element_id',myfunction);
detectswipe('an_other_element_id',my_other_function);
Если обнаружен пробой, функция "myfunction" вызывается с параметром-id и "l, r, u, d" (слева, справа, вверх, вниз).
Пример: http://jsfiddle.net/rvuayqeo/1/
Ответ 3
Все эти коды нуждаются в улучшении (например, большинство кодов, которые вы можете найти при манипуляции сенсорными клавишами).
При игре с сенсорным событием помните, что у пользователя более одного пальца, что прикосновение имеет идентификатор и что touches
список представляет все текущие штрихи на поверхности даже затрагивает , которые не перемещаются.
Итак, процесс относительно прост:
-
ontouchstart: получите первое измененное касание (не event.originalEvent.touches
свойство, но event.originalEvent.changedTouches
one). Зарегистрируйте свой идентификатор с помощью event.originalEvent.changedTouches[0].identifier
и коснитесь свойств для поиска (pageX
/pageY
или clientX
/clientY
, которые очень полезны в сочетании с методом DOMElement.getBoundingClientRect()
);
-
ontouchmove: убедитесь, что текущее касание находится в списке changedTouches с помощью event.originalEvent.changedTouches.identifiedTouch( identifier )
. Если он ничего не возвращает, это означает, что пользователь переместил другое касание (а не тот, который вы ищете). Также регистрируйте свойства касания, чтобы искать и делать все, что хотите.
-
ontouchend: опять же, вы должны быть уверены, что текущее касание находится в списке changedTouches. Сделайте работу с сенсорными свойствами и, наконец, отбросьте свой текущий идентификатор касания.
Если вы хотите сделать это сильнее, рассмотрите несколько касаний (не только один) для наблюдения.
Дополнительная информация о TouchEvent, TouchList и Touch on: https://developer.mozilla.org/en-US/docs/Web/Guide/Events/Touch_events
Ответ 4
Вдохновленный @cocco, я создал лучшую (не минимизированную) версию:
(function(d) {
// based on original source: https://stackoverflow.com/a/17567696/334451
var newEvent = function(e, name) {
// This style is already deprecated but very well supported in real world: https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent/initCustomEvent
// in future we want to use CustomEvent function: https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent/CustomEvent
var a = document.createEvent("CustomEvent");
a.initCustomEvent(name, true, true, e.target);
e.target.dispatchEvent(a);
a = null;
return false
};
var debug = false; // emit info to JS console for all touch events?
var active = false; // flag to tell if touchend should complete the gesture
var min_gesture_length = 20; // minimum gesture length in pixels
var tolerance = 0.3; // value 0 means pixel perfect movement up or down/left or right is required, 0.5 or more means any diagonal will do, values between can be tweaked
var sp = { x: 0, y: 0, px: 0, py: 0 }; // start point
var ep = { x: 0, y: 0, px: 0, py: 0 }; // end point
var touch = {
touchstart: function(e) {
active = true;
t = e.touches[0];
sp = { x: t.screenX, y: t.screenY, px: t.pageX, py: t.pageY };
ep = sp; // make sure we have a sensible end poin in case next event is touchend
debug && console.log("start", sp);
},
touchmove: function(e) {
if (e.touches.length > 1) {
active = false;
debug && console.log("aborting gesture because multiple touches detected");
return;
}
t = e.touches[0];
ep = { x: t.screenX, y: t.screenY, px: t.pageX, py: t.pageY };
debug && console.log("move", ep, sp);
},
touchend: function(e) {
if (!active)
return;
debug && console.log("end", ep, sp);
var dx = Math.abs(ep.x - sp.x);
var dy = Math.abs(ep.y - sp.y);
if (Math.max(dx, dy) < min_gesture_length) {
debug && console.log("ignoring short gesture");
return; // too short gesture, ignore
}
if (dy > dx && dx/dy < tolerance && Math.abs(sp.py - ep.py) > min_gesture_length) { // up or down, ignore if page scrolled with touch
newEvent(e, (ep.y - sp.y < 0 ? 'gesture-up' : 'gesture-down'));
//e.cancelable && e.preventDefault();
}
else if (dx > dy && dy/dx < tolerance && Math.abs(sp.px - ep.px) > min_gesture_length) { // left or right, ignore if page scrolled with touch
newEvent(e, (ep.x - sp.x < 0 ? 'gesture-left' : 'gesture-right'));
//e.cancelable && e.preventDefault();
}
else {
debug && console.log("ignoring diagonal gesture or scrolled content");
}
active = false;
},
touchcancel: function(e) {
debug && console.log("cancelling gesture");
active = false;
}
};
for (var a in touch) {
d.addEventListener(a, touch[a], false);
// TODO: MSIE touch support: https://github.com/CamHenlin/TouchPolyfill
}
})(window.document);
Важные изменения по сравнению с оригинальной версией @cocco:
- используйте
event.touches[0].screenX/screenY
в качестве основного источника информации. pageX/pageY
не правильно отображают движение касаний на экране, потому что если какой-то фрагмент страницы прокручивается касанием, это также влияет на значения pageX/pageY
. - добавить настройку минимальной длины жеста
- добавить настройку допуска для игнорирования жестов рядом с диагональю
- игнорировать жест, если содержимое страницы прокручивалось с помощью жеста (проверьте разницу в
pageX/pageY
до запуска жеста) - отменить жест, если во время жеста выполняется несколько касаний
Что нужно сделать в будущем:
- используйте интерфейс функции
CustomEvent()
вместо метода createEvent()
. - добавить совместимость MSIE
- Может быть, настроить минимальную длину жеста для
pageX/pageY
отдельно от screenX/screenY
? - Кажется, что прокрутка Chrome с резьбой все еще вызывает некоторые проблемы с обнаружением прокрутки, если движение касания слишком быстрое. Возможно, дождитесь следующего кадра, прежде чем решить, куда пошла прокрутка, прежде чем решить, следует ли инициировать событие?
Использование заключается в следующем:
document.body.addEventListener('gesture-right', function (e) { ... });
или стиль JQuery
$("article").on("gesture-down", function (e) { ... });
Ответ 5
Обнаружение влево и вправо при касании неподвижное.
Это делается с сохранением последней позиции и использованием таймаута для удаления последней позиции после остановки сенсорной остановки.
var currentX;
var lastX = 0;
var lastT;
$(document).bind('touchmove', function(e) {
// If still moving clear last setTimeout
clearTimeout(lastT);
currentX = e.originalEvent.touches[0].clientX;
// After stoping or first moving
if(lastX == 0) {
lastX = currentX;
}
if(currentX < lastX) {
// Left
} else if(currentX > lastX){
// Right
}
// Save last position
lastX = currentX;
// Check if moving is done
lastT = setTimeout(function() {
lastX = 0;
}, 100);
});