Pagine    Articoli    Prodotti    Forum    Cerca  
Nickname

Password


Non sei registrato?
Registrati a GPI qui!

Puoi anche attivare un vecchio utente GPI e chiedere una nuova password.
I Team

Mappa Team
I nostri utenti

Mappa Utenti
  Creare un gestore di tween minimale in Actionscript 3
Pubblicato da Andrea Venturi il 2012-06-17 13:25:39

In Flash i tween servono a modificare delle proprietà di un oggetto nel tempo.
Purtroppo però un singolo tween (di quelli dati dalla Adobe) gestisce una sola proprietà per volta, quindi dopo aver visto cosa offre il web (svariate librerie che gestiscono anche le cose più assurde) mi sono deciso di crearmene una mia versione semplificata che faccia giusto quello che serve a me senza troppi fronzoli (non è detto che sia la soluzione giusta per voi).

 

Un pò di matematica per capire meglio come gestire l'avanzamento dell'animazione:

 

FR : frame rate del filmato
T : tempo dell'animazione
dT = T / FR  #delta temporale per frame
pI : valore iniziale della proprietà
pF : valore finale della proprietà
dP : (pF - pI) / dT #incremento per frame della proprietà

 

Capiti questi semplicissimi conti il resto è solo implementazione, che a partire dalla versione basilare, potete aggiungere funzionalità e/o ottimizzare alcuni passaggi.

 

Per prima cosa ho creato una classe che mi gestisse la lista di animazioni, quindi mi servirà un array per maneggiarle, un booleano per gestire l'interruzzione brusca di tutte le animazioni in corso e un intero per mantermi il frameRate del mio filmato.

 

private var frameRate:int = 0;
private var lista:Array = [];
private var killed:Boolean = false;

 

Quindi il costruttore necessita il parametro frameRate per poter inizializzare la variabile:

 

public function TweenManager(fr:int) 
{
      frameRate = fr;
}

 

Per la firma del metodo per l'inserzione di una nuova animazione mi sono adattato a quello che utilizzano le altre librerie, semplificando dove si può:

 

public function to(target:MovieClip, tempo:Number, param:Object):void

 

target è l'oggetto che subisce la modifica
tempo indica il tempo complessivo di esecuzione della modifica
param contiene le proprietà con relativo valore finale (es.: x:100, y:50, alpha:0.2 ecc..) e 5 proprietà aggiuntive che mi tornano utili per il mio lavoro e sono:

  1. delay : ritardo prima di far partire l'animazione
  2. onStart :  funzione da invocare prima dell'animazione
  3. onStartParams : parametri da passare alla funzione onStart
  4. onComplete : funzione da lanciare alla fine dell'animazione
  5. onCompleteParams :parametri da passare alla funzione onComplete

Per recuperare queste informazioni:

 

//prendo il delay
if(param.delay != undefined)
{
    delay = Number(param.delay) *800;
    delete param.delay;
}
//onStart
var sFunction:Function = null;
if(param.onStart!= undefined)
{
    sFunction = param.onStart;
    delete param.onStart;
}
//onStartParams
var onStartParams:Array = [];
if(param.onStartParams != undefined)
{
    onStartParams = param.onStartParams as Array;
    delete param.onStartParams;
}
//onComplete 
var cFunction:Function = null;
if(param.onComplete != undefined)
{
    cFunction = param.onComplete;
    delete param.onComplete;
}
//onCompleteParams
var onCompleteParams:Array = [];
if(param.onCompleteParams != undefined)
{
    onCompleteParams = param.onCompleteParams as Array;
    delete param.onCompleteParams;
}

 

Se vi state chiedendo il motivo per cui cancello le proprietà "particolari" dai params, la risposta è semplice, per semplificarmi la gestione dei prorpietà base, se queste sono esplicitate:

 

var dT:Number = (tempo) * frameRate;
var delay:Number = 0.0;
var prop:Array = [];
var step:Object = { };
for (var prp in param)
{
    prop.push(prp);
    step[prp] = (param[prp] - target[prp]) / dT;
}

 

Ciclo su quelle rimanenti e ne salvo il nome e il valore da raggiungere, con il quale vado a calcolarmi il valore dello step incrementale, da poter così passare alla classe che lo gestirà.

 

Questa simpatica funzione si conclude con il consumo del primo parametro il delay, quindi non vado a far altro che settarmi un time-out per poi far partire tutto il meccanismo:

 

setTimeout(startAnimation,delay,target, prop, step,dT,cFunction,onCompleteParams,sFunction,onStartParams);

 

Ora andiamo ad analizzare la funzione start animation, che dovrà chiamare la funzione onStart se specificata e lanciare l'animazione:

 

public function startAnimation():void 
{
  if (arguments[6] != null)
  {
    arguments[6].apply(null, arguments[7]);
  }

  var tt:GOLTween = new GOLTween(lista.length);
  tt.Tweener(arguments[0], arguments[1], arguments[2],arguments[3],arguments[4],arguments[5]);
  lista.push(tt); 

  if (!hasEventListener(Event.ENTER_FRAME) && !killed )
  {
    addEventListener(Event.ENTER_FRAME, onEnterFrame);
  }
}

 

Purtroppo non potendo specificare gli argomenti nella firma della funzione si devono richiamare gli argomenti in maniera poco classica, andando a reperirli nell'array degli argomenti arguments, quindi tenete sempre sott'occhio la chiamata per capire quale argomento si sta utilizzando.
Ora la funzione che ogni frame verrà richiamata, che non farà altro che aggiornare le animazioni nell'array o le eliminerà al loro completamento:

 

private function onEnterFrame(e:Event):void 
{
    for (var it:int; it < lista.length ;++it )
    {
        if (!lista[it].ucciso)
        {
            lista[it].onIncrement();
        }
        else
        {
            lista[it].onComplete();
            lista.splice(it, 1);
        }
    }
    if (lista.length == 0 )
    {
        removeEventListener(Event.ENTER_FRAME, onEnterFrame);
    }
}

 

 

infine la funzione che arresta tutto:

 

public function kill():void
{
    killed = true;
    removeEventListener(Event.ENTER_FRAME, onEnterFrame);
    for (var t:int = 0; t < lista.length;++t )
    {
        lista[t].ucciso= true;
        lista[t] = null;
    }
}

 

E qui si conclude la clase che gestisce le animazioni.
Ora passiamo alla classe che esegue la singola animazione.

 

public function onIncrement():void 
{
    if (ucciso){ return; }
    _nStep--;
    ucciso = _nStep == 0;
    for(var prp in _prop)
    {
        _target[_prop[prp]] += _step[_prop[prp]];
    }
}

 

e la chiamata alla onComplete, che abbiamo visto prima:

 

public function onComplete():void 
{
    if (_onComplete != null)
        {
            _onComplete.apply(null,_completeParam);
        }
}

 

per concludere la funzione che banalmente uccide l'animazione:

 

public function kill():void 
{
    ucciso = true;
}

 

Fine.

 

Per chi ha bisogno di spingere al massimo le potenzialità di queste funzioni, può migliorarlo andando a cambiare prima di tutto l'array nel gestore e sostituirlo con una struttura dati tipo Linked-List che è più performante sia nel ciclare tra gli elementi, in inserimento ed eliminazione di oggetti.

 

Campagne crowfunding

Just One Line
Siamo presenti su

     
Copyright ©2016 - Manifesto - Privacy - Termini di Servizio - Community - Collaboratori - Contattaci