İPhone ve Android'de JavaScript aracılığıyla parmak kaydırmayı algılama


268

Bir kullanıcının JavaScript içeren bir web sayfası üzerinde parmağını bir yönde kaydırdığını nasıl tespit edebilirsiniz?

Hem iPhone hem de Android telefondaki web siteleri için işe yarayacak tek bir çözüm olup olmadığını merak ediyordum.


1
Kaydırma tanıma için Hammer.js'yi tavsiye ederim . Oldukça küçük ve birçok hareketleri destekliyor: - Kaydır - Döndür -
Sıkış

Bir olay var: "touchmove"
Clay

@ Safari'de hala çalışmıyor, bu yüzden iPhone yok.
Jakuje

Yanıtlar:


341

Basit vanilya JS kodu örneği:

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'de test edildi.


1
Tipler, bu olaylar için destek ne olduğu hakkında bir fikri soğutmak ve basit touchstart, touchmove?
d.raev

1
Oldukça iyi çalışıyor, ancak düz hareketleri tespit etme sorunu var. Bu konuya bunu JQuery (masaüstü) çözümü olarak düzelten başka bir cevap göndereceğim. Ayrıca, bu kaydırma olaylarının fare sürümünü ekler ve bir duyarlılık seçeneği ekler.
Codebeat

1
Lanet olsun. Konu kapalı, bu yüzden cevabımı ekleyemiyorum!
Codebeat

3
Bu harika çalışıyor, ancak sol / sağ ve yukarı / aşağı geriye doğru.
Peter Eisentraut

4
originalEvent bir JQuery özelliğidir. JQuery olmadan saf javascript çalıştırırsanız dışarıda bırakılmalıdır. JQuery olmadan çalıştırılırsa geçerli kod bir istisna oluşturur.
Jan Derk

31

@ Givanse'nin cevabına dayanarak, bunu şu şekilde yapabilirsiniz 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);
    }
}

Bunun gibi kullanmaktan daha fazlasını yapabilirsiniz:

// 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
Bu kod muhtemelen işe yaramayacaktır çünkü .bindundefined'i çağırmaya çalışırken bir istisna alacaksınız çünkü handleTouchMoveaslında hiçbir şey döndürmediniz. Ayrıca this.zaten geçerli bağlama bağlı olduğu için işlev
çağrılırken

3
Sadece kaldırdım .bind(this);ve incelikle çalıştı. thank you @nicholas_r
Ali Ghanavatian

Parçayı kendiniz edinin, sadece document.getElementById ('my-element') içindeki '#' öğesini kaldırıyorum ve iyi çalıştı. @Marwelln :)
Mavi Tramvay

4
Eğer tokatlamak SONA kadar beklemek istiyorsanız, değişime (onlar onmouseup kendi parmağını veya sonrasında anlamında) touches[0]için changedTouches[0]ve olay işleyicisi türü handleTouchMoveiçinhandleTouchEnd
TetraDev

run()iki kez arayın ve kötü bir bellek sızıntısı olsun
Mat

20

Burada cevaplardan birkaçını DOM'da hızlıca kaydırılan olayları tetiklemek için CustomEvent kullanan bir komut dosyasına birleştirdim . Sayfanıza 0.7k swiped-events.min.js komut dosyasını ekleyin ve kaydırılan etkinlikleri dinleyin :

swiped-sol

document.addEventListener('swiped-left', function(e) {
    console.log(e.target); // the element that was swiped
});

swiped sağ

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 aşağı

document.addEventListener('swiped-down', function(e) {
    console.log(e.target); // the element that was swiped
});

Doğrudan bir öğeye de ekleyebilirsiniz:

document.getElementById('myBox').addEventListener('swiped-down', function(e) {
    console.log(e.target); // the element that was swiped
});

İsteğe bağlı yapılandırma

Saydam etkileşiminin sayfanızdaki işlevlerini nasıl değiştireceğini ayarlamak için aşağıdaki özellikleri belirleyebilirsiniz (bunlar isteğe bağlıdır) .

