ตรวจจับการกวาดนิ้วผ่าน JavaScript บน iPhone และ Android


268

คุณจะตรวจจับได้อย่างไรว่าผู้ใช้รูดนิ้วไปในทิศทางใดทิศทางหนึ่งผ่านหน้าเว็บด้วย JavaScript

ฉันสงสัยว่ามีวิธีแก้ไขปัญหาเดียวที่สามารถใช้ได้กับเว็บไซต์ทั้ง iPhone และโทรศัพท์ Android หรือไม่


1
สำหรับการรับรู้รูดฉันจะแนะนำHammer.js มันค่อนข้างเล็กและรองรับท่าทางต่าง ๆ : - ปัด - หมุน - หยิก - กด (ค้างไว้นาน) - แตะ - แพน
Will Brickner Will

มีเหตุการณ์: "touchmove"
Clay

@ แสดงว่ายังไม่สามารถใช้งานได้ใน Safari ดังนั้นจึงไม่มี iPhone
Jakuje

คำตอบ:


341

ตัวอย่างรหัสวานิลลาอย่างง่าย:

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 (เดสก์ท็อป) นอกจากนี้ยังเพิ่มเวอร์ชันเมาส์ของเหตุการณ์กวาดเหล่านี้และเพิ่มตัวเลือกความไว
Codebeat

1
ประณาม. หัวข้อถูกปิดดังนั้นไม่สามารถเพิ่มคำตอบของฉันได้!
Codebeat

3
ใช้งานได้ดี แต่ซ้าย / ขวาและขึ้น / ลงกลับด้าน
Peter Eisentraut

4
originalEvent เป็นคุณสมบัติ JQuery มันควรจะเหลือถ้าคุณใช้จาวาสคริปต์บริสุทธิ์โดยไม่ต้อง JQuery รหัสปัจจุบันทำให้เกิดข้อยกเว้นหากทำงานโดยไม่มี JQuery
Jan Derk

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
Ali Ghanavatian

ส่วนรับองค์ประกอบด้วยตัวเองฉันเพิ่งลบ '#' ใน document.getElementById ('องค์ประกอบของฉัน') และมันใช้งานได้ดี ขอบคุณ @Marwelln :)
รถรางสีน้ำเงิน

4
หากคุณต้องการที่จะรอจนกระทั่งรูดจบ (หมายถึงหลังจากที่พวกเขายกนิ้วหรือ onmouseup) เปลี่ยนtouches[0]เป็นchangedTouches[0]และประเภทการจัดการเหตุการณ์handleTouchMoveเพื่อhandleTouchEnd
TetraDev

โทรrun()สองครั้งและคุณจะได้รับการรั่วไหลของหน่วยความจำที่น่ารังเกียจ
Mat

20

ฉันรวมคำตอบบางส่วนที่นี่ลงในสคริปต์ที่ใช้CustomEventเพื่อเริ่มต้นเหตุการณ์ swiped ใน DOM เพิ่มสคริปต์swiped-events.min.js 0.7kลงในหน้าของคุณและฟังเหตุการณ์swiped :

swiped ซ้าย

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

swiped ขวา

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

swiped ขึ้น

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

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 ถ้าคุณทำเครื่องหมายบนgitub repoด้วยข้อมูลเพียงพอที่จะอนุญาตให้ฉันทำซ้ำปัญหาฉันจะดูมัน
John Doherty

1
ขอบคุณมันทำงานได้อย่างสมบูรณ์แบบ! ฉันแทนที่ Hammer.js กับห้องสมุดของคุณเพราะก่อนหน้านี้ไม่สามารถใช้งานได้กับเบราว์เซอร์ซูมและนั่นเป็นปัญหาการใช้งานที่ร้ายแรง ด้วยห้องสมุดนี้การซูมจะทำงานอย่างถูกต้อง (ทดสอบบน Android)
collimarco

15

สิ่งที่ฉันใช้ก่อนหน้านี้คือคุณต้องตรวจจับเหตุการณ์ mousedown บันทึกตำแหน่ง x, y (แล้วแต่จำนวนใดที่เกี่ยวข้อง) จากนั้นตรวจพบเหตุการณ์การวางเมาส์และลบค่าทั้งสอง


28
ฉันเชื่อว่ามันเป็น touchstart, touchmove, touchcancel และ touchend ที่ใคร ๆ ก็ใช้ได้ด้วยไม่ใช่ mousedown หรือ mouseup
Volomike


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 Browser
  • iOS : Safari, Chrome, UC Browser

originalEvent เป็นคุณสมบัติ JQuery มันไม่ได้มีอยู่ในจาวาสคริปต์ที่แท้จริง
Jan Derk

1
ตามคำตอบดังนั้นนี้event.originalEventเหตุการณ์สัมผัสถ้าได้รับการสนับสนุนโดยเบราเซอร์จะได้สัมผัสผ่าน สิ่งที่เป็นได้หยุดที่จะมีอยู่ในขณะนี้และผลในการevent.touches undefined
nashcheez

