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.
Innanzitutto, quando sviluppiamo in HTML5 è opportuno testare il nostro progetto sui principali browser sia sul desktop che sui dispositivi mobili. Un servizio che uso di solito, utile per capire la compatibilità di browser con i nuovi standard, è https://html5test.com. Il sito osserva una serie di parametri: i tag, la gestione dei video, la grafica 2d e così via. Per ogni opzione analizzata restituisce yes oppure no e alla fine mostra un punteggio numerico che va da 0 a 555. Ecco di seguito la classifica come appare nel momento in cui scrivo questo articolo (27 ottobre 2016) con i browser che uso attualmente su Windows 10.
- Google Chrome versione 54.0.2840.71. Punteggio: 499.
- Opera versione 41.0. Punteggio: 496.
- Firefox versione Firefox 49.0. Punteggio: 465.
- Edge versione 14. Punteggio: 460.
Ovviamente, come tutti i web developer sanno, allo stato attuale il browser più in linea con l’HTML5 sia su computer che sul mobile resta Google Chrome, ed infatti è quello che porta a casa il punteggio più alto. Al secondo posto, separato da uno scarto minimo si piazza un ottimo Opera (da sempre in prima linea sull’adeguamento agli standard) e a seguire Firefox ed Edge. Non avendo un Mac non posso testare Safari di persona, ma in base alle statistiche riportate sul sito, il punteggio della versione 9.1 dovrebbe essere attorno a 370.
Poiché le versioni per i dispositivi mobili variano, abbiamo punteggi leggermente diversi, tuttavia il predominio di Chrome resta invariato in tutte le piattaforme. Tornando all’argomento principale di questo articolo: quando si sviluppano pagine HTML5 che vedono createJS gestire il trascinamento interattivo, come ho scoperto effettuando vari test (e ammetto di essere rimasto sorpreso da alcuni risultati) le versioni sul mobile di alcuni browser mostrano problemi di vario tipo: da un rallentamento delle interazioni generate dal tocco fino al blocco totale. Incredibile ma vero, a creare maggiori problemi è stato Firefox 48.0 per Android.
Dopo aver analizzato i vari bug, Inizialmente mi sono limitato a fare qualche piccolo ritocco, ma alla luce delle varie testimonianze online da parte di vari colleghi – per ottenere un risultato soddisfacente – ho deciso di reimpostare tutta la gestione degli eventi mettendo da parte pressmove.
Un’alternativa a pressmove
Purtroppo l’evento pressmove, 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. Come abbiamo visto nel corso del precedente articolo, in teoria, il modo più semplice per implementare un trascinamento interattivo sarebbe quello di usare pressmove per innescare l’azione e pressup 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:
- Associare l’evento mousedown ad un oggetto per stabilire il target da trascinare.
- Usare l’evento tick per innescare una funzione che modifica le coordinate dell’oggetto trascinato.
- Disattivare il tick quando l’utente interrompe il tocco, ovvero al verificarsi dell’evento pressup.
In più ci sono un paio di accorgimenti aggiuntivi.
- 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.
- 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, qui invece il link per scaricare il sorgente della nuova versione: esempio_drag2.0
In conclusione
Il linguaggio createJS è ancora relativamente giovane, per cui rispetto ai numerosi pregi, possiamo perdonargli qualche piccolo difetto. Purtroppo, la compatibilità sui browser è una brutta gatta da pelare per tutti i linguaggi di programmazione. A prescindere dalla buona volontà dei creatori di un linguaggio nel voler rispettare gli standard, ci sarà sempre l’azienda produttrice del browser che dirà la sua nell’interpretare quel determinato codice. Probabilmente tra non molto il problema legato a pressmove sarà risolto e questo articolo diventerà obsoleto. Ma almeno per adesso, conviene non usare quel gestore per applicazioni destinate a smartphone e tablet. Visto che ci sono, nei prossimi giorni pubblicherò anche la versione riveduta e corretta del puzzle e per l’occasione inizieremo a parlare in maniera più approfondita di ottimizzazione per i dispositivi mobili.