javascript - Hvordan skille mellom mus "klikk" og "dra"


Jeg bruker jQuery.click for å håndtere musklikkhendelsen på Raphael-grafen, samtidig trenger jeg å håndtere mus drag-hendelsen, musdrag består av mousedown, mouseup og mousemove i Raphael.

Det er vanskelig å skille click og drag fordi click også inneholder mousedown og mouseup. Hvordan kan jeg da skille mus "click" og mus "drag" i Javascript?

Svar #1

Jeg tror forskjellen er at det er en mousemove mellom mousedown og mouseup i en draing, men ikke i et klikk.

Du kan gjøre noe sånt som dette:

const element = document.createElement('div')
element.textContent = 'test'
document.body.appendChild(element)

let moved = false
const downListener = () => {
  moved = false
}
const moveListener = () => {
  moved = true
}
const upListener = () => {
  if (moved) {
    console.log('moved')
  } else {
    console.log('not moved')
  }
}

// Attach listeners
element.addEventListener('mousedown', downListener)
element.addEventListener('mousemove', moveListener)
element.addEventListener('mouseup', upListener)

// Release memory
element.removeEventListener('mousedown', downListener)
element.removeEventListener('mousemove', moveListener)
element.removeEventListener('mouseup', upListener)

Svar #2

Alle disse løsningene bryter enten ved små musebevegelser, eller er overkompliserte.

Her er en enkel tilpassbar løsning som bruker to hendelseslyttere. Delta er avstanden i piksler som du må bevege deg horisontalt eller vertikalt mellom opp- og nedhendelsene for at koden skal klassifisere det som en draing i stedet for et klikk. Dette er fordi du noen ganger vil bevege musen eller fingeren noen få piksler før du løfter den.

const delta = 6;
let startX;
let startY;

element.addEventListener('mousedown', function (event) {
  startX = event.pageX;
  startY = event.pageY;
});

element.addEventListener('mouseup', function (event) {
  const diffX = Math.abs(event.pageX - startX);
  const diffY = Math.abs(event.pageY - startY);

  if (diffX < delta && diffY < delta) {
    // Click!
  }
});

Svar #3

Renere ES2015

let drag = false;

document.addEventListener('mousedown', () => drag = false);
document.addEventListener('mousemove', () => drag = true);
document.addEventListener('mouseup', () => console.log(drag ? 'drag' : 'click'));

Opplevde ingen feil, som andre kommenterer.

Svar #4

I tilfelle du allerede bruker jQuery:

var $body = $('body');
$body.on('mousedown', function (evt) {
  $body.on('mouseup mousemove', function handler(evt) {
    if (evt.type === 'mouseup') {
      // click
    } else {
      // drag
    }
    $body.off('mouseup mousemove', handler);
  });
});

Svar #5

Dette bør fungere bra. Ligner på det aksepterte svaret (selv om det bruker jQuery), men isDragging-flagget tilbakestilles bare hvis den nye museposisjonen er annerledes enn på mousedown-hendelsen. I motsetning til det aksepterte svaret, fungerer dette på nyere versjoner av Chrome, hvor mousemove utløses uavhengig av om musen ble flyttet eller ikke.

var isDragging = false;
var startingPos = [];
$(".selector")
    .mousedown(function (evt) {
        isDragging = false;
        startingPos = [evt.pageX, evt.pageY];
    })
    .mousemove(function (evt) {
        if (!(evt.pageX === startingPos[0] && evt.pageY === startingPos[1])) {
            isDragging = true;
        }
    })
    .mouseup(function () {
        if (isDragging) {
            console.log("Drag");
        } else {
            console.log("Click");
        }
        isDragging = false;
        startingPos = [];
    });

Du kan også justere koordinatsjekken i mousemove hvis du vil legge til litt toleranse (dvs. behandle små bevegelser som klikk, ikke dra).

Svar #6

Hvis du har lyst til å bruke Rxjs:

var element = document;

Rx.Observable
  .merge(
    Rx.Observable.fromEvent(element, 'mousedown').mapTo(0),
    Rx.Observable.fromEvent(element, 'mousemove').mapTo(1)
  )
  .sample(Rx.Observable.fromEvent(element, 'mouseup'))
  .subscribe(flag => {
      console.clear();
      console.log(flag ? "drag" : "click");
  });
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://unpkg.com/@reactivex/[email /dist/global/Rx.js"></script>

Dette er en direkte klone av det @wong2 gjorde i sitt svar, men konvertert til RxJs.

Også interessant bruk av sample. sample-operatoren vil ta den nyeste verdien fra kilden (samlingen av mousedown og mousemove) og sende den ut når den indre observable (mouseup) sender ut.

Svar #7

Som mrjrdnthms påpeker i sin kommentar til det aksepterte svaret, fungerer ikke dette lenger på Chrome (det utløser alltid mousemove), jeg har tilpasset Gustavos svar (siden jeg bruker jQuery) for å ta hensyn til Chrome-oppførselen.

var currentPos = [];

$(document).on('mousedown', function (evt) {

   currentPos = [evt.pageX, evt.pageY]

  $(document).on('mousemove', function handler(evt) {

    currentPos=[evt.pageX, evt.pageY];
    $(document).off('mousemove', handler);

  });

  $(document).on('mouseup', function handler(evt) {

    if([evt.pageX, evt.pageY].equals(currentPos))
      console.log("Click")
    else
      console.log("Drag")

    $(document).off('mouseup', handler);

  });

});

Funksjonen Array.prototype.equals kommer fra dette svaret

Relaterte artikler