Як можна виявити, що користувач провев пальцем у певному напрямку по веб-сторінці з JavaScript?
Мені було цікаво, чи є одне рішення, яке буде працювати для веб-сайтів як на iPhone, так і на телефоні Android.
Як можна виявити, що користувач провев пальцем у певному напрямку по веб-сторінці з JavaScript?
Мені було цікаво, чи є одне рішення, яке буде працювати для веб-сайтів як на iPhone, так і на телефоні Android.
Відповіді:
Простий зразок коду ванілі JS:
document.addEventListener('touchstart', handleTouchStart, false);
document.addEventListener('touchmove', handleTouchMove, false);
var xDown = null;
var yDown = null;
function getTouches(evt) {
return evt.touches || // browser API
evt.originalEvent.touches; // jQuery
}
function handleTouchStart(evt) {
const firstTouch = getTouches(evt)[0];
xDown = firstTouch.clientX;
yDown = firstTouch.clientY;
};
function handleTouchMove(evt) {
if ( ! xDown || ! yDown ) {
return;
}
var xUp = evt.touches[0].clientX;
var yUp = evt.touches[0].clientY;
var xDiff = xDown - xUp;
var yDiff = yDown - yUp;
if ( Math.abs( xDiff ) > Math.abs( yDiff ) ) {/*most significant*/
if ( xDiff > 0 ) {
/* left swipe */
} else {
/* right swipe */
}
} else {
if ( yDiff > 0 ) {
/* up swipe */
} else {
/* down swipe */
}
}
/* reset values */
xDown = null;
yDown = null;
};
Тестовано в Android.
touchstart
, touchmove
?
Виходячи з відповіді @ givanse, це можна зробити так classes
:
class Swipe {
constructor(element) {
this.xDown = null;
this.yDown = null;
this.element = typeof(element) === 'string' ? document.querySelector(element) : element;
this.element.addEventListener('touchstart', function(evt) {
this.xDown = evt.touches[0].clientX;
this.yDown = evt.touches[0].clientY;
}.bind(this), false);
}
onLeft(callback) {
this.onLeft = callback;
return this;
}
onRight(callback) {
this.onRight = callback;
return this;
}
onUp(callback) {
this.onUp = callback;
return this;
}
onDown(callback) {
this.onDown = callback;
return this;
}
handleTouchMove(evt) {
if ( ! this.xDown || ! this.yDown ) {
return;
}
var xUp = evt.touches[0].clientX;
var yUp = evt.touches[0].clientY;
this.xDiff = this.xDown - xUp;
this.yDiff = this.yDown - yUp;
if ( Math.abs( this.xDiff ) > Math.abs( this.yDiff ) ) { // Most significant.
if ( this.xDiff > 0 ) {
this.onLeft();
} else {
this.onRight();
}
} else {
if ( this.yDiff > 0 ) {
this.onUp();
} else {
this.onDown();
}
}
// Reset values.
this.xDown = null;
this.yDown = null;
}
run() {
this.element.addEventListener('touchmove', function(evt) {
this.handleTouchMove(evt).bind(this);
}.bind(this), false);
}
}
Ви можете використовувати його так:
// Use class to get element by string.
var swiper = new Swipe('#my-element');
swiper.onLeft(function() { alert('You swiped left.') });
swiper.run();
// Get the element yourself.
var swiper = new Swipe(document.getElementById('#my-element'));
swiper.onLeft(function() { alert('You swiped left.') });
swiper.run();
// One-liner.
(new Swipe('#my-element')).onLeft(function() { alert('You swiped left.') }).run();
.bind
не визначено, оскільки ваш handleTouchMove
фактично нічого не повернув. Крім того, марно зв'язувати дзвінки при виклику функції, this.
оскільки це вже пов'язано з поточним контекстом
.bind(this);
і це витончено спрацювало. дякую @nicholas_r
touches[0]
changedTouches[0]
handleTouchMove
handleTouchEnd
run()
двічі, і ви отримаєте неприємний витік пам’яті
Я об'єднав декілька відповідей тут у сценарій, який використовує CustomEvent для запуску перенесених подій у DOM. Додайте на свою сторінку скрипт 0,7k swiped-events.min.js і слухайте розгорнуті події:
document.addEventListener('swiped-left', function(e) {
console.log(e.target); // the element that was swiped
});
document.addEventListener('swiped-right', function(e) {
console.log(e.target); // the element that was swiped
});
document.addEventListener('swiped-up', function(e) {
console.log(e.target); // the element that was swiped
});
document.addEventListener('swiped-down', function(e) {
console.log(e.target); // the element that was swiped
});
Ви також можете приєднати безпосередньо до елемента:
document.getElementById('myBox').addEventListener('swiped-down', function(e) {
console.log(e.target); // the element that was swiped
});
Ви можете вказати наступні атрибути, щоб налаштувати, як проведіть функцію взаємодії на своїй сторінці (вони необов’язкові) .
<div data-swipe-threshold="10"
data-swipe-timeout="1000"
data-swipe-ignore="false">
Swiper, get swiping!
</div>
Вихідний код доступний на Github
Що я раніше використовував - ви повинні виявити подію mousedown, записати її x, y місце розташування (залежно від того, що стосується), а потім виявити подію миші та відняти два значення.
jQuery Mobile також включає підтримку розгортання: http://api.jquerymobile.com/swipe/
Приклад
$("#divId").on("swipe", function(event) {
alert("It's a swipe!");
});
Я знайшов блискучу відповідь на @givanse як найбільш надійну та сумісну у кількох мобільних браузерах для реєстрації пальних дій.
Однак в його коді є зміна, необхідна для того, щоб він працював у сучасних мобільних браузерах, які використовуються jQuery
.
event.touches
не буде існувати, якщо jQuery
він використовується і приводить до нього undefined
і його слід замінити event.originalEvent.touches
. Без jQuery
, event.touches
має добре працювати.
Тож рішення стає,
document.addEventListener('touchstart', handleTouchStart, false);
document.addEventListener('touchmove', handleTouchMove, false);
var xDown = null;
var yDown = null;
function handleTouchStart(evt) {
xDown = evt.originalEvent.touches[0].clientX;
yDown = evt.originalEvent.touches[0].clientY;
};
function handleTouchMove(evt) {
if ( ! xDown || ! yDown ) {
return;
}
var xUp = evt.originalEvent.touches[0].clientX;
var yUp = evt.originalEvent.touches[0].clientY;
var xDiff = xDown - xUp;
var yDiff = yDown - yUp;
if ( Math.abs( xDiff ) > Math.abs( yDiff ) ) {/*most significant*/
if ( xDiff > 0 ) {
/* left swipe */
} else {
/* right swipe */
}
} else {
if ( yDiff > 0 ) {
/* up swipe */
} else {
/* down swipe */
}
}
/* reset values */
xDown = null;
yDown = null;
};
Тестовано на:
event.originalEvent
. Справа event.touches
перестала існувати зараз і призводить до undefined
.
event.originalEvent
. Я оновлю свою відповідь. Дякую! :)
Я перепакував TouchWipe
як короткий плагін jquery:detectSwipe
Деякі найвищі відповіді (не можу коментувати ...), щоб вирішити короткі перегони
document.addEventListener('touchstart', handleTouchStart, false);
document.addEventListener('touchmove', handleTouchMove, false);
var xDown = null;
var yDown = null;
function handleTouchStart(evt) {
xDown = evt.touches[0].clientX;
yDown = evt.touches[0].clientY;
};
function handleTouchMove(evt) {
if ( ! xDown || ! yDown ) {
return;
}
var xUp = evt.touches[0].clientX;
var yUp = evt.touches[0].clientY;
var xDiff = xDown - xUp;
var yDiff = yDown - yUp;
if(Math.abs( xDiff )+Math.abs( yDiff )>150){ //to deal with to short swipes
if ( Math.abs( xDiff ) > Math.abs( yDiff ) ) {/*most significant*/
if ( xDiff > 0 ) {/* left swipe */
alert('left!');
} else {/* right swipe */
alert('right!');
}
} else {
if ( yDiff > 0 ) {/* up swipe */
alert('Up!');
} else { /* down swipe */
alert('Down!');
}
}
/* reset values */
xDown = null;
yDown = null;
}
};
trashold, проведіть пальцем тайм-аут, swipeBlockElems додати.
document.addEventListener('touchstart', handleTouchStart, false);
document.addEventListener('touchmove', handleTouchMove, false);
document.addEventListener('touchend', handleTouchEnd, false);
const SWIPE_BLOCK_ELEMS = [
'swipBlock',
'handle',
'drag-ruble'
]
let xDown = null;
let yDown = null;
let xDiff = null;
let yDiff = null;
let timeDown = null;
const TIME_TRASHOLD = 200;
const DIFF_TRASHOLD = 130;
function handleTouchEnd() {
let timeDiff = Date.now() - timeDown;
if (Math.abs(xDiff) > Math.abs(yDiff)) { /*most significant*/
if (Math.abs(xDiff) > DIFF_TRASHOLD && timeDiff < TIME_TRASHOLD) {
if (xDiff > 0) {
// console.log(xDiff, TIME_TRASHOLD, DIFF_TRASHOLD)
SWIPE_LEFT(LEFT) /* left swipe */
} else {
// console.log(xDiff)
SWIPE_RIGHT(RIGHT) /* right swipe */
}
} else {
console.log('swipeX trashhold')
}
} else {
if (Math.abs(yDiff) > DIFF_TRASHOLD && timeDiff < TIME_TRASHOLD) {
if (yDiff > 0) {
/* up swipe */
} else {
/* down swipe */
}
} else {
console.log('swipeY trashhold')
}
}
/* reset values */
xDown = null;
yDown = null;
timeDown = null;
}
function containsClassName (evntarget , classArr) {
for (var i = classArr.length - 1; i >= 0; i--) {
if( evntarget.classList.contains(classArr[i]) ) {
return true;
}
}
}
function handleTouchStart(evt) {
let touchStartTarget = evt.target;
if( containsClassName(touchStartTarget, SWIPE_BLOCK_ELEMS) ) {
return;
}
timeDown = Date.now()
xDown = evt.touches[0].clientX;
yDown = evt.touches[0].clientY;
xDiff = 0;
yDiff = 0;
}
function handleTouchMove(evt) {
if (!xDown || !yDown) {
return;
}
var xUp = evt.touches[0].clientX;
var yUp = evt.touches[0].clientY;
xDiff = xDown - xUp;
yDiff = yDown - yUp;
}
Якщо хтось намагається використовувати jQuery Mobile на Android і має проблеми з виявленням пальців JQM
(У мене були деякі на Xperia Z1, Galaxy S3, Nexus 4 і деякі телефони Wiko), це може бути корисно:
//Fix swipe gesture on android
if(android){ //Your own device detection here
$.event.special.swipe.verticalDistanceThreshold = 500
$.event.special.swipe.horizontalDistanceThreshold = 10
}
Проведення пальцем по android не було виявлено, якщо це не дуже довгий, точний і швидкий перехід пальцем.
З цими двома рядками він працює правильно
$.event.special.swipe.scrollSupressionThreshold = 8;
але ви направляєте мене в правильному напрямку! Дякую!
У мене були проблеми з доторканнямся до оброблювача дотику, коли користувач перетягував палець. Я не знаю, чи це через щось, що я роблю неправильно, чи ні, але я перемотав це, щоб накопичити рухи за допомогою touchmove та touchchend насправді спрацьовує зворотний виклик.
Мені також потрібно було мати велику кількість цих примірників, тому я додав методи включення / відключення.
І поріг, де короткий поворот не стріляє. Нульовий сенсорний запуск - це лічильники кожного разу.
Ви можете змінити target_node на льоту. Увімкнути для створення необов’язково.
/** Usage: */
touchevent = new Modules.TouchEventClass(callback, target_node);
touchevent.enable();
touchevent.disable();
/**
*
* Touch event module
*
* @param method set_target_mode
* @param method __touchstart
* @param method __touchmove
* @param method __touchend
* @param method enable
* @param method disable
* @param function callback
* @param node target_node
*/
Modules.TouchEventClass = class {
constructor(callback, target_node, enable=false) {
/** callback function */
this.callback = callback;
this.xdown = null;
this.ydown = null;
this.enabled = false;
this.target_node = null;
/** move point counts [left, right, up, down] */
this.counts = [];
this.set_target_node(target_node);
/** Enable on creation */
if (enable === true) {
this.enable();
}
}
/**
* Set or reset target node
*
* @param string/node target_node
* @param string enable (optional)
*/
set_target_node(target_node, enable=false) {
/** check if we're resetting target_node */
if (this.target_node !== null) {
/** remove old listener */
this.disable();
}
/** Support string id of node */
if (target_node.nodeName === undefined) {
target_node = document.getElementById(target_node);
}
this.target_node = target_node;
if (enable === true) {
this.enable();
}
}
/** enable listener */
enable() {
this.enabled = true;
this.target_node.addEventListener("touchstart", this.__touchstart.bind(this));
this.target_node.addEventListener("touchmove", this.__touchmove.bind(this));
this.target_node.addEventListener("touchend", this.__touchend.bind(this));
}
/** disable listener */
disable() {
this.enabled = false;
this.target_node.removeEventListener("touchstart", this.__touchstart);
this.target_node.removeEventListener("touchmove", this.__touchmove);
this.target_node.removeEventListener("touchend", this.__touchend);
}
/** Touchstart */
__touchstart(event) {
event.stopPropagation();
this.xdown = event.touches[0].clientX;
this.ydown = event.touches[0].clientY;
/** reset count of moves in each direction, [left, right, up, down] */
this.counts = [0, 0, 0, 0];
}
/** Touchend */
__touchend(event) {
let max_moves = Math.max(...this.counts);
if (max_moves > 500) { // set this threshold appropriately
/** swipe happened */
let index = this.counts.indexOf(max_moves);
if (index == 0) {
this.callback("left");
} else if (index == 1) {
this.callback("right");
} else if (index == 2) {
this.callback("up");
} else {
this.callback("down");
}
}
}
/** Touchmove */
__touchmove(event) {
event.stopPropagation();
if (! this.xdown || ! this.ydown) {
return;
}
let xup = event.touches[0].clientX;
let yup = event.touches[0].clientY;
let xdiff = this.xdown - xup;
let ydiff = this.ydown - yup;
/** Check x or y has greater distance */
if (Math.abs(xdiff) > Math.abs(ydiff)) {
if (xdiff > 0) {
this.counts[0] += Math.abs(xdiff);
} else {
this.counts[1] += Math.abs(xdiff);
}
} else {
if (ydiff > 0) {
this.counts[2] += Math.abs(ydiff);
} else {
this.counts[3] += Math.abs(ydiff);
}
}
}
}
Я також з’єднав декілька відповідей, в основному першу та другу з класами, і ось моя версія:
export default class Swipe {
constructor(options) {
this.xDown = null;
this.yDown = null;
this.options = options;
this.handleTouchStart = this.handleTouchStart.bind(this);
this.handleTouchMove = this.handleTouchMove.bind(this);
document.addEventListener('touchstart', this.handleTouchStart, false);
document.addEventListener('touchmove', this.handleTouchMove, false);
}
onLeft() {
this.options.onLeft();
}
onRight() {
this.options.onRight();
}
onUp() {
this.options.onUp();
}
onDown() {
this.options.onDown();
}
static getTouches(evt) {
return evt.touches // browser API
}
handleTouchStart(evt) {
const firstTouch = Swipe.getTouches(evt)[0];
this.xDown = firstTouch.clientX;
this.yDown = firstTouch.clientY;
}
handleTouchMove(evt) {
if ( ! this.xDown || ! this.yDown ) {
return;
}
let xUp = evt.touches[0].clientX;
let yUp = evt.touches[0].clientY;
let xDiff = this.xDown - xUp;
let yDiff = this.yDown - yUp;
if ( Math.abs( xDiff ) > Math.abs( yDiff ) ) {/*most significant*/
if ( xDiff > 0 && this.options.onLeft) {
/* left swipe */
this.onLeft();
} else if (this.options.onRight) {
/* right swipe */
this.onRight();
}
} else {
if ( yDiff > 0 && this.options.onUp) {
/* up swipe */
this.onUp();
} else if (this.options.onDown){
/* down swipe */
this.onDown();
}
}
/* reset values */
this.xDown = null;
this.yDown = null;
}
}
Потім можна використовувати його як наступне:
let swiper = new Swipe({
onLeft() {
console.log('You swiped left.');
}
});
Це допомагає уникнути помилок консолі, коли ви хочете зателефонувати лише скажімо, метод "onLeft".
Використовували два:
jQuery mobile: працюйте в більшості випадків, і особливо, коли ви розробляєте додаток, який використовує інший плагін jQuery, тоді краще використовувати для цього мобільні елементи управління jQuery. Відвідайте його тут: https://www.w3schools.com/jquerymobile/jquerymobile_events_touch.asp
Час молотка! одна з найкращих, легких і швидких бібліотек на основі JavaScript. Відвідайте його тут: https://hammerjs.github.io/
Якщо вам просто потрібно провести пальцем, вам краще використовувати розмір, використовуючи лише потрібну частину. Це має працювати на будь-якому сенсорному пристрої.
Це ~ 450 байт 'після стиснення, мінімізації, бабеля тощо.
Я написав нижчий клас на основі інших відповідей, він використовує відсоток переміщених замість пікселів та шаблон диспетчера подій, щоб підключити / зняти речі.
Використовуйте його так:
const dispatcher = new SwipeEventDispatcher(myElement);
dispatcher.on('SWIPE_RIGHT', () => { console.log('I swiped right!') })
export class SwipeEventDispatcher {
constructor(element, options = {}) {
this.evtMap = {
SWIPE_LEFT: [],
SWIPE_UP: [],
SWIPE_DOWN: [],
SWIPE_RIGHT: []
};
this.xDown = null;
this.yDown = null;
this.element = element;
this.options = Object.assign({ triggerPercent: 0.3 }, options);
element.addEventListener('touchstart', evt => this.handleTouchStart(evt), false);
element.addEventListener('touchend', evt => this.handleTouchEnd(evt), false);
}
on(evt, cb) {
this.evtMap[evt].push(cb);
}
off(evt, lcb) {
this.evtMap[evt] = this.evtMap[evt].filter(cb => cb !== lcb);
}
trigger(evt, data) {
this.evtMap[evt].map(handler => handler(data));
}
handleTouchStart(evt) {
this.xDown = evt.touches[0].clientX;
this.yDown = evt.touches[0].clientY;
}
handleTouchEnd(evt) {
const deltaX = evt.changedTouches[0].clientX - this.xDown;
const deltaY = evt.changedTouches[0].clientY - this.yDown;
const distMoved = Math.abs(Math.abs(deltaX) > Math.abs(deltaY) ? deltaX : deltaY);
const activePct = distMoved / this.element.offsetWidth;
if (activePct > this.options.triggerPercent) {
if (Math.abs(deltaX) > Math.abs(deltaY)) {
deltaX < 0 ? this.trigger('SWIPE_LEFT') : this.trigger('SWIPE_RIGHT');
} else {
deltaY > 0 ? this.trigger('SWIPE_UP') : this.trigger('SWIPE_DOWN');
}
}
}
}
export default SwipeEventDispatcher;
Я хотів виявити лише пальцем ліворуч і праворуч, але запустити дію лише тоді, коли сенсорне подія закінчиться , тому я трохи змінив чудову відповідь @ givanse для цього.
Навіщо це робити? Якщо, наприклад, під час перенесення користувач помічає, що він нарешті не хоче проводити пальцем, він може перемістити палець у вихідне положення (це робить дуже популярна програма для знайомств ";"), а потім "проведіть пальцем праворуч" подія скасовується.
Отже, щоб уникнути події "правого пальця" лише тому, що горизонтально є різниця в 3 пікселів, я додав поріг, під яким подія відкидається: щоб мати подію "праворуч вправо", користувач повинен провести пальцем принаймні 1/3 ширини браузера (звичайно, ви можете це змінити).
Усі ці дрібні деталі покращують користувацький досвід. Ось код (Vanilla JS):
var xDown = null, yDown = null, xUp = null, yUp = null;
document.addEventListener('touchstart', touchstart, false);
document.addEventListener('touchmove', touchmove, false);
document.addEventListener('touchend', touchend, false);
function touchstart(evt) { const firstTouch = (evt.touches || evt.originalEvent.touches)[0]; xDown = firstTouch.clientX; yDown = firstTouch.clientY; }
function touchmove(evt) { if (!xDown || !yDown ) return; xUp = evt.touches[0].clientX; yUp = evt.touches[0].clientY; }
function touchend(evt) {
var xDiff = xUp - xDown, yDiff = yUp - yDown;
if ((Math.abs(xDiff) > Math.abs(yDiff)) && (Math.abs(xDiff) > 0.33 * document.body.clientWidth)) {
if (xDiff < 0)
document.getElementById('leftnav').click();
else
document.getElementById('rightnav').click();
}
xDown = null, yDown = null;
}
Простий приклад JS ванілі для горизонтального пальця:
let touchstartX = 0
let touchendX = 0
const slider = document.getElementById('slider')
function handleGesure() {
if (touchendX < touchstartX) alert('swiped left!')
if (touchendX > touchstartX) alert('swiped right!')
}
slider.addEventListener('touchstart', e => {
touchstartX = e.changedTouches[0].screenX
})
slider.addEventListener('touchend', e => {
touchendX = e.changedTouches[0].screenX
handleGesure()
})
Ви можете використовувати таку ж логіку для вертикального переведення.
Додайте сюди цю відповідь . Цей додає підтримку подій миші для тестування на робочому столі:
<!--scripts-->
class SwipeEventDispatcher {
constructor(element, options = {}) {
this.evtMap = {
SWIPE_LEFT: [],
SWIPE_UP: [],
SWIPE_DOWN: [],
SWIPE_RIGHT: []
};
this.xDown = null;
this.yDown = null;
this.element = element;
this.isMouseDown = false;
this.listenForMouseEvents = true;
this.options = Object.assign({ triggerPercent: 0.3 }, options);
element.addEventListener('touchstart', evt => this.handleTouchStart(evt), false);
element.addEventListener('touchend', evt => this.handleTouchEnd(evt), false);
element.addEventListener('mousedown', evt => this.handleMouseDown(evt), false);
element.addEventListener('mouseup', evt => this.handleMouseUp(evt), false);
}
on(evt, cb) {
this.evtMap[evt].push(cb);
}
off(evt, lcb) {
this.evtMap[evt] = this.evtMap[evt].filter(cb => cb !== lcb);
}
trigger(evt, data) {
this.evtMap[evt].map(handler => handler(data));
}
handleTouchStart(evt) {
this.xDown = evt.touches[0].clientX;
this.yDown = evt.touches[0].clientY;
}
handleMouseDown(evt) {
if (this.listenForMouseEvents==false) return;
this.xDown = evt.clientX;
this.yDown = evt.clientY;
this.isMouseDown = true;
}
handleMouseUp(evt) {
if (this.isMouseDown == false) return;
const deltaX = evt.clientX - this.xDown;
const deltaY = evt.clientY - this.yDown;
const distMoved = Math.abs(Math.abs(deltaX) > Math.abs(deltaY) ? deltaX : deltaY);
const activePct = distMoved / this.element.offsetWidth;
if (activePct > this.options.triggerPercent) {
if (Math.abs(deltaX) > Math.abs(deltaY)) {
deltaX < 0 ? this.trigger('SWIPE_LEFT') : this.trigger('SWIPE_RIGHT');
} else {
deltaY > 0 ? this.trigger('SWIPE_UP') : this.trigger('SWIPE_DOWN');
}
}
}
handleTouchEnd(evt) {
const deltaX = evt.changedTouches[0].clientX - this.xDown;
const deltaY = evt.changedTouches[0].clientY - this.yDown;
const distMoved = Math.abs(Math.abs(deltaX) > Math.abs(deltaY) ? deltaX : deltaY);
const activePct = distMoved / this.element.offsetWidth;
if (activePct > this.options.triggerPercent) {
if (Math.abs(deltaX) > Math.abs(deltaY)) {
deltaX < 0 ? this.trigger('SWIPE_LEFT') : this.trigger('SWIPE_RIGHT');
} else {
deltaY > 0 ? this.trigger('SWIPE_UP') : this.trigger('SWIPE_DOWN');
}
}
}
}
// add a listener on load
window.addEventListener("load", function(event) {
const dispatcher = new SwipeEventDispatcher(document.body);
dispatcher.on('SWIPE_RIGHT', () => { console.log('I swiped right!') })
dispatcher.on('SWIPE_LEFT', () => { console.log('I swiped left!') })
});
Приклад використання із зміщенням.
// at least 100 px are a swipe
// you can use the value relative to screen size: window.innerWidth * .1
const offset = 100;
let xDown, yDown
window.addEventListener('touchstart', e => {
const firstTouch = getTouch(e);
xDown = firstTouch.clientX;
yDown = firstTouch.clientY;
});
window.addEventListener('touchend', e => {
if (!xDown || !yDown) {
return;
}
const {
clientX: xUp,
clientY: yUp
} = getTouch(e);
const xDiff = xDown - xUp;
const yDiff = yDown - yUp;
const xDiffAbs = Math.abs(xDown - xUp);
const yDiffAbs = Math.abs(yDown - yUp);
// at least <offset> are a swipe
if (Math.max(xDiffAbs, yDiffAbs) < offset ) {
return;
}
if (xDiffAbs > yDiffAbs) {
if ( xDiff > 0 ) {
console.log('left');
} else {
console.log('right');
}
} else {
if ( yDiff > 0 ) {
console.log('up');
} else {
console.log('down');
}
}
});
function getTouch (e) {
return e.changedTouches[0]
}
Можливо, вам буде легше спочатку реалізувати його з подіями миші до прототипу.
Тут є багато відповідей, включаючи верхню, слід використовувати обережно, оскільки вони не враховують крайових випадків, особливо навколо обмежувальних коробок.
Побачити:
Вам потрібно буде експериментувати, щоб зрозуміти кращі випадки та поведінку, наприклад, вказівник, що рухається поза елементом, перш ніж закінчитися.
Проведення пальцем - це дуже основний жест, який є більш високим рівнем обробки взаємодії вказівників інтерфейсу, приблизно розташованої між обробкою вихідних подій та розпізнаванням рукописного вводу.
Немає єдиного точного методу виявлення повороту або ковзання, хоча практично всі зазвичай дотримуються основного принципу виявлення руху по елементу з порогом відстані, швидкості чи швидкості. Ви можете просто сказати, що якщо в заданому напрямку рухається 65% від розміру екрана в заданому напрямку, то це пальцем. Точно, де ви намалюєте лінію і як ви її обчислите, залежить від вас.
Деякі можуть також дивитись на це з точки зору імпульсу в напрямку та наскільки далеко від екрану він був відсунутий, коли елемент вивільнений. Це зрозуміліше з клейкими поворотами, де елемент можна перетягнути, і тоді при відпусті або відскочить назад, або злетить з екрану, ніби ламка зламалася.
Напевно, ідеально спробувати знайти бібліотеку жестів, яку можна або перенести, або повторно використовувати, що зазвичай використовується для послідовності. Багато прикладів тут є надмірно спрощеними, реєструючи пальцем як найменший дотик у будь-якому напрямку.
Android був би очевидним вибором, хоча має протилежну проблему, він надмірно складний.
Багато людей, здається, неправильно трактували питання як будь-який рух у напрямку. Проведення пальцем - це широкий і відносно короткий рух, що має перевагу в одному напрямку (хоча він може бути дугоподібним і мати певні властивості прискорення). Fling схожий, хоча має намір випадково просувати предмет далеко на відстані під власним імпульсом.
Ці два є достатньо схожими, що деякі бібліотеки можуть надавати лише гойдалки або пальці, які можна використовувати без змін. На плоскому екрані важко по-справжньому розділити два жести, і в цілому люди говорять про обидва (проведіть пальцем по фізичному екрану, але кидаючи на екран елемент UI, відображений на екрані).
Найкращий варіант - не робити цього самостійно. Уже існує велика кількість бібліотек JavaScript для виявлення простих жестів .
Я переробив рішення @givanse , щоб функціонувати як гак Реагу . Вхід - це деякі необов'язкові слухачі подій, вихід - це функціональний перегляд (він повинен бути функціональним, щоб гак міг повторно працювати, коли / якщо зміна поправки).
Також додається у вертикальному / горизонтальному параметрі повороту, щоб невеликі рухи випадково не викликали слухачів події, але їх можна встановити на 0, щоб більш точно імітувати оригінальну відповідь.
Порада: для найкращої роботи функції введення слухача подій слід запам'ятати.
function useSwipeDetector({
// Event listeners.
onLeftSwipe,
onRightSwipe,
onUpSwipe,
onDownSwipe,
// Threshold to detect swipe.
verticalSwipeThreshold = 50,
horizontalSwipeThreshold = 30,
}) {
const [domRef, setDomRef] = useState(null);
const xDown = useRef(null);
const yDown = useRef(null);
useEffect(() => {
if (!domRef) {
return;
}
function handleTouchStart(evt) {
const [firstTouch] = evt.touches;
xDown.current = firstTouch.clientX;
yDown.current = firstTouch.clientY;
};
function handleTouchMove(evt) {
if (!xDown.current || !yDown.current) {
return;
}
const [firstTouch] = evt.touches;
const xUp = firstTouch.clientX;
const yUp = firstTouch.clientY;
const xDiff = xDown.current - xUp;
const yDiff = yDown.current - yUp;
if (Math.abs(xDiff) > Math.abs(yDiff)) {/*most significant*/
if (xDiff > horizontalSwipeThreshold) {
if (onRightSwipe) onRightSwipe();
} else if (xDiff < -horizontalSwipeThreshold) {
if (onLeftSwipe) onLeftSwipe();
}
} else {
if (yDiff > verticalSwipeThreshold) {
if (onUpSwipe) onUpSwipe();
} else if (yDiff < -verticalSwipeThreshold) {
if (onDownSwipe) onDownSwipe();
}
}
};
function handleTouchEnd() {
xDown.current = null;
yDown.current = null;
}
domRef.addEventListener("touchstart", handleTouchStart, false);
domRef.addEventListener("touchmove", handleTouchMove, false);
domRef.addEventListener("touchend", handleTouchEnd, false);
return () => {
domRef.removeEventListener("touchstart", handleTouchStart);
domRef.removeEventListener("touchmove", handleTouchMove);
domRef.removeEventListener("touchend", handleTouchEnd);
};
}, [domRef, onLeftSwipe, onRightSwipe, onUpSwipe, onDownSwipe, verticalSwipeThreshold, horizontalSwipeThreshold]);
return (ref) => setDomRef(ref);
};