Categorie
Javascript Programmazione

{JS} API Device Mobile: Orientation

Cos’è l’API Mobile Orientation?

L’avvento dei dispositivi mobile e la loro possibilità di accedere a contenuti web ha fatto emergere l’esigenza di dover integrare eventi come la rotazione dello schermo, l’interazione con i touch screen, la geolocalizzazione, ecc. nelle pagine HTML. W3C ha introdotto una serie di specifiche per consentire la gestione tramite JavaScript di funzionalità tipiche del mondo dei device mobile.

La Device Orientation API consente di gestire l’orientamento di un dispositivo mobile come la sua posizione, verticale (portrait) o orizzontale (landscape) ed eventiali rotazioni che possono avvenire anche durante la navigazione di un app/sito.

Eventi

Esistono due eventi JavaScript che gestiscono le informazioni sull’orientamento di un dispositivo:

deviceorientationl’evento viene attivato quando l’accelerometro rileva un cambiamento nell’orientamento del dispositivo
devicemotionl’evento viene attivato quando è stata aggiunta una modifica dell’accelerazione

I due eventi sono differenti seppur possono sembrare similari.
Il primo “deviceorientation” rintraccia il movimento/orientamento del device mediante il giroscopio; il secondo “devicemotion”, ascolta i cambiamenti, intesi come dettagli del movimento, usufruendo del giroscopio e dell’accelerometro.

deviceorientation evento

Tutto quello che dovremo fare per entrambi i metodi è metterci in ascolto dell’evento con .addEventListener()

window.addEventListener('deviceorientation', handler, true);

Dopo essersi messi in ascolto dell’evento, l’evento sarà periodicamente verificato con dati di orientamento aggiornati.

“DeviceOrientationEvent” è l’evento da monitorare; “handler” è la funzione da richiamare con conterrà l’oggetto “event” auto-generato ed il valore “true” indica la gestione asincrona del processo; quest’ultimo è il valore di default quindi può essere omesso.

window.addEventListener('deviceorientation', function(event){
...
});

L’oggetto “event” conterrà le seguenti proprietà:

  • event.absolute
  • event.alpha
  • event.beta
  • event.gamma

Proprietà dell’oggetto event

L’oggetto event di questo evento è anche chiamato “Object Event Orientation”.
Per comprendere il funzionamento delle proprietà di questo oggetto dobbiamo immagine che sul nostro device ci siano tre assi: x, y e z.

ProprietàDescrizione
event.alpharestituisce il valore che rappresenta il movimento del dispositivo attorno all’asse z, rappresentato in gradi con valori compresi tra 0 (incluso) e 360 ​​(escluso).
event.betarestituisce il valore che rappresenta il movimento del dispositivo attorno all’asse x, rappresentato in gradi con valori compresi tra -180 (incluso) e 180 (escluso)
event.gammarestituisce il valore che rappresenta il movimento del dispositivo attorno all’asse y, rappresentato in gradi con valori compresi tra -90 (incluso) e 90 (escluso)

Con il termine movimento indichiamo la rotazione attorno agli assi.

Dimostrazione

Facciamo subito un esempio che mostri i valori rilevati in base alle inclinazioni del nostro device lungo gli assi x, y e z:

<p id="info">>Caricamento..</p>
var informazioni = document.getElementById('info');

window.addEventListener('deviceorientation', function(event){
informazioni.innerHTML=`Rotazione attorno asse Z: ${event.alpha}<br>
Rotazione attorno asse X: ${event.beta}<br>
Rotazione attorno asse Y: ${event.gamma}<br>
N.B: Funziona solo sui device mobile`; 
});

Caricamento..

Rotazione attorno asse Z

L’ immagine mostra la rotazione lungo l’asse Z nota come rotazione “alpha”.

Caricamento..

