Nessun risultato. Prova con un altro termine.
Guide
Notizie
Software
Tutorial

Gestire input multi-touch e mouse in tutti i browser mobile

Link copiato negli appunti

Per siti, applicazioni e pagine Web diventa sempre più interessante introdurre l'interazione tattile, importante per garantire una usabilità migliore a chi utilizza prodotti touch ready come i diversi dispositivi mobili disponibili e i prodotti Windows 8 con le ultime versioni di Internet Explorer.

In questo articolo vediamo come poter creare una sorta di facade per poter utilizzare con la stessa interfaccia il modello dei pointer events introdotto con IE10, il TouchEvent di iOS , e il classico modello a eventi del mouse.

L'idea e il codice sono di Ted Johnson e nascono da alcune prove sul proprio tablet Samsung 700T, equipaggiato con Windows 8, sul quale ha lanciato le demo Touch Effects e Lasso Birds, per testarne il multi-touch.

Ted ha notato che Lasso Birds funzionava su diversi dispositivi e browser oltre che su IE10, ad esempio funziona in multi-touch anche su iOS. Ted Johnson prende quindi alcuni pattern da questa demo per estendere la compatibilità anche a browser più datati.

Prima di passare ad esaminare il codice, vediamo il risultato dei suoi esperimenti (dovrebbe funzionare su qualunque browser):




Il codice

Johnson inizia a spiegare il codice con l'esame dell'algoritmo di base per il disegno, che sfrutta il classico modello a eventi del mouse:

var drawingStarted = false;
function DoEvent(eventObject) {
	if (eventObject.type == "mousedown") {
		drawingStarted = true;
		startDraw(eventObject.pageX, eventObject.pageY);
	}
		else if (eventObject.type == "mousemove") {
		if (drawingStarted) {
			extendDraw(eventObject.pageX, eventObject.pageY);
		}
	}
	else if (eventObject.type == "mouseup") {
		drawingStarted = false;
		endDraw();
	}
}

Perché questo stesso algoritmo funzioni anche con i pointer events di IE10 dobbiamo aggiungere il riconoscimento di puntatori multipli (dobbiamo gestire eventi multi-touch) che possano essere scatenati simultaneamente.

Ci aiuta sapere che ciascuno di questi eventi touch è identificato con un pointerId specifico. Il pointer model di IE infatti scatena eventi diversi (MSPointerDown, MSPointerMove o MSPointerUp) per ciascun puntatore che cambia stato.

var drawingStarted = {};
function DoEvent(eventObject) {
	// per bloccare le funzioni standard di pan e zoom
	if (eventObject.preventManipulation)
		eventObject.preventManipulation();
	var pointerId = eventObject.pointerId;
	if (eventObject.type == "MSPointerDown") {
		drawingStarted[pointerId] = true;
		startDraw(pointerId, eventObject.pageX, eventObject.pageY);
	}
	else if (eventObject.type == "MSPointerMove") {
		if (drawingStarted[pointerId]) {
			extendDraw(pointerId, eventObject.pageX, eventObject.pageY);
		}
	}
	else if (eventObject.type == "MSPointerUp") {
		delete drawingStarted[pointerId];
		endDraw(pointerId);
	}
}

Per adattare il classico modello a eventi del mouse al "touch event model" di iOS dovremo lavorare un po'. Anche se, come per il pointer model di IE10, esiste un unico identificatore per ogni punto di contatto, i cambiamenti di stato non scatenano eventi multipli ma sono impacchettati in un evento unico. Per questo dovremo scorrere la lista dei changedTouches e valutare ogni evento touchstart, touchmove e touchend.

var drawingStarted = {};
function DoEvent(eventObject) {
	// senza questa riga invece di disegnare spostiamo lo schermo (pan)
	eventObject.preventDefault(); 
	for (var i=0; i<eventObject.changedTouches.length; ++i) {
		var touchPoint = eventObject.changedTouches[i];
		var touchPointId = touchPoint.identifier;
		if (eventObject.type == "touchstart") {
			drawingStarted[touchPointId] = true;
			startDraw(touchPointId, touchPoint.pageX, touchPoint.pageY);
		}
		else if (eventObject.type == "touchmove") {
			if (drawingStarted[touchPointId]) {
				extendDraw(touchPointId, touchPoint.pageX, touchPoint.pageY);
			}
		}
		else if (eventObject.type == "touchend") {
			delete drawingStarted[touchPointId];
			endDraw(touchPointId);
		}
	}
}

