如何检测用户用JavaScript在网页上向某个方向滑动手指?
我想知道是否有一种解决方案可以同时适用于iPhone和Android手机上的网站。
如何检测用户用JavaScript在网页上向某个方向滑动手指?
我想知道是否有一种解决方案可以同时适用于iPhone和Android手机上的网站。
当前回答
我将这里的一些答案合并到一个脚本中,该脚本使用CustomEvent在DOM中触发滑动事件。添加0.7k的swiped-events.min.js脚本到你的页面,并监听滑动事件:
刷卡
document.addEventListener('swiped', function(e) {
console.log(e.target); // the element that was swiped
console.log(e.detail.dir); // swiped direction
});
swiped-left
document.addEventListener('swiped-left', function(e) {
console.log(e.target); // the element that was swiped
});
swiped-right
document.addEventListener('swiped-right', function(e) {
console.log(e.target); // the element that was swiped
});
swiped-up
document.addEventListener('swiped-up', function(e) {
console.log(e.target); // the element that was swiped
});
swiped-down
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>
要在应用程序范围内设置默认值,请在最顶部的元素上设置配置属性:
<body data-swipe-threshold="100" data-swipe-timeout="250">
<div>Swipe me</div>
<div>or me</div>
</body>
源代码可在Github
其他回答
函数检查水平方向和垂直方向,以确定哪个滑动更长,以防止执行两个指令,因为不可能进行完美的单向滑动。滑动总是在X和Y上有偏差。
let touchstartX = 0;
let touchendX = 0;
let touchstartY = 0;
let touchendY = 0;
function checkDirection() {
let difX = touchstartX - touchendX;
let difY = touchstartY - touchendY;
if (Math.abs(difX) > Math.abs(difY)) {
if (touchendX < touchstartX) {/*left*/}
if (touchendX > touchstartX) {/*right*/}
} else {
if (touchendY < touchstartY) {/*up*/}
if (touchendY > touchstartY) {/*down*/}
}
};
document.addEventListener('touchstart', e => {
e.preventDefault();
touchstartX = e.changedTouches[0].screenX;
touchstartY = e.changedTouches[0].screenY;
});
document.addEventListener('touchend', e => {
e.preventDefault();
touchendX = e.changedTouches[0].screenX;
touchendY = e.changedTouches[0].screenY;
checkDirection();
});
我重新包装了TouchWipe作为一个简短的jquery插件:detectSwipe
根据@givanse的回答,以下是你可以用类来做的:
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();
再加上这个答案。这个增加了对鼠标事件的支持,用于桌面测试:
<!--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!') })
});
我只想检测左右滑动,但只在触摸事件结束时触发动作,所以我稍微修改了@givanse的答案来实现这一点。
为什么要这么做?例如,在滑动时,用户注意到他最终不想滑动,他可以将手指移动到原来的位置(一个非常流行的“约会”手机应用程序可以做到这一点;)),然后“向右滑动”事件被取消。
因此,为了避免因为水平上有3px的差异而导致“向右滑动”事件,我添加了一个阈值,在该阈值下事件将被丢弃:为了有一个“向右滑动”事件,用户必须至少滑动浏览器宽度的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;
}