I valori che può assumere sono compresi tra 0° e 360° che viene escluso in quanto coincide con i 0° questo perchè si tratta di un giro completo.
Se ruotiamo verso destra si parte dal valore 360° che decrescerà in base alla roteazione.
Una roteazione di 90° verso destra avrà valore 270° fino alla metà del cerchio dove troviamo i 180° che collidono con la rotazione inversa verso sinista.
Inversamente per una roteazione veros sinistra si parte da 0°, valore che aumenterà in base all’aumentare dell’inclinazione.
Una roteazione di 90° in questo caso avrà valore 90°.

Dimostrazione
<div id="contenitore">
   <svg id="sfera">
    <circle cx="50%" cy="50%" r="40"></circle>
   </svg>
</div>

<style>
#contenitore{display: flex;  justify-content: center; align-items: center; height: 350px; width: 350px; border: 10px solid green; border-radius: 5px;}

#sfera{display:block; position:relative; top:0%; left:0%; right:0%; bottom:0%; transform: scale(1); fill:blue; stroke:blue; }
</style>
var contentiore = document.getElementById('contenitore');
var sfera = document.getElementById('sfera');

window.addEventListener('deviceorientation', function(event){

var rotazioneAsseZ = event.alpha;
//rotazione verso destra
if(rotazioneAsseZ > 0 && rotazioneAsseZ > 180 && rotazioneAsseZ < 300){
sfera.style.transform="scale(1.7)";
}
//rotazione verso sinistra
if(rotazioneAsseZ > 0  && rotazioneAsseZ > 10 && rotazioneAsseZ < 180){
sfera.style.transform="scale(0.4)";
}

}, true);

Ruotando attorno l'asse Z il device, la sfera diventerà più grande se ruotato verso destra, più piccola se ruotato verso sinistra.

E' necessario spiegare nel dettaglio le condizioni nei due if:

...
if(rotazioneAsseZ > 0 && rotazioneAsseZ > 180 && rotazioneAsseZ < 300){...}
...
if(rotazioneAsseZ > 0  && rotazioneAsseZ > 10 && rotazioneAsseZ < 180){...}

Il pirmo "if " intercetta le coordinate verso destra; entrambe le condizioni partiranno dalla verifica che ci sia un valore positivo maggiore di 0 "rotazioneAsseZ > 0" ; sia che la rotazione vada a sinistra o a destra avremo sempre un valore positivo.

Con le condizioni " rotazioneAsseZ > 180", " rotazioneAsseZ <180" verifichiamo la direzione della rotazione; una rotazione che va verso sinistra avrà un valore inferiore a 180, mentre una rotazione che va verso destra avrà un valore superiore a 180.

Con le condizioni "rotazioneAsseZ > 10" e "rotazioneAsseZ < 300" stiamo impostando una "tolleranza della rotazione" per evitare che venga attivata alla minima ed involontaria rotazione.

Un nota importante è necessaria per la disposizione delle condizioni;
nello specifico è stato necessario impostare in posizioni diverse le condizioni di tolleranza; questo perché se mettiamo una condizione antecedente che contiene un valore richiesto inferiore non possiamo accodare una condizione che ne richiede una superiore.

Nota: l'inizio del calcolo delle coordinate in gradi dei vari assi parte dalla posizione che ha il dispositivo non appena accede alla nostra app/sito (es. se si ricarica la pagina saranno ricalcolate le coordinate in base alla disposizione del device); il che significa che se dovessimo sospendere l'esecuzione con il blocca schermo e riprenderla in una posizione diversa troveremo delle incongruenze nella coordinate. 
Rotazione attorno asse X

Questa immagine mostra la rotazione lungo l'asse X nota come rotazione "beta".

Caricamento..

La rotazione beta parte da un valore di 0° e raggiunge 180° con un inclinazione superiore e -180° con un inclinazione inferiore lungo l'asse X.

Rotazione attorno asse Y

Questa immagine mostra la rotazione lungo l'asse Y nota come rotazione "gamma".

Caricamento..

Dimostrazione

