Làm thế nào bạn có thể phát hiện ra rằng một người dùng đã vuốt ngón tay của mình theo một hướng nào đó trên một trang web bằng JavaScript?
Tôi đã tự hỏi nếu có một giải pháp sẽ hoạt động cho các trang web trên cả iPhone và điện thoại Android.
Làm thế nào bạn có thể phát hiện ra rằng một người dùng đã vuốt ngón tay của mình theo một hướng nào đó trên một trang web bằng JavaScript?
Tôi đã tự hỏi nếu có một giải pháp sẽ hoạt động cho các trang web trên cả iPhone và điện thoại Android.
Câu trả lời:
Mẫu mã vanilla đơn giản:
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;
};
Đã thử nghiệm trên Android.
touchstart
, touchmove
?
Dựa trên câu trả lời của @ givanse, đây là cách bạn có thể làm với 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);
}
}
Bạn có thể sử dụng nó như thế này:
// 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
không xác định vì handleTouchMove
thực tế bạn không trả lại bất cứ điều gì. Ngoài ra, việc gọi liên kết khi gọi hàm là vô ích this.
vì nó đã bị ràng buộc với bối cảnh hiện tại
.bind(this);
và nó hoạt động duyên dáng. cảm ơn bạn @nicholas_r
touches[0]
thành changedTouches[0]
và loại xử lý sự kiện handleTouchMove
thànhhandleTouchEnd
run()
hai lần và bạn bị rò rỉ bộ nhớ khó chịu
Tôi đã hợp nhất một vài câu trả lời ở đây thành một tập lệnh sử dụng CustomEvent để kích hoạt các sự kiện đã vuốt trong DOM. Thêm tập lệnh 0,7k swiped-event.min.js vào trang của bạn và lắng nghe các sự kiện đã vuốt :
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
});
Bạn cũng có thể đính kèm trực tiếp vào một yếu tố:
document.getElementById('myBox').addEventListener('swiped-down', function(e) {
console.log(e.target); // the element that was swiped
});
Bạn có thể chỉ định các thuộc tính sau để điều chỉnh cách các chức năng tương tác vuốt trong trang của bạn (đây là các tùy chọn) .
<div data-swipe-threshold="10"
data-swipe-timeout="1000"
data-swipe-ignore="false">
Swiper, get swiping!
</div>
Mã nguồn có sẵn trên Github
những gì tôi đã sử dụng trước đây là bạn phải phát hiện sự kiện mousedown, ghi lại vị trí x, y của nó (tùy theo điều kiện nào) sau đó phát hiện sự kiện mouseup và trừ hai giá trị.
jQuery Mobile cũng bao gồm hỗ trợ vuốt: http://api.jquerymobile.com/swipe/
Thí dụ
$("#divId").on("swipe", function(event) {
alert("It's a swipe!");
});
Tôi đã tìm thấy @givanse câu trả lời tuyệt vời là đáng tin cậy và tương thích nhất trên nhiều trình duyệt di động để đăng ký hành động vuốt.
Tuy nhiên, có một sự thay đổi trong mã của anh ta để làm cho nó hoạt động trong các trình duyệt di động hiện đại đang sử dụng jQuery
.
event.touches
sẽ không tồn tại nếu jQuery
được sử dụng và kết quả undefined
và nên được thay thế bằng event.originalEvent.touches
. Nếu không jQuery
, event.touches
nên làm việc tốt.
Vì vậy, giải pháp trở thành,
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;
};
Đã thử nghiệm trên:
event.originalEvent
. Điều này event.touches
đã không còn tồn tại và kết quả là undefined
.
event.originalEvent
. Tôi sẽ cập nhật câu trả lời của tôi. Cảm ơn! :)
Tôi đã đóng gói lại TouchWipe
như một plugin jquery ngắn:detectSwipe
Một số mod của câu trả lời tối đa (không thể bình luận ...) để đối phó với các thao tác vuốt ngắn
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;
}
};
thùng rác, vuốt hết thời gian, vuốtBlockElems thêm.
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;
}
Nếu bất cứ ai đang cố gắng sử dụng jQuery Mobile trên Android và gặp vấn đề với phát hiện vuốt JQM
(Tôi đã có một số trên Xperia Z1, Galaxy S3, Nexus 4 và một số điện thoại Wiko nữa) điều này có thể hữu ích:
//Fix swipe gesture on android
if(android){ //Your own device detection here
$.event.special.swipe.verticalDistanceThreshold = 500
$.event.special.swipe.horizontalDistanceThreshold = 10
}
Vuốt trên Android không được phát hiện trừ khi đó là thao tác vuốt rất dài, chính xác và nhanh.
Với hai dòng này, nó hoạt động chính xác
$.event.special.swipe.scrollSupressionThreshold = 8;
nhưng bạn đặt tôi đi đúng hướng! Cảm ơn bạn!
Tôi gặp sự cố với trình xử lý touchend bắn liên tục trong khi người dùng đang kéo một ngón tay xung quanh. Tôi không biết liệu đó có phải là do tôi làm sai hay không nhưng tôi đã viết lại phần này để tích lũy các bước di chuyển bằng touchmove và touchend thực sự kích hoạt cuộc gọi lại.
Tôi cũng cần phải có một số lượng lớn các trường hợp này và vì vậy tôi đã thêm các phương thức enable / vô hiệu hóa.
Và một ngưỡng mà một cú vuốt ngắn không bắn. Touchstart zero's quầy mỗi lần.
Bạn có thể thay đổi target_node khi đang bay. Cho phép tạo là tùy chọn.
/** 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);
}
}
}
}
Tôi cũng đã hợp nhất một vài câu trả lời, chủ yếu là câu đầu tiên và câu thứ hai với các lớp và đây là phiên bản của tôi:
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;
}
}
Sau đó có thể sử dụng nó như sau:
let swiper = new Swipe({
onLeft() {
console.log('You swiped left.');
}
});
Nó giúp tránh các lỗi bảng điều khiển khi bạn chỉ muốn gọi phương thức "onLeft".
Sử dụng hai:
jQuery mobile: hoạt động trong hầu hết các trường hợp và đặc biệt khi bạn đang phát triển applicaion sử dụng plugin jQuery khác thì tốt hơn nên sử dụng các điều khiển di động jQuery cho việc này. Truy cập tại đây: https://www.w3schools.com/jquerymobile/jquerymobile_events_touch.asp
Giờ búa! một trong những thư viện dựa trên javascript tốt nhất, nhẹ và nhanh. Truy cập tại đây: https://hammerjs.github.io/
Nếu bạn chỉ cần vuốt, tốt hơn hết là bạn chỉ nên sử dụng phần bạn cần. Điều này sẽ làm việc trên bất kỳ thiết bị cảm ứng.
Đây là ~ 450 byte 'sau khi nén gzip, thu nhỏ, babel, v.v.
Tôi đã viết lớp dưới đây dựa trên các câu trả lời khác, nó sử dụng tỷ lệ phần trăm được di chuyển thay vì pixel và một mẫu trình điều phối sự kiện để móc / không lấy các thứ.
Sử dụng nó như vậy:
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;
Tôi chỉ muốn phát hiện vuốt trái và phải, nhưng chỉ kích hoạt hành động khi sự kiện chạm kết thúc , vì vậy tôi đã sửa đổi một chút câu trả lời tuyệt vời của @ givanse để làm điều đó.
Tại sao phải làm điều đó? Ví dụ, trong khi vuốt, người dùng thông báo cuối cùng anh ta không muốn vuốt, anh ta có thể di chuyển ngón tay của mình ở vị trí ban đầu (một ứng dụng điện thoại "hẹn hò" rất phổ biến thực hiện điều này;)), và sau đó "vuốt sang phải" sự kiện bị hủy bỏ.
Vì vậy, để tránh sự kiện "vuốt phải" chỉ vì có chênh lệch 3px theo chiều ngang, tôi đã thêm một ngưỡng theo đó một sự kiện bị loại bỏ: để có sự kiện "vuốt phải", người dùng phải vuốt ít nhất 1/3 chiều rộng trình duyệt (tất nhiên bạn có thể sửa đổi điều này).
Tất cả những chi tiết nhỏ này nâng cao trải nghiệm người dùng. Đây là mã (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;
}
Ví dụ JS vanilla đơn giản để vuốt ngang:
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()
})
Bạn có thể sử dụng logic khá giống nhau để vuốt dọc.
Thêm vào câu trả lời này ở đây . Điều này thêm hỗ trợ cho các sự kiện chuột để thử nghiệm trên máy tính để bàn:
<!--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!') })
});
Một ví dụ về cách sử dụng với offset.
// 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]
}
Bạn có thể có một thời gian dễ dàng hơn trước tiên thực hiện nó với các sự kiện chuột đến nguyên mẫu.
Có rất nhiều câu trả lời ở đây, bao gồm cả đầu, nên được sử dụng một cách thận trọng vì chúng không xem xét các trường hợp cạnh đặc biệt là xung quanh các hộp giới hạn.
Xem:
Bạn sẽ cần thử nghiệm để bắt các trường hợp và hành vi cạnh như con trỏ di chuyển bên ngoài phần tử trước khi kết thúc.
Vuốt là một cử chỉ rất cơ bản, đó là mức độ tương tác con trỏ giao diện cao hơn, xử lý gần như ngồi giữa xử lý các sự kiện thô và nhận dạng chữ viết tay.
Không có phương pháp chính xác duy nhất để phát hiện thao tác vuốt hoặc ném dù hầu như tất cả đều tuân theo nguyên tắc cơ bản là phát hiện chuyển động trên một phần tử có ngưỡng khoảng cách và tốc độ hoặc vận tốc. Bạn có thể chỉ cần nói rằng nếu có một chuyển động trên 65% kích thước màn hình theo một hướng nhất định trong một thời gian nhất định thì đó là một lần vuốt. Chính xác nơi bạn vẽ đường thẳng và cách bạn tính toán nó là tùy thuộc vào bạn.
Một số người cũng có thể nhìn nó từ góc độ của động lượng theo hướng và nó được đẩy ra khỏi màn hình bao xa khi phần tử được giải phóng. Điều này rõ ràng hơn với các lần vuốt dính mà phần tử có thể được kéo và sau đó khi phát hành sẽ bật trở lại hoặc bay khỏi màn hình như thể dây thun bị đứt.
Có lẽ lý tưởng để cố gắng tìm một thư viện cử chỉ mà bạn có thể chuyển hoặc sử dụng lại thường được sử dụng để thống nhất. Nhiều ví dụ ở đây là quá đơn giản, đăng ký một lần vuốt như một cú chạm nhẹ nhất theo bất kỳ hướng nào.
Android sẽ là sự lựa chọn rõ ràng mặc dù có vấn đề ngược lại, nó quá phức tạp.
Nhiều người dường như đã giải thích sai câu hỏi là bất kỳ chuyển động theo một hướng. Vuốt là một chuyển động rộng và tương đối ngắn áp đảo theo một hướng duy nhất (mặc dù có thể được trang bị và có các thuộc tính gia tốc nhất định). Một fling là tương tự mặc dù có ý định tình cờ đẩy một vật đi một khoảng cách hợp lý theo đà của chính nó.
Cả hai đều tương tự nhau đến mức một số thư viện chỉ có thể cung cấp fling hoặc vuốt, có thể được sử dụng thay thế cho nhau. Trên màn hình phẳng, thật khó để phân tách hai cử chỉ và nói chung mọi người đang thực hiện cả hai (vuốt màn hình vật lý nhưng vung yếu tố UI hiển thị trên màn hình).
Bạn lựa chọn tốt nhất là không tự làm. Hiện đã có một số lượng lớn các thư viện JavaScript để phát hiện các cử chỉ đơn giản .
Tôi đã làm lại giải pháp của @givanse để hoạt động như một cái móc React. Đầu vào là một số trình lắng nghe sự kiện tùy chọn, đầu ra là một ref chức năng (cần phải có chức năng để hook có thể chạy lại khi / nếu ref thay đổi).
Cũng được thêm vào trong tham số ngưỡng vuốt dọc / ngang, để các chuyển động nhỏ không vô tình kích hoạt người nghe sự kiện, nhưng chúng có thể được đặt thành 0 để bắt chước câu trả lời ban đầu chặt chẽ hơn.
Mẹo: để có hiệu suất tốt nhất, các chức năng nhập của trình lắng nghe sự kiện cần được ghi nhớ.
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);
};