Drag’n’Drop con eventi del mouse

Drag’n’Drop è un’ottima soluzione di interfaccia. Prendere qualcosa e trascinarlo e rilasciarlo è un modo chiaro e semplice per fare molte cose, dalla copia e lo spostamento di documenti (come nei file manager) all’ordinazione (rilascio di articoli in un carrello).

Nel moderno HTML standard è presente una sezione sul trascinamento della selezione con eventi speciali come dragstart, dragend e così via.

Questi eventi ci consentono di supportare tipi speciali di drag’n’drop, come la gestione del trascinamento di un file dal file manager del sistema operativo e il rilascio nella finestra del browser. Quindi JavaScript può accedere al contenuto di tali file.

Ma anche gli eventi di trascinamento nativi hanno delle limitazioni. Ad esempio, non possiamo impedire il trascinamento da una determinata area. Inoltre non possiamo fare solo il trascinamento “orizzontale” o “verticale”. E ci sono molte altre attività di trascinamento che non è possibile eseguire utilizzandole. Inoltre, il supporto dei dispositivi mobili per tali eventi è molto debole.

Quindi qui vedremo come implementare Drag’n’Drop utilizzando gli eventi del mouse.

Algoritmo Drag’n’Drop

L’algoritmo Drag’n’Drop di base ha il seguente aspetto:

  1. Su mousedown – prepara l’elemento per lo spostamento, se necessario (magari crearne un clone, aggiungere una classe o altro).
  2. Quindi su mousemove spostalo modificando left/top con position:absolute.
  3. Su mouseup – esegui tutte le azioni relative al completamento del drag’n ‘drop.

Queste sono le basi. Più avanti vedremo come altre funzionalità, come l’evidenziazione degli elementi sottostanti correnti mentre li trasciniamo.

Ecco l’implementazione del trascinamento di una pallina:

Se eseguiamo il codice, possiamo notare qualcosa di strano. All’inizio del drag’n’drop, la pallina “si biforca”: iniziamo a trascinare il suo “clone”.

Ecco un esempio in azione:

Prova a trascinare e rilasciare con il mouse e vedrai tale comportamento.

Questo perché il browser ha il suo supporto di trascinamento per immagini e alcuni altri elementi. Funziona automaticamente e va in conflitto con il nostro.

Per disabilitarlo:

Ora andrà tutto bene.

In azione:

Un altro aspetto importante – monitoriamo mousemove su document, non su ball. A prima vista può sembrare che il mouse sia sempre sopra la palla e possiamo mettere mousemove su di esso.

Ma come ricordiamo, mousemove si attiva spesso, ma non per ogni pixel. Quindi, dopo un rapido movimento, il puntatore può saltare dalla palla da qualche parte nel mezzo del documento (o anche fuori dalla finestra).

Quindi dovremmo ascoltare document per prenderla.

Posizionamento corretto

Negli esempi sopra la palla è sempre mossa in modo che il centro sia sotto il puntatore:

Non male, ma c’è un effetto collaterale. Per avviare il trascinamento, possiamo mousedown ovunque sulla palla. Ma se “la prendi” dal bordo, la pallina “salta” improvvisamente per centrarsi sotto il puntatore del mouse.

Sarebbe meglio se mantenessimo lo spostamento iniziale dell’elemento rispetto al puntatore.

Ad esempio, se iniziamo a trascinare dal bordo della palla, il puntatore dovrebbe rimanere sul bordo durante il trascinamento.

Aggiorniamo il nostro algoritmo:

Il codice finale con un posizionamento migliore:

In azione (all’interno di <iframe>) :

La differenza è particolarmente evidente se trasciniamo la pallina dall’angolo inferiore destro. Nell’esempio precedente la palla “salta” sotto il puntatore. Ora segue fluentemente il puntatore dalla posizione corrente.

Potenziali bersagli a caduta (droppabili)

Negli esempi precedenti la palla poteva essere lasciato cadere “ovunque” per restare. Nella vita reale di solito prendiamo un elemento e lo rilasciamo su un altro. Ad esempio, un “file” in una “cartella” o qualcos’altro.