Nel mettere insieme questi tre algoritmi non possiamo fare a meno di notare le differenze tra i nomi degli eventi e tra i nomi degli identificatori unici per i punti di contatto. Inoltre dobbiamo terner conto dell'assenza di un un identificatore nel modello a eventi classico del mouse.

Nel codice completo, che stiamo per esaminare, l'autore aggiunge un controllo per verificare la posizione cambi veramente in presenza di uno spostamento. Questo perché il pointer model di IE10 crea un flusso di eventi MSPointerMove, anche con le stesse coordinate x e y, se l'utente rimane ferno su un punto con il dito o lo stilo. Filtrando questi eventi ridondanti, si evita di effetture chiamate a vuoto di extendDraw().

Questa verifica è implementata registrando l'ultima posizione (x,y) in un oggetto lastXY. Se lastXY esiste per un particolare id, esso rimpiazza l'oggetto drawingStarted che avevamo negli ultimi due esempi di codice.

var lastXY = {};
function DoEvent(eventObject) {
	// ferma pan e zoom e ci permette di disegnare
	if (eventObject.preventManipulation) eventObject.preventManipulation();
	// prendiamo possesso di questo evento
	if (eventObject.preventDefault) eventObject.preventDefault();
	// se abbiamo un array di changedTouches, lo utilizziamo
	// altrimenti creiamo un array di una posizione con il nostro eventObject
	var touchPoints = (typeof eventObject.changedTouches != 'undefined') ?
eventObject.changedTouches : [eventObject];
	for (var i = 0; i < touchPoints.length; ++i) {
		var touchPoint = touchPoints[i];
		// prendiamo l'unico id touchPoint se ne abbiamo uno o usiamo 1 per default
		var touchPointId = (typeof touchPoint.identifier != 'undefined') ? touchPoint.identifier : (typeof touchPoint.pointerId != 'undefined') ? touchPoint.pointerId : 1;
		if (eventObject.type.match(/(down|start)$/i)) {
			// elabora mousedown, MSPointerDown e touchstart
			lastXY[touchPointId] = { x: touchPoint.pageX, y: touchPoint.pageY };
			startDraw(touchPointId, touchPoint.pageX, touchPoint.pageY);
		}
		else if (eventObject.type.match(/move$/i)) {
			// elabora mousemove, MSPointerMove e touchmove
			if (lastXY[touchPointId] && !(lastXY[touchPointId].x == touchPoint.pageX && lastXY[touchPointId].y == touchPoint.pageY)) {
				lastXY[touchPointId] = { x: touchPoint.pageX, y: touchPoint.pageY };
				extendDraw(touchPointId, touchPoint.pageX, touchPoint.pageY);
			}
		}
		else if (eventObject.type.match(/(up|end)$/i)) {
			// elabora mouseup, MSPointerUp e touchend
			delete lastXY[touchPointId];
			endDraw(touchPointId);
		}
	}
}

Negli esempi fatti vengono ignorate cose come la registrazione per la ricezione degli eventi o i controlli necessari per vedere se vengono effettivametne applicati all'oggetto che contiene il disegno. Se volessimo realizzare un prodotto "reale" e che includa tutti i browser possibili (comprese le vecchie versioni di IE) ci sarebbe un po' di lavoro in più da fare.

Chi fosse interessato può scaricare la versione definitiva di questo progetto per il disegno multi-browser e multi-touch.

Per migliorare le performance delle interazioni touch, è necessario aggiungere all'elemento target la proprietà CSS che disabiliti le azioni touch di default:

#touchTarget {
	-ms-touch-action: none;
}

Nel codice JavaScript possiamo impostare questa proprietà utilizzando:

if (typeof touchTarget.style.msTouchAction != 'undefined')
	touchTarget.style.msTouchAction = "none";

Link utili

Ti consigliamo anche