1. Intro
Il web si evolve e con esso anche i linguaggi che lo ‘compongono’.
HTML5, insieme ai cugini CSS3 e Javascript, si prefigge di abbattere i limiti imposti dal suo predecessore aggiungendo al linguaggio di ipertesti tutte quelle feature finora sognate dai web developer.
Chi, navigando in rete, non si è appassionato di quei giochini interattivi, che spesso e volentieri si basano su concetti fisici (come quelli tanto noiosi studiati a scuola) che rendono l’esperienza meno artificiale e quindi più naturale?
In questo articolo vogliamo introdurre e integrare questi due aspetti: usare a nostro piacimento quelle semplici nozioni di fisica, o meglio di meccanica, applicate però al contesto della pagina web.
2. Gli Strumenti
Per realizzare il nostro esempio non abbiamo bisogno di nulla di particolarmente elaborato: se si ha una minima esperienza di web design o di web development possiedi già tutti gli strumenti necessari. Nel dettaglio necessitiamo di:
- Un browser moderno ( suvvia anche IE9 andrà benissimo 😛 )
- Un editor di testi ( questa scelta è più che personale, quindi usate quello che volete. Se però vi riconoscete nella categoria degli Hipster allora Vim o Emacs sono d’obbligo )
- Costrutti base del linguaggio js
- Un po’ di matematica/fisica ( su tutte il concetti di spazio tempo e velocità )
3. L’esempio
Come primo articoli introduttivo useremo l’empio classico delle bouncing balls. Il nostro esempio/esperimento mostrerà come rendere ‘reale’ l’animazione di rimbalzo (bouncing) di un oggetto, nel nostro caso un semplice div rotondo.
Step 1. Creazione dello stage
Sfruttando il tag canvas di HTML5, creiamo un semplice canvas di dimensioni a piacere ( io userò un canvas di 800x250px). Per comodità di calcoli userò dimensioni fisse, nulla vieta di usare dimensioni relative ( o percentuali ) facendo attenzione ad adattare il codice.
<canvas id="myScene" width="800" height="250"></canvas>
Con questa semplice istruzione creo un Canvas (una tela su cui disegnare) a cui assegno l’id ‘myScene’ e delle dimensioni 800 per 250 pixel.
Mediante javascript creiamo l’oggetto che verrà posizionato sul canvas, nel nostro caso una pallina da 10px di raggio. Aggiungiamo quindi una funzione js (dato che useremo la procedura di creazione dell’oggetto durante la nostra animazione) che svolga il lavoro ‘sporco’ per noi.
Creiamo una variabile che conterrà tutte le info che descriveranno il mio oggetto cerchio:
var cerchio = { raggio: 50, bordo: 5, x: canvas.width/2, y: 2*raggio, velX: 0, velY: 0 };
e passiamo questa variabile alla funzione che si occuperà di disegnarla a schermo
function creaPalla( cerchio )
{
// recupero la 'tela' su cui disegno
var canvas = document.getElementById("myScene");
// e il contesto 2d per avere a disposizione tutti gli strumenti per disegnare
var context = canvas.getContext("2d");
// imposto un valore random ad ogni spostamento
var red = Math.floor(Math.random()*255);
var green = Math.floor(Math.random()*255);
var blue = Math.floor(Math.random()*255);
context.beginPath();
// disegno il mio cerchio
context.arc( cerchio.x,cerchio.y, cerchio.raggio, 0, 2 * Math.PI, false);
// setto i colori del riempimento e del contorno
context.fillStyle = "rgba("+ red +","+ green +","+ blue +", 0.5)";
context.fill();
context.lineWidth = cerchio.bordo;
context.strokeStyle = "rgba("+ red +","+ green +","+ blue +", 0.5)";
context.stroke();
}
Il contesto 2d recuperato a partire dal canvas rappresenta un’astrazione logica (e software) comune alla maggior parte delle librerie grafiche. Si preoccupa di disegnare, mettendo a disposizione una serie di metodi che in un sol colpo di permettono di renderizzare le forme e i colori che decidiamo, mediante semplici istruzioni.
Posizioniamo l’oggetto appena creato all’interno del nostro canvas
var posX = canvas.width / 2;
var posY = canvas.height / 2;
// posiziono la mia pallina al centro del canvas
var miaPalla = creaPalla( 10, posX, posY );
Step 2. Caduta Massi.
Siamo già ad un buon punto. Abbiamo il nostro canvas e l’oggetto pronti pronti per essere animati.
Un’animazione, come si può facilmente intuire, deve svilupparsi nel tempo: quindi dal tempo t0 iniziale al tempo tf finale il nostro oggetto verrà matematicamente traslato, per la forza g di gravità che gli applicheremo, verso il basso.
Usando dei semplici concetti di fisica ci calcoliamo la velocità di caduta dell’oggetto mediante le righe
var time = new Date().getTime();
var timeDiff = time - lastTime;
var g = 9.8; // pixels / second^2
var velocitaPerFrame = g * timeDiff / 1000; // pixels / second
cerchio.velY += velocitaPerFrame;
cerchio.y += ( cerchio.velY * timeDiff);
Queste poche righe svolgono tutto il lavoro sporco:
– calcolano la differenza di tempo tra la passata interazione e quella corrente
– calcolano la velocità per frame (che cambia in base all’accelerazione e al tempo trascorso)
– aggiornano la posizione verticale corrente
Dato che questa equazione è funzione del tempo dovrà essere eseguita ad intervalli regolari per tutta la durata della caduta, quindi creeremo un metodo che si occuperà di disegnare l’animazione e di aggiornare il canvas: questo metodo verrà richiamato ad ogni intervallo temporale.
Il metodo animate() si occupa di tutto questo: aggiornare il canvas ad ogni periodo. Basandoci sulle considerzioni presenti su un famoso articolo di Paul Irish (2) usiamo il metodo requestAnimFrame (ottimizzato per le animazioni) piuttosto che il vecchio e abusato setInterval().
Questo metodo viene attaccato ricorsivamente in coda al metodo animate() e si occupa di far avanzare l’animazione:
requestAnimFrame(function(){
animate(lastTime, cerchio);
});
Step 3. A volte ritornano.
Cosa succede quando l’oggetto tocca terra (ovvero il bordo inferiore del canvas) ?
Attualmente si ferma, ma se vogliamo rendere più interessante l’esempio possiamo pensare di realizzare un sistema di rimbalzo tipico degli oggetti in caduta libera.
Per realizzare questa ‘feature’ dobbiamo capire cosa succede quando un oggetto tocca uno spigolo ( nel nostro caso il pavimento ): la sua velocità viene invertita e ridotta a causa dell’impatto con l’ostacolo. Ma di quanto e come?
Tutto dipende sia dalla consistenza dell’ostacolo e dell’oggetto. Per comodità trascuriamo l’ostacolo e ci concentriamo sull’oggetto aggiungendo alle sue proprietà un valore attenuazioneImpatto che determina la percentuale di riduzione della velocità.
var cerchio = { …altre proprietà… , attenuazioneImpatto: 0.2 };
Ora modifichiamo il comportamento con il contatto sul pavimento:
// se raggiungo il pavimento
if ( cerchio.y > canvas.height - cerchio.raggio) {
cerchio.y = canvas.height - cerchio.raggio;
// inverto la velocità
cerchio.velY *= -1;
// calcolo quindi la velocità di rimbalzo attenuata dal pavimento
cerchio.velY *= (1 - cerchio.attenuazioneImpatto);
if ( Math.ceil( cerchio.velY ) == 0.0 ) {
context.clearRect(0, 0, canvas.width, canvas.height);
creaPalla( cerchio );
return;
}
}
Conclusioni
Abbiamo solo sfiorato la superficie: questo è un tutorial molto semplice che concentra tutto il suo contenuto in 4 o 5 righe di codice. E’ però utile per capire come metter su un piccolo sistema su cui far girare le nostre animazioni basate su regole fisiche (e quindi presumibilmente più ‘reali’).
Ma questo è solo l’inizio. Si può pensare di modificare tale tutorial affinchè l’oggetto si muova anche in direzione orizzontale (moto di un proiettile?) oppure abbandonare l’idea del from-scratch e pensare di usare un framework fisico in js ( per sempio kineticjs(3) ). In base ai vostri feedback si può valutare o meno se seguire una direzione o l’altra (evolvendo man mano gli esempi) presentando qualcosa di più pratico e di facile utilizzo.
Riferimenti
- html5canvastutorials.com/labs/html5-canvas-interactive-ball-physics/
- http://paulirish.com/2011/requestanimationframe-for-smart-animating/
- http://kineticjs.com/