Riprendiamo l'esempio precedente; questa volta faremo muovere il cerchio all'interno del suo container in base alle rotazioni attorno agli assi x e y fruttando i valori di "event.beta" e "event.gamma" e l'evento "deviceorientation"

<div id="contenitore2">
   <svg id="containerSVG" height="350px" width="350px">
    <circle id="cerchio" cx="50%" cy="50%" r="40"></circle>
   </svg>
</div>

<style>
#contenitore2{display: flex;  justify-content: center; align-items: center; height: 350px; width: 350px; border: 10px solid green; border-radius: 5px;}

#cerchio{display:block; position:relative; top:0%; left:0%; right:0%; bottom:0%; transform: scale(1); fill:blue; stroke:blue; }
</style>

Abbiamo modificato leggermente la struttura dell'HTML in modo da concentrarci principalmente sull'SVG Canvas e sul disegno SVG.

Per controllare il movimento della sfera a seconda delle rotazione degli assi
avremo la necessità di mappare le dimensioni di tutti gli elementi; faremo uso di tutta una serie di proprietà specifiche per la rivelazione delle dimensioni degli elementi:

element.clientWidth
element.clientHeight
restituisce la larghezza e l'altezza interna di un elemento ( elemento o elemento container) in pixel; include il riempimento ma esclude i bordi e margini 
element.offsetWidth
element.offsetHeight
restituisce l'altezza interna di un elemento ( elemento o elemento container) in pixel; includendo il riempimento, bordi e margini 
element.getBoundingClientRect().width
element.getBoundingClientRect().height
restituisce la larghezza e l'altezza del rendering, ovvero quando su un elemento è utilizzato es. transform: scale(2)
var containerSVG = document.getElementById('containerSVG');
var cerchio = document.getElementById('cerchio');

window.addEventListener('deviceorientation', function(event){

var rotazioneAxisY = event.gamma;
var rotazioneAxisX = event.beta;

//dimensioni container SVG Canvas della sfera
var larghezzaContainerSVG = containerSVG.clientWidth;
var altezzaContainerSVG = containerSVG.clientHeight;

//dimensioni del cerchio SVG
var larghezzaCerchio = cerchio.getBoundingClientRect().width;
var altezzaCerchio = cerchio.getBoundingClientRect().height;

//calcolo spazio disponibile di movimento
var spazioMovimentoX = larghezzaContainerSVG - larghezzaCerchio;
var spazioMovimentoY = altezzaContainerSVG - altezzaCerchio;

//impostiamo valori massimali di rotazione
if(rotazioneAxisY > 90){ rotazioneAxisY = 90 } //default range [-90,90]
if(rotazioneAxisY < -90){ rotazioneAxisY = -90 }
if(rotazioneAxisX > 90){ rotazioneAxisY = 90 } //default range [-180,180]
if(rotazioneAxisX < -90){ rotazioneAxisY = -90 }

//divisione dello spazio disponibile per 2 (sinistra, destra e su e giu) e trasformazione in valore percentuale

var spazioDivisoDueValorePercentualeX = (spazioMovimentoX/2)/90;
var spazioDivisoDueValorePercentualeY = (spazioMovimentoY/2)/90;


//ASSE X movimento [da 0 a 90] e [da 0 a -90]
if(rotazioneAxisX > 0){
var movimentoX = spazioDivisoDueValorePercentualeX*rotazioneAxisX;
cerchio.setAttribute('cx', `${movimentoX}%`);
} 
if(rotazioneAxisX < 0){
movimentoX  = spazioDivisoDueValorePercentualeX*Math.abs(rotazioneAxisX);
cerchio.setAttribute('cx', `-${movimentoX}%`);
}

//ASSE Y movimento [da 0 a 90] e [da 0 a -90]
if(rotazioneAxisY > 0){
var movimentoY = spazioDivisoDueValorePercentualeY*rotazioneAxisY;
cerchio.setAttribute('cy', `${movimentoY}%`);
} 
if(rotazioneAxisY < 0){
movimentoY  = spazioDivisoDueValorePercentualeY*Math.abs(rotazioneAxisY);
cerchio.setAttribute('cy', `-${movimentoY}%`);
}


}, true);