Parlando di abstract, prendiamo un elemento “trascinabile” e lo rilasciamo su un elemento “droppabile”.

Noi bisogno di sapere:

  • dove l’elemento è stato rilasciato alla fine di Drag’n’Drop – per eseguire l’azione corrispondente,
  • e, preferibilmente, conoscere il droppable we stai trascinando per evidenziarlo.

La soluzione è piuttosto interessante e solo un po ‘complicata, quindi copriamola qui.

Cosa potrebbe essere la prima idea? Probabilmente per impostare mouseover/mouseup gestori su potenziali droppabili?

Ma questo non funziona.

Il problema è che, visto che siamo trascinando, l’elemento trascinabile è sempre al di sopra degli altri elementi.E gli eventi del mouse si verificano solo sull’elemento superiore, non su quelli sotto di esso.

Ad esempio, sotto ci sono due elementi <div>, uno rosso sopra quello blu uno (copre completamente). Non c’è modo di catturare un evento su quello blu, perché il rosso è in cima:

Lo stesso con un elemento trascinabile. La palla è sempre in primo piano rispetto ad altri elementi, quindi gli eventi accadono su di essa. Qualunque siano gli handler impostati sugli elementi inferiori, non funzioneranno.

Ecco perché l’idea iniziale di mettere gli handler su potenziali droppables non funziona nella pratica. Non funzioneranno.

Allora, cosa fare?

Esiste un metodo chiamato document.elementFromPoint(clientX, clientY). Restituisce l’elemento più annidato su determinate coordinate relative alla finestra (o null se le coordinate fornite sono fuori dalla finestra).

Possiamo usarlo in qualsiasi i nostri gestori di eventi del mouse per rilevare il potenziale droppabile sotto il puntatore, in questo modo:

Nota: dobbiamo nascondere la pallina prima della chiamata (*). Altrimenti di solito avremo una pallina su queste coordinate, poiché è l’elemento in alto sotto il puntatore: elemBelow=ball. Quindi lo nascondiamo e lo mostriamo di nuovo immediatamente.

Possiamo usare quel codice per controllare quale elemento stiamo “sorvolando” in qualsiasi momento. E gestire il rilascio quando accade.

Un codice esteso di onMouseMove per trovare elementi “droppabili”:

Nell’esempio seguente, quando la palla viene trascinata sopra la porta di calcio, la porta viene evidenziata.

Ora abbiamo l’attuale “obiettivo di rilascio”, su cui stiamo sorvolando, nella variabile currentDroppable durante l’intero processo e possiamo usarlo per evidenziare o qualsiasi altra cosa.

Riepilogo

Abbiamo considerato un algoritmo Drag’n’Drop di base.

I componenti chiave:

  1. Flusso di eventi: ball.mousedowndocument.mousemoveball.mouseup (non dimenticare di annullare ondragstart).
  2. All’inizio del trascinamento, ricorda lo spostamento iniziale del puntatore rispetto all’elemento: shiftX/shiftY e mantenerlo durante il trascinamento.
  3. Rileva elementi trascinabili e er il puntatore utilizzando document.elementFromPoint.

Possiamo gettare molto su queste basi.

  • Su mouseup possiamo finalizzare intellettualmente il rilascio: modificare i dati, spostare gli elementi.
  • Possiamo evidenziare gli elementi su cui stiamo sorvolando.
  • Noi possiamo limitare il trascinamento di una determinata area o direzione.
  • Possiamo utilizzare la delega degli eventi per mousedown/up. Un gestore di eventi di ampia area che controlla event.target può gestire Drag’n’Drop per centinaia di elementi.
  • E così via.

Esistono framework che creano architettura su di esso: DragZone, Droppable, Draggable e altre classi. La maggior parte di loro fa cose simili a quanto descritto sopra, quindi dovrebbe essere facile capirle ora. Oppure creane uno tuo, come puoi vedere che è abbastanza facile da fare, a volte più facile che adattare una soluzione di terze parti.

Write a Comment

Il tuo indirizzo email non sarà pubblicato. I campi obbligatori sono contrassegnati *