event.touches หยุดอยู่เฉพาะเมื่อใช้ JQuery ลองใช้โค้ดของคุณโดยไม่ใช้ JQuery และคุณจะได้รับข้อผิดพลาดที่ evt.originalEvent ไม่ได้ถูกกำหนด JQuery แทนที่เหตุการณ์ด้วยตัวเองทั้งหมดและทำให้เหตุการณ์เบราว์เซอร์ดั้งเดิมเป็นเหตุการณ์ดั้งเดิม เวอร์ชั่นย่อ: รหัสของคุณใช้ได้กับ JQuery เท่านั้น มันทำงานได้โดยไม่ต้อง JQuery ถ้าคุณลบต้นฉบับเหตุการณ์
Jan Derk

1
ใช่ฉันวิจัยเล็กน้อยและตระหนักถึงคุณมีสิทธิที่เกี่ยวกับความพร้อมของ jQuery event.originalEventการเปิดใช้งาน ฉันจะอัปเดตคำตอบของฉัน ขอบคุณ! :)
nashcheez


6

บางคำตอบที่ยอดเยี่ยม (ไม่สามารถแสดงความคิดเห็น ... ) เพื่อจัดการกับ swipes สั้น ๆ

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

ถังขยะ, การหมดเวลากวาด, รูดเพิ่มล็อคเอ็มเอ็มเอ็ม

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;แต่คุณทำให้ฉันไปในทิศทางที่ถูกต้อง! ขอบคุณ!
Philip G

4

ฉันมีปัญหากับตัวจัดการ touchend ที่ยิงอย่างต่อเนื่องในขณะที่ผู้ใช้ลากนิ้วไปมา ฉันไม่รู้ว่าเป็นเพราะสิ่งที่ฉันทำผิดหรือเปล่า แต่ฉันต้องการสิ่งนี้อีกครั้งเพื่อสะสมการเคลื่อนไหวด้วย touchmove และ touchend จริง ๆ แล้วทำการโทรกลับ

ฉันต้องมีอินสแตนซ์เหล่านี้จำนวนมากด้วยดังนั้นฉันจึงเพิ่มวิธีเปิด / ปิดใช้งาน

และจุดเริ่มต้นที่การกวาดนิ้วสั้น ๆ ไม่ทำงาน Touchstart zero คือตัวนับแต่ละครั้ง

คุณสามารถเปลี่ยน 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

@gigawatts ฉันจำไม่ได้ โครงการที่ใช้ที่มี EOL มาถึงแล้วและฉันไม่ได้ต้องการรหัสตั้งแต่ ฉันสงสัยว่าตอนที่ฉันเขียน ES6 แต่นั่นเป็นเวลา 2 ปีที่ผ่านมา
แนวโน้ม 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 mobile control สำหรับเรื่องนี้ดีกว่า เยี่ยมชมได้ที่นี่: https://www.w3schools.com/jquerymobile/jquerymobile_events_touch.asp

ค้อนเวลา! หนึ่งในห้องสมุดที่ดีที่สุดน้ำหนักเบาและรวดเร็วจาวาสคริปต์ เยี่ยมชมได้ที่นี่: https://hammerjs.github.io/


2

หากคุณต้องการรูดเพียงอย่างเดียวคุณก็ควรเลือกใช้เฉพาะส่วนที่คุณต้องการ สิ่งนี้ควรใช้กับอุปกรณ์ระบบสัมผัส

นี่คือ ~ 450 bytes 'หลังจากการบีบอัด gzip, minification, babel เป็นต้น

ฉันเขียนคลาสด้านล่างตามคำตอบอื่น ๆ โดยใช้เปอร์เซ็นต์ย้ายแทนพิกเซลและรูปแบบโปรแกรมเลือกจ่ายงานกิจกรรมเพื่อเชื่อมต่อ / ถอดสิ่งต่างๆ

ใช้มันอย่างนั้น:

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 ในการทำเช่นนั้น

ทำไมต้องทำเช่นนั้น? ตัวอย่างเช่นในขณะที่ปัดผู้ใช้สังเกตเห็นว่าเขาไม่ต้องการรูดในที่สุดเขาสามารถเลื่อนนิ้วของเขาไปที่ตำแหน่งเดิม (แอพพลิเคชั่นโทรศัพท์ "dating" ที่เป็นที่นิยมมากทำเช่นนี้;)) และจากนั้น กิจกรรมถูกยกเลิก

ดังนั้นเพื่อหลีกเลี่ยงเหตุการณ์ "การปัดนิ้วที่ถูกต้อง" เพียงเพราะมีความแตกต่างในแนวนอน 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;
}

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


กำลังใช้รุ่นนี้ ฉันจะป้องกันไม่ให้สิ่งนี้ยิงได้หลายครั้งหากใช้การปัดซ้ำ ฉันใช้สิ่งนี้ด้วยคุณสมบัติภาพเคลื่อนไหวสำหรับแบบฟอร์มการหมุนด้านข้างและเมื่อฉันปัดหลาย ๆ ครั้งสิ่งต่าง ๆ จะดูสับสนเล็กน้อยและ div ของฉันเริ่มซ้อนทับกันในพื้นที่ที่มองเห็นได้
NMALM