<div data-swipe-threshold="10"
     data-swipe-timeout="1000"
     data-swipe-ignore="false">
        Swiper, get swiping!
</div>

Kaynak kodu Github'da mevcuttur


Buraya geldim çünkü pure-
swipe

@StefanBob sorunu yeniden üretmeme izin vermek için yeterli bilgi ile github repo bir kene yükseltmek , ben içine bakacağım
John Doherty

1
Teşekkürler, mükemmel çalışıyor! Hammer.js'yi kitaplığınızla değiştirdim, çünkü eski tarayıcı yakınlaştırma ile çalışmaz ve bu ciddi bir kullanılabilirlik sorunudur. Bu kütüphane ile yakınlaştırma düzgün çalışıyor (Android'de test edildi)
Collimarco

15

daha önce kullandığım mousedown olayı tespit etmek, x, y konumunu (hangisi alakalı ise) kaydetmek ve daha sonra mouseup olayını tespit etmek ve iki değeri çıkarmak zorundasınız.


28
İnanıyorum ki, dokunmatik ekran, touchmove, touchcancel ve touchend, fareyle ya da fareyle değil, onunla çalışacak.
Volomike


12

Kaydırma işlemlerini kaydetmek için @givanse mükemmel yanıtını birden çok mobil tarayıcıda en güvenilir ve uyumlu olarak buldum.

Ancak, kodunu kullanan modern mobil tarayıcılarda çalışması için gerekli bir değişiklik var jQuery.

event.toucheseğer var olmayacak jQuerykullanılan ve sonuçları edilir undefinedve değiştirilmesi gerektiğini event.originalEvent.touches. Olmadan jQuery, event.touchesiyi çalışmalı.

Böylece çözüm,

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

Test tarihi:

  • Android : Chrome, UC Tarayıcı
  • iOS : Safari, Chrome, UC Tarayıcı

originalEvent bir JQuery özelliğidir. Saf Javascript'te bile mevcut değildir.
Jan Derk

1
Gereğince bu SO cevap , tarayıcı tarafından desteklenen bir dokunma olayı eğer yoluyla maruz kalacak event.originalEvent. Şey artık event.touchesvar olmaktan çıkıyor ve sonuçlanıyor undefined.
nashcheez

event.touches yalnızca JQuery kullanılırken sona erdi. JQuery olmadan kodunuzu deneyin ve evt.originalEvent tanımsız bir hata alırsınız. JQuery, etkinliği tamamen kendisiyle değiştirir ve yerel tarayıcı olayını orijinaline yerleştirir. Kısa sürüm: Kodunuz yalnızca JQuery ile çalışır. Orijinali kaldırırsanız JQuery olmadan çalışır.
Jan Derk

1
Evet, biraz araştırdım ve jquery etkinliğinin kullanılabilirliği konusunda haklı olduğunuzu fark ettim event.originalEvent. Cevabımı güncelleyeceğim. Teşekkürler! :)
nashcheez


6

Kısa tokatlamak için başa çıkmak için bazı uppest cevap modu (yorum yapamıyorum ...)

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, zaman aşımı tokatlamak, tokatlamakBlockElems ekleyin.

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

Herkes Android'de jQuery Mobile kullanmaya çalışıyor ve JQM tokatlamak algılama ile ilgili sorunları varsa

(Xperia Z1, Galaxy S3, Nexus 4 ve bazı Wiko telefonlarda da vardı) bu yararlı olabilir:

 //Fix swipe gesture on android
    if(android){ //Your own device detection here
        $.event.special.swipe.verticalDistanceThreshold = 500
        $.event.special.swipe.horizontalDistanceThreshold = 10
    }

Android üzerinde hızlıca kaydırma, çok uzun, kesin ve hızlı bir kaydırma olmadığı sürece algılanmadı.

Bu iki çizgi ile düzgün çalışıyor


