Drag and drop con createJS (seconda parte)

Circa un mesetto fa ho scritto un articolo sulla gestione delle collisioni e il trascinamento interattivo e a seguire un tutorial su come realizzare un puzzle game basato sugli stessi principi. Dopo aver testato i vari esempi, ho raccolto una serie di risultati interessanti che ho deciso di condividere, per cui quella che segue può essere considerata a tutti gli effetti la seconda parte del mio precedente articolo sul drag and drop.

⚠️ Nota tecnica:
Questo articolo è stato originariamente pubblicato nel 2016. Alcuni riferimenti (come le versioni dei browser o l’età del framework CreateJS) potrebbero risultare datati. Tuttavia, l’esempio incluso è stato aggiornato e testato: la tecnica descritta è ancora valida per gestire il drag and drop su dispositivi desktop e mobili, in alternativa a pressmove.

Un’alternativa a pressmove

Purtroppo l’evento <code>pressmove</code>, lo strumento a rigor di logica più adatto per gestire il drag and drop, almeno per adesso (ovvero nel momento in cui sto scrivendo questo articolo), non è del tutto affidabile sui dispositivi mobili. In teoria, il modo più semplice per implementare un trascinamento interattivo sarebbe quello di usare <code>pressmove</code> per innescare l’azione e <code>pressup</code> per interromperla. Ma fino a quando CreateJS e i browser non troveranno il modo di ovviare ai problemi di instabilità, è meglio mettere da parte questo gestore degli eventi.

La mia soluzione alternativa consiste nel suddividere il compito in tre passi:

  1. Associare l’evento mousedown ad un oggetto per stabilire il target da trascinare.
  2. Usare l’evento tick per innescare una funzione che modifica le coordinate dell’oggetto trascinato.
  3. Disattivare il tick quando l’utente interrompe il tocco, ovvero al verificarsi dell’evento pressup.

In più ci sono un paio di accorgimenti aggiuntivi.

  1. Fissare la proprietà timingMode su RAF_SYNCHED. Si tratta di una procedura per tentare di sincronizzare l’intervallo di tempo con il quale l’evento tick ripete le istruzioni con il framerate corrente. In base alla documentazione ufficiale si ottengono i risultati migliori con una frequenza impostata su numeri divisori di 60 (10,12,15, 20, 30 e 60). Personalmente, ho effettivamente riscontrato un leggero miglioramento delle prestazioni.
  2. Settare tutti gli oggetti non bitmap sul valore cache as bitmap tramite il pannello proprietà di Adobe Animate.

Chiaramente questa vuole essere solo una tra le possibili soluzioni. Anzi, se qualcuno ha trovato una soluzione migliore o meglio ancora un modo per impedire i bug derivati da pressmove, il suo contributo è ben accetto.

Un esempio di  drag and drop alternativo

Nel mio primo articolo sul drag and drop avevo realizzato un esempio costituito da quattro carte da gioco che, se trascinate nelle aree con il seme corrispondente, si agganciavano nella parte alta dello stage bloccando la loro posizione.
Partendo dallo stesso esempio, mi sono limitato a regolare la frequenza dei fotogrammi sul valore numerico 30. Inoltre, il codice dell’esempio è stato modificato in modo da fare a meno di pressmove.

createjs.Touch.enable(stage);
createjs.Ticker.timingMode = createjs.Ticker.RAF_SYNCHED;
var cliptarget;
var tk;
// quadri
this.a_quadri.b=this.bersaglio_q;
this.a_quadri.on("mousedown", startdrag.bind(this));
this.a_quadri.on("pressup",stopdrag.bind(this));
// picche
this.a_picche.b=this.bersaglio_p;
this.a_picche.on("mousedown", startdrag.bind(this));
this.a_picche.on("pressup",stopdrag.bind(this));
// fiori
this.a_fiori.b=this.bersaglio_f;
this.a_fiori.on("mousedown", startdrag.bind(this));
this.a_fiori.on("pressup",stopdrag.bind(this));
// cuori
this.a_cuori.b=this.bersaglio_c;
this.a_cuori.on("mousedown", startdrag.bind(this));
this.a_cuori.on("pressup",stopdrag.bind(this));
// funzioni
function startdrag(e) {
cliptarget=e.currentTarget;
tk = this.on("tick", drag.bind(this));
this.setChildIndex(cliptarget,this.numChildren-1); 
}
function drag() {
var local = stage.globalToLocal(stage.mouseX,stage.mouseY,cliptarget);
cliptarget.x=local.x;
cliptarget.y=local.y;
}
function stopdrag() {
this.off("tick", tk);
var local = cliptarget.b.localToLocal(0, 0, cliptarget);
if (cliptarget.hitTest(local.x, local.y)) {
cliptarget.x=cliptarget.b.x;
cliptarget.y=cliptarget.b.y;
cliptarget.rotation=0; 
cliptarget.removeAllEventListeners("mousedown");
}
}

Se facciamo un raffronto con la versione precedente, possiamo notare che non cambia quasi nulla. Solo, al posto dell’evento pressmove abbiamo un semplice mousedown. Inoltre abbiamo una funzione in più denominata drag che ha il compito di associare le coordinate del carta trascinata dall’utente tramite l’evento tick. A questo indirizzo trovate l’esempio online.

In conclusione

Il linguaggio CreateJS ha attraversato diverse fasi dalla sua nascita, e continua a essere un’ottima soluzione per prototipi e progetti HTML5 basati su canvas. Purtroppo, la compatibilità dei browser resta una sfida per tutti gli sviluppatori: a prescindere dagli standard, ogni browser può comportarsi in modo diverso. Fortunatamente, la tecnica descritta in questo articolo rappresenta ancora oggi una valida alternativa all’evento pressmove per le interazioni mobili.