Визначте пальцем пальцем через JavaScript на iPhone та Android


268

Як можна виявити, що користувач провев пальцем у певному напрямку по веб-сторінці з JavaScript?

Мені було цікаво, чи є одне рішення, яке буде працювати для веб-сайтів як на iPhone, так і на телефоні Android.


1
Для розпізнавання пальця я рекомендую Hammer.js . Він зовсім невеликий, і він підтримує безліч жестів: - Проведіть пальцем - Поворот - Щіпком - Натисніть (тривало утримуйте) - Натисніть - Пан
Вілл Брікнер

Є подія: "touchmove"
Глина

@ Заграйте, що все ще не працює в Safari, тому немає iPhone.
Якаву

Відповіді:


341

Простий зразок коду ванілі 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.


1
Виглядає круто і просто, будь-яку ідею , що є підтримка цих подій touchstart, touchmove?
d.raev

1
Він працює досить добре, але має проблему з виявленням прямих рухів. Я опублікую ще одну відповідь на цю тему, яка зафіксувала це як рішення JQuery (desktop). Він також додає мишу версію цих пальців подій та додає параметр чутливості.
Codebeat

1
Блін. Тема закрита, тому я не можу додати свою відповідь!
Codebeat

3
Це чудово, але ліворуч / праворуч та вгору / вниз назад.
Пітер Ейзентрав

4
originalEvent - властивість JQuery. Це слід залишити, якщо ви запускаєте чистий JavaScript без JQuery. Поточний код створює виняток, якщо він працює без JQuery.
Ян Дерк

31

Виходячи з відповіді @ 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();

7
цей код, ймовірно, не буде працювати, тому що ви отримаєте виняток під час спроби зателефонувати .bindне визначено, оскільки ваш handleTouchMoveфактично нічого не повернув. Крім того, марно зв'язувати дзвінки при виклику функції, this.оскільки це вже пов'язано з поточним контекстом
nick.skriabin

3
Я щойно зняв, .bind(this);і це витончено спрацювало. дякую @nicholas_r
Алі Ганаватіян

Частина отримайте сам елемент, я просто видаляю "#" у document.getElementById ("мій елемент"), і він добре працював. Дякую @Marwelln :)
Блакитний трамвай

4
Якщо ви хочете зачекати, поки проведіть ENDS (маючи на увазі після того, як вони піднімуть палець або touches[0]changedTouches[0]handleTouchMovehandleTouchEnd
натисніть кнопку миші

зателефонуйте run()двічі, і ви отримаєте неприємний витік пам’яті
Мат.

20

Я об'єднав декілька відповідей тут у сценарій, який використовує 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


Я приїхав сюди, тому що чистий
потік

@StefanBob якщо ти поставиш галочку на репортажі github з достатньою інформацією, щоб я міг відтворити проблему, я буду розглядати це
Джон Догерті

1
Дякую, це прекрасно працює! Я замінив Hammer.js вашою бібліотекою, тому що перший не працює із збільшенням браузера, і це серйозна проблема використання. З цією бібліотекою зум працює належним чином (тестується на Android)
collimarco

15

Що я раніше використовував - ви повинні виявити подію mousedown, записати її x, y місце розташування (залежно від того, що стосується), а потім виявити подію миші та відняти два значення.


28
Я вважаю, що це сенсорний запуск, сенсорний рух, сенсорний та дотик, з яким можна було б працювати, а не мишатись чи мишати.
Воломіке


12

Я знайшов блискучу відповідь на @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;                                             
};

Тестовано на:

  • Android : Chrome, браузер UC
  • iOS : Safari, Chrome, браузер UC

originalEvent - властивість JQuery. Він навіть не існує в чистому Javascript.
Ян Дерк

1
Відповідно до цієї відповіді ТА , сенсорний випадок, якщо він підтримується браузером, буде виставлений на екран event.originalEvent. Справа event.touchesперестала існувати зараз і призводить до undefined.
нашчез

event.touches припинив своє існування лише під час використання JQuery. Спробуйте свій код без JQuery, і ви отримаєте помилку про те, що evt.originalEvent не визначено. JQuery повністю замінює подію власною і ставить вихідну подію браузера в оригінальний захід. Коротка версія: Ваш код працює лише з JQuery. Він працює без JQuery, якщо ви видалите оригіналвент.
Ян Дерк

1
Так, я трохи дослідив і зрозумів, що ти маєш рацію щодо наявності увімкнення jquery event.originalEvent. Я оновлю свою відповідь. Дякую! :)
нашчез


6

Деякі найвищі відповіді (не можу коментувати ...), щоб вирішити короткі перегони

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;
    }
};

6

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;
}

4

Якщо хтось намагається використовувати 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;але ви направляєте мене в правильному напрямку! Дякую!
Філіп G