Ayrıca eklemem gerekiyordu: $.event.special.swipe.scrollSupressionThreshold = 8;ama sen beni doğru yöne koydun! Teşekkür ederim!
Philip G

4

Kullanıcı parmağını sürüklerken touchend işleyicisi sürekli ateş ile sorun vardı. Bunun yanlış yaptığım bir şeyden kaynaklanıp kaynaklanmadığını bilmiyorum ama touchmove ile hareketleri biriktirmek için yeniden bağladım ve touchend aslında geri aramayı tetikliyor.

Ayrıca bu örneklerin çok sayıda olması gerekiyordu ve bu yüzden etkinleştirme / devre dışı bırakma yöntemleri ekledim.

Ve kısa bir tokatlamanın ateşlemediği bir eşik. Touchstart zero her seferinde sayaçlardır.

Target_node komutunu anında değiştirebilirsiniz. Oluşturmada etkinleştir isteğe bağlıdır.

/** 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);
            }
        }
    }
}

Bu ES5 veya ES6 için mi?
1.21 gigawatts

@gigawatts Hatırlamıyorum. Kullanılan proje zaten EOL'ye ulaştı ve o zamandan beri koda ihtiyacım olmadı. ES6 için yazarken şüpheliyim ama bu 2 yıldan uzun bir süre önceydi.
Trendal

3

Cevapların birçoğunu da birleştirdim, çoğunlukla birinci ve ikincisi sınıflarla birleştim ve işte benim versiyonum:

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

Daha sonra aşağıdaki gibi kullanabilirsiniz:

let swiper = new Swipe({
                    onLeft() {
                        console.log('You swiped left.');
                    }
});

Sadece "onLeft" yöntemini çağırmak istediğinizde konsol hatalarını önlemeye yardımcı olur.



2

Sadece tokatlamak gerekiyorsa, sadece ihtiyacınız olan kısmı kullanarak boyut akıllıca daha iyi. Bu, herhangi bir dokunmatik cihazda çalışmalıdır.

Bu gzip sıkıştırma, küçültme, babil vb. Sonra ~ 450 bayttır.

Aşağıdaki sınıfı diğer cevaplara göre yazdım, piksel yerine taşınan yüzdeyi ve bir şeyleri kancalamak / kancalarını kaldırmak için bir olay dağıtıcı desen kullanır.

Şöyle kullanın:

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

Yalnızca sola ve sağa hızlıca kaydırmayı algılamak istedim, ancak yalnızca dokunma olayı sona erdiğinde eylemi tetikledim , bu yüzden @ givanse'ın bunu yapmak için harika cevabını biraz değiştirdim.

Bunu neden yapmalı? Örneğin, kaydırırken, kullanıcı nihayet kaydırmak istemediğini fark ederse , parmağını orijinal konumuna (çok popüler bir "flört" telefon uygulaması bunu yapar;)) ve sonra "sağa kaydır" olay iptal edildi.

Bu nedenle, yatay olarak 3 piksellik bir fark olduğu için "sağa hızlıca kaydır" olayından kaçınmak için, altına bir olayın atıldığı bir eşik ekledim: "sağa hızlıca kaydır" etkinliğine sahip olmak için, kullanıcının en azından hızlıca kaydırması gerekir Tarayıcı genişliğinin 1 / 3'ü (elbette bunu değiştirebilirsiniz).

Tüm bu küçük ayrıntılar kullanıcı deneyimini geliştirir. İşte (Vanilla JS) kodu:

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

Yatay kaydırma için basit vanilya JS örneği:

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()
})

Dikey kaydırma için aynı mantığı kullanabilirsiniz.


1

Bu yanıta burada ekliyoruz . Bu, masaüstünde test etmek için fare olayları için destek ekler:

<!--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

Ofset ile nasıl kullanılacağına dair bir örnek.

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


Şu anda bu sürüm kullanılıyor. Tekrarla kaydırılırsa bunun birden çok kez tetiklenmesini nasıl önlerim? Yan kaydırma formunun animasyon özelliği ile bunu kullanıyorum ve birden fazla kez kaydırdığımda işler biraz berbat oluyor ve div'lerim görünür alanda çakışmaya başlıyor.
NMALM

0

Prototip oluşturmak için önce fare olaylarıyla uygulamak daha kolay bir zamanınız olabilir.

Burada üst kısım dahil olmak üzere birçok cevap vardır, özellikle sınırlayıcı kutular etrafında kenar durumlarını dikkate almadığından dikkatli kullanılmalıdır.

Görmek:

Bitmeden önce öğenin dışına hareket eden işaretçi gibi kenar durumları ve davranışları yakalamak için denemeler yapmanız gerekir.

Kaydırma, ham olayları işleme ile el yazısı tanıma arasında kabaca oturan daha yüksek bir arabirim işaretçisi etkileşim işlemidir.

Bir tokatlamak veya fırlatmayı tespit etmek için tek bir kesin yöntem yoktur, ancak neredeyse hepsi genellikle bir öğe boyunca mesafe ve hız veya hız eşiği ile bir hareketi tespit etmek için temel bir prensibi takip eder. Belirli bir süre içinde belirli bir yönde ekran boyutunun% 65'i boyunca bir hareket varsa, o zaman bir kaydırma olduğunu söyleyebilirsiniz. Çizgiyi nerede çizdiğiniz ve nasıl hesapladığınız tam size bağlıdır.

Bazıları buna bir yöndeki momentum perspektifinden ve eleman bırakıldığında ekrandan ne kadar uzağa itildiğine de bakabilir. Bu, elemanın sürüklenebileceği yapışkan tomurcuklarla daha açıktır ve daha sonra serbest bırakıldığında elastik kırılmış gibi ekrandan geri döner veya ekrandan uçar.

Muhtemelen tutarlılık için yaygın olarak kullanılan port veya yeniden kullanabileceğiniz bir hareket kütüphanesi bulmaya çalışmak idealdir. Buradaki örneklerin çoğu aşırı derecede basittir ve bir tokatlamayı herhangi bir yöne en ufak bir dokunuş olarak kaydeder.

Android , tam tersi bir seçim olsa da, tam tersi bir sorun var, aşırı karmaşık.

Birçok insan soruyu bir yönde herhangi bir hareket olarak yanlış yorumlamış gibi görünüyor. Kaydırma, ezici bir şekilde tek bir yönde geniş ve nispeten kısa bir harekettir (gerçi olabilir ve belirli hızlanma özelliklerine sahip olabilir). Bir atış benzerdir, ancak bir öğeyi kendi momentumu altında adil bir mesafeden rasgele itmek niyetindedir.

İkisi, bazı kütüphanelerin yalnızca birbirinin yerine kullanılabilecek bir fırlatma veya kaydırma sağlaması için yeterince benzerdir. Düz bir ekranda iki hareketi gerçekten ayırmak zordur ve genellikle konuşan insanlar her ikisini de yapar (fiziksel ekranı kaydırmak, ancak ekranda görüntülenen UI öğesini fırlatmak).

En iyi seçenek kendiniz yapmamaktır. Basit hareketleri algılamak için çok sayıda JavaScript kitaplığı zaten var .


0

Tepki kancası işlevi görmek için @givanse'nin çözümünü elden geçirdim . Giriş isteğe bağlı bazı olay dinleyicileri, çıktı işlevsel bir ref (işlevsel olması gerekir, böylece ref değiştiğinde / değiştiğinde kanca yeniden çalışabilir).

Dikey / yatay kaydırma eşiği parametresinde de eklenir, böylece küçük hareketler olay dinleyicilerini yanlışlıkla tetiklemez, ancak orijinal yanıtı daha yakından taklit etmek için 0'a ayarlanabilir.

İpucu: en iyi performans için olay dinleyicisi giriş işlevleri not edilmelidir.

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);
};
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.