Il codice è stato abbondantemente commentato per far comprendere ogni procedimento.
In estrema sintesi è stato calcolato lo spazio disponibile per lo spostamento ricavato dalla sottrazione dello spazio occupato dalla SVG Canvas ed il disegno; sono stati modificati i valori massimi di rotazione; ed in fine sono stati riadattati i valori rilevati per poi passarli ai relativi attributi.

Nella dimostrazione live abbiamo invertito le indicazioni di modifica degli attributi "cx" e "cy" in modo che il movimento Asse X modifichi "cy" ed il movimento asse Y modifichi "cx" per rendere l'esperienza utente più naturale.

Note: Per convertire un numero negativo in positivo abbiamo utilizzato il metodo dell'oggetto Math "Math.abs()"; avremmo anche potuto moltiplicare il risultato "*-1"

devicemotion evento

In modo analogo all'orientamento del dispositivo possiamo intercettare e gestire alcuni dettagli sul suo movimento.

window.addEventListener("devicemotion", function(event){
...
});

Proprietà

Le proprietà dell'oggetto "Event Device Motion" forniscono dettagli sul movimento rilevato.

ProprietàDescrizione
event.accelerationrestituisce un oggetto con le proprietà xy, e z; questa proprietà riporta l'accelerazione registrata dal dispositivo espressa in m/s2; gli assi xy, e z rappresentano rispettivamente l'asse da ovest verso est, l'asse da sud a nord e l'asse perpendicolare alla terra
event.accelerationIncludingGravityquesta proprietà è analoga alla precedente, ma include nell'accelerazione del dispositivo anche l'accelerazione di gravità
event.rotationRaterestituisce un oggetto con proprietà alphabeta e gamma; rappresenta la velocità di rotazione intorno a ciascun asse espresso in gradi al secondo
event.intervalrestituisce un valore che indica l'intervallo di tempo in millisecondi di rilevazione del movimento da parte dell'hardware del dispositivo

La differenza tra la proprietà acceleration e la proprietà accelerationIncludingGravity è che quest'ultima è è soggetto alla forza di gravità sull'asse Z

Questi i valori restituiti e contenuti nell'oggetto di un device posato su di un tavolo monitorato mediante la porpiretà acceleration:

{ x: 0, y: 0, z: 0 }

Questi invece valori restituiti e contenuti nell'oggetto di un device posato su di un tavolo monitorato mediante la porpiretà accelerationIncludingGraivity:

{ x: 0, y: 0, z: 9.81 }
var informazioniMovimento = document.getElementById('output');

window.addEventListener('devicemotion',function(event){
var accellerazione = event.acceleration;
var accellerazionePiuGravita = event.accelerationIncludingGravity;
var velocitaRotazione = event.rotationRate;
var intervalloRilevamento = event.interval;
informazioniMovimento.innerHTML=`Accellerazioni registrate:<br> X: ${accellerazione.x} m/s2<br> Y: ${accellerazione.y} m/s2 <br> Z: ${accellerazione.z} m/s2<br>
Accelerazioni rilevate con gravità:<br> X: ${accellerazionePiuGravita.x} m/s2<br> Y: ${accellerazionePiuGravita.y} m/s2<br> Z: ${accellerazionePiuGravita.z} m/s2<br>
Velocità di rotazione assi x, y e z:<br> 
Alpha: ${velocitaRotazione.alpha} °/s<br>
Beta: ${velocitaRotazione.beta} °/s<br>
Gamma: ${velocitaRotazione.gamma} °/s<br>
Intervallo di rilevamento dispositivo:<br> ${intervalloRilevamento } ms`;
});

Lascia un commento

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