4

У мене були проблеми з доторканнямся до оброблювача дотику, коли користувач перетягував палець. Я не знаю, чи це через щось, що я роблю неправильно, чи ні, але я перемотав це, щоб накопичити рухи за допомогою 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);
            }
        }
    }
}

Це для ES5 чи ES6?
1,21 гігаватт

@gigawatts я не пам'ятаю. Проект, який використовувався, вже дійшов до EOL, і з того часу мені не потрібен код. Я підозрюю, що я писав для ES6, але це було понад 2 роки тому.
Trendal Toews

3

Я також з’єднав декілька відповідей, в основному першу та другу з класами, і ось моя версія:

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".


2

Використовували два:

jQuery mobile: працюйте в більшості випадків, і особливо, коли ви розробляєте додаток, який використовує інший плагін jQuery, тоді краще використовувати для цього мобільні елементи управління jQuery. Відвідайте його тут: https://www.w3schools.com/jquerymobile/jquerymobile_events_touch.asp

Час молотка! одна з найкращих, легких і швидких бібліотек на основі JavaScript. Відвідайте його тут: https://hammerjs.github.io/


2

Якщо вам просто потрібно провести пальцем, вам краще використовувати розмір, використовуючи лише потрібну частину. Це має працювати на будь-якому сенсорному пристрої.

Це ~ 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;


2

Я хотів виявити лише пальцем ліворуч і праворуч, але запустити дію лише тоді, коли сенсорне подія закінчиться , тому я трохи змінив чудову відповідь @ 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;
}

1

Простий приклад 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()
})

Ви можете використовувати таку ж логіку для вертикального переведення.


1

Додайте сюди цю відповідь . Цей додає підтримку подій миші для тестування на робочому столі:

<!--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!') })
});

0

Приклад використання із зміщенням.

// 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]
}


В даний час використовується ця версія. Як я можу запобігти цьому кілька разів стріляти, якщо проведіть повторно? Я використовую це за допомогою функції анімації для форми бічного прокручування, і коли я проведу кілька разів, речі стають трохи гнучкими, і мої діви починають перекриватися у видимій області.
NMALM

0

Можливо, вам буде легше спочатку реалізувати його з подіями миші до прототипу.

Тут є багато відповідей, включаючи верхню, слід використовувати обережно, оскільки вони не враховують крайових випадків, особливо навколо обмежувальних коробок.

Побачити:

Вам потрібно буде експериментувати, щоб зрозуміти кращі випадки та поведінку, наприклад, вказівник, що рухається поза елементом, перш ніж закінчитися.

Проведення пальцем - це дуже основний жест, який є більш високим рівнем обробки взаємодії вказівників інтерфейсу, приблизно розташованої між обробкою вихідних подій та розпізнаванням рукописного вводу.

Немає єдиного точного методу виявлення повороту або ковзання, хоча практично всі зазвичай дотримуються основного принципу виявлення руху по елементу з порогом відстані, швидкості чи швидкості. Ви можете просто сказати, що якщо в заданому напрямку рухається 65% від розміру екрана в заданому напрямку, то це пальцем. Точно, де ви намалюєте лінію і як ви її обчислите, залежить від вас.

Деякі можуть також дивитись на це з точки зору імпульсу в напрямку та наскільки далеко від екрану він був відсунутий, коли елемент вивільнений. Це зрозуміліше з клейкими поворотами, де елемент можна перетягнути, і тоді при відпусті або відскочить назад, або злетить з екрану, ніби ламка зламалася.

Напевно, ідеально спробувати знайти бібліотеку жестів, яку можна або перенести, або повторно використовувати, що зазвичай використовується для послідовності. Багато прикладів тут є надмірно спрощеними, реєструючи пальцем як найменший дотик у будь-якому напрямку.

Android був би очевидним вибором, хоча має протилежну проблему, він надмірно складний.

Багато людей, здається, неправильно трактували питання як будь-який рух у напрямку. Проведення пальцем - це широкий і відносно короткий рух, що має перевагу в одному напрямку (хоча він може бути дугоподібним і мати певні властивості прискорення). Fling схожий, хоча має намір випадково просувати предмет далеко на відстані під власним імпульсом.

Ці два є достатньо схожими, що деякі бібліотеки можуть надавати лише гойдалки або пальці, які можна використовувати без змін. На плоскому екрані важко по-справжньому розділити два жести, і в цілому люди говорять про обидва (проведіть пальцем по фізичному екрану, але кидаючи на екран елемент UI, відображений на екрані).

Найкращий варіант - не робити цього самостійно. Уже існує велика кількість бібліотек JavaScript для виявлення простих жестів .


0

Я переробив рішення @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);
};
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.