0

คุณอาจมีเวลาง่ายขึ้นก่อนที่จะใช้มันกับเหตุการณ์เมาส์เพื่อต้นแบบ

มีคำตอบมากมายที่นี่รวมถึงด้านบนควรใช้ด้วยความระมัดระวังเนื่องจากพวกเขาไม่ได้พิจารณากรณีขอบโดยเฉพาะรอบ ๆ กล่องที่มีขอบ

ดู:

คุณจะต้องทำการทดลองเพื่อตรวจจับเคสและพฤติกรรมเช่นตัวชี้ที่เคลื่อนไหวนอกองค์ประกอบก่อนสิ้นสุด

การกวาดนิ้วเป็นท่าทางขั้นพื้นฐานซึ่งเป็นระดับที่สูงขึ้นของการโต้ตอบกับตัวชี้ส่วนต่อประสานระหว่างการประมวลผลเหตุการณ์ดิบและการรู้จำลายมือ

ไม่มีวิธีการที่แน่นอนเพียงวิธีเดียวในการตรวจจับการปัดหรือการเหวี่ยงแม้ว่าโดยทั่วไปแล้วทั้งหมดจะทำตามหลักการพื้นฐานของการตรวจจับการเคลื่อนไหวในองค์ประกอบที่มีขีด จำกัด ของระยะทางและความเร็วหรือความเร็ว คุณอาจพูดง่ายๆว่าหากมีการเคลื่อนไหวข้าม 65% ของขนาดหน้าจอในทิศทางที่กำหนดภายในเวลาที่กำหนดมันจะเป็นการปัด ตรงที่คุณวาดเส้นและวิธีการคำนวณขึ้นอยู่กับคุณ

บางคนอาจมองจากมุมมองของโมเมนตัมในทิศทางและระยะห่างจากหน้าจอเมื่อผลักองค์ประกอบออกไป นี่คือชัดเจนด้วย swipes เหนียวที่องค์ประกอบสามารถลากและจากนั้นปล่อยจะเด้งกลับหรือบินออกจากหน้าจอราวกับว่ายืดหยุ่นแตก

อาจเป็นวิธีที่ดีในการพยายามค้นหาไลบรารีท่าทางสัมผัสที่คุณสามารถใช้พอร์ตหรือนำมาใช้ซ้ำซึ่งมักใช้เพื่อความมั่นคง ตัวอย่างจำนวนมากที่นี่เรียบง่ายมากเกินไปการลงทะเบียนปัดเป็นการสัมผัสที่น้อยที่สุดในทุกทิศทาง

Androidจะเป็นตัวเลือกที่ชัดเจนแม้ว่าจะมีปัญหาตรงกันข้ามมันซับซ้อนเกินไป

หลายคนดูเหมือนจะตีความคำถามผิด ๆ ว่าเป็นการเคลื่อนไหวในทิศทางใด การปัดนิ้วนั้นเป็นการเคลื่อนไหวที่กว้างและสั้น ๆ อย่างท่วมท้นในทิศทางเดียว การพุ่งนั้นคล้ายกัน แต่ตั้งใจที่จะขับสิ่งของออกไปในระยะที่พอเหมาะภายใต้โมเมนตัมของมัน

ทั้งสองมีความคล้ายคลึงกันพอสมควรที่ห้องสมุดบางแห่งอาจจัดเตรียมแบบปัดหรือปัดซึ่งสามารถใช้แทนกันได้ บนหน้าจอแบนเป็นการยากที่จะแยกท่าทางทั้งสองออกจากกันอย่างแท้จริงและผู้คนกำลังพูดกันว่ากำลังทำทั้งสองอย่างอยู่ (การปัดหน้าจอทางกายภาพ แต่โยนองค์ประกอบ UI ที่แสดงบนหน้าจอ)

ตัวเลือกที่ดีที่สุดของคุณคืออย่าทำเอง มีอยู่เป็นจำนวนมากของห้องสมุด JavaScript สำหรับการตรวจจับท่าทางที่เรียบง่าย


0

ฉันทำใหม่โซลูชันของ@givanseเพื่อใช้เป็น React hook อินพุตเป็นผู้ฟังเหตุการณ์ที่เป็นตัวเลือกเอาต์พุตคือการอ้างอิงการทำงาน (ต้องใช้งานได้ดังนั้นตะขอสามารถเรียกใช้อีกครั้งเมื่อ / หากการอ้างอิงเปลี่ยนไป)

นอกจากนี้ยังมีการเพิ่มใน param threshold การปัดแบบแนวตั้ง / แนวนอนเพื่อให้การเคลื่อนไหวขนาดเล็กไม่ได้ตั้งใจให้ผู้ฟังเหตุการณ์เกิดเหตุการณ์ขึ้นโดยบังเอิญ แต่สิ่งเหล่านี้สามารถตั้งค่าเป็น 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);
};
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.