Articles taggués ‘tween’

M4Tween - Javascript

10th août, 2009 par Arno

Depuis que je me suis mis plus sérieusement au javascript, je me suis toujours dit que çà pourrait être interessant de coder un petit moteur de Tween pour gérer de l’animation de base ou encore faire des benchmarks.
Alors oui ça existe déjà, certains sont même très performants (Scriptaculous…), ou encore directement intégré dans une librairie (Jquery…) mais le but ici n’est pas de remplacer ces librairies, dans un premier temps il s’agit de me faire une petite expérience javascript ainsi que de me permettre de manipuler un pattern dont Flo m’a parler : les linkedList (pour, ensuite, conquérir le monde).

Pour ce qui est de l’organisation générale, j’ai repris le principe que Flo avait déjà mis en place pour les M4Tween, excepté pour ce qui est de d’objet pooling que j’implenterais sans doute plus tard.
La classe M4Tween javascript requiert la librairie Prototype pour son excecution. On utilise notamment l’objet PeriodicalExcecuter pour émuler l’évènnement EnterFrame, ainsi que l’objet Template afin de pouvoir gérer facilement les propriétés css particulières (telles que la rotation par exemple).

Plutôt que de m’arracher les cheveux avec un navigateur qui ne respecte pas grand chose, j’ai fais l’impasse sur IE. Les tests effectués se sont trouvés concluant pour firefox 3.5, firefox 3.6 alpha1, Chrome 2.0, Chrome 3.0 ainsi qu’Opéra 10 beta 2.

Exemple d’utilisation simple de la classe :

<div id="myDiv"></div>
<script type="text/javascript" src="path/to/prototype.js"></script>
<script type="text/javascript" src="path/to/M4Tween.js"></script>
<script type="text/javascript">
//instanciation de la classe
M4Tween.create("myDiv", .7, {"marginLeft":"150px","opacity":.5});
</script>

Pour ce qui est des propriétés que l’on peut modifier, il n’y a que quelques propriétés supportées pour le moment : marginLeft, marginTop, opacity, top, left, width, height…

Les paramètres attendus par la méthode d’instanciation sont :

M4Tween.create(id, duree, params, onCompleteCallBack);
-id de l’élément du DOM à manipuler
-durée de l’animation
-paramètres ( {ease:Linera.easeNone, “marginLeft”:”140px”} )
-fonction à appeler à la fin de l’animation

Les fonctions de easing sont les mêmes que pour la version AS3, il s’agit des équations de Robet Penner.

Enfin voici quelques démo du moteur en action sur 50 div, 100 div, 150 div, 200 div et 250 div.

M4Tween.js

Object Pooling

23rd juillet, 2009 par Floz

Ça fait un moment que je veux mettre à plat ce que j’ai compris vis à vis des techniques d’Object Pooling et de Linked List, et nous y voilà. On va commencer par l’Object Pooling.

L’Object Pooling est une structure de programmation (ou Design Pattern) qui consiste en une liste d’objets disponibles à l’utilisation.

En gros, au lieu de passer par un new PooledObject( params… ), on utilisera une méthode similaire à Pool.create( params… ), qui nous renverra le PooledObject en question (comme nous l’aurait fait new PooledObject( params… )). La différence principale réside dans le fait que, derrière, la classe Pool regorge un certains nombres d’objets qui n’attendent qu’à être utilisés.

Instancier un objet, et passer par l’opérateur new s’avère assez gourmand en ressources. Du coup, créer une liste d’objet s’avère intéressant car :
- On instancie les objets par pack (la quantité est à définir en fonction des besoins),
- Une seule référence pour toute la liste (et donc tous les objets) est nécessaires.

Cette structure nous offre donc un gain en terme de performances, et de mémoire.

Voyons ce que ça donne au niveau du code. J’ai fais quelques schémas aussi pour représenter visuellement ce qu’il se passe ;)
A noter que le modèle de code provient du wiki de Joa Ebert. C’est à partir de ce lien que j’ai appris quasiment tout ce que je sais à propos de l’Object Pooling.

1) L’initialisation de la liste.

Dans le lien ci dessus, l’initialisation de la liste se fait au premier appel de la méthode statique create();

// - CONSTS ----------------------------------------------------------------------

private const GROWTH_RATE:int = 4;

// - PRIVATE VARIABLES -----------------------------------------------------------

private var _allowInstanciation:Boolean;
private var _availableInPool:int;
private var _currentPooledCircle:PooledCircle;

private var _next:PooledCircle;

public static function create():PooledCircle
{
var pooledCircle:PooledCircle;

if ( !_availableInPool )
{
var i:int = GROWTH_RATE;
while ( --i > -1 )
{
_allowInstanciation = true; {
pooledCircle = new PooledCircle();
} _allowInstanciation = false;

pooledCircle._next = _currentPooledCircle;
_currentPooledCircle = pooledCircle;
}

_availableInPool = GROWTH_RATE;
}

pooledCircle = _currentPooledCircle;
_currentPooledCircle = pooledCircle._next;

--_availableInPool;

return pooledCircle;
}

Ici, l’instanciation des objets se fait à partir d’une boucle while. Dans cet exemple, le premier objet crée au sein de cette boucle, sera le dernier disponible de la liste.
On note aussi l’existence d’une variable _currentPooledCircle. Celle ci contient la référence au prochain objet qui sera utilisé, et donc à l’objet renvoyé lors de l’appel de la méthode create().

A l’appel de la méthode create(), la valeur de _currentPooledCircle change : elle devient _currentPooledCircle._next, soit le prochain objet disponible.

Si _availableInPool arrive à 0, c’est qu’il n’y a plus d’objets disponibles. Ainsi, nous devons “refaire le plein” d’objets. A noter que lorsque _availableInPool est égal à 0, la valeur de _currentPoolCircle est null.

Bon, visuellement ça donne ça :

Initialisation de la liste d'objets

Initialisation de la liste d'objets

Les pastilles vertes correspondent à des objets disponibles.
La pastille vertes clair/tournant au jaune correspond à _currentPooledCircle et donc au prochain objet qui sera utilisé.
La pastille avec une croix orange correspond à un élément null.
Les flèches correspondent au lien entre chaque objet. Elles représentent la valeur de _next.

2) Création (récupération) d’un élément : appel à la méthode create().

Dans notre cas, l’initialisation se fait lors du premier appel de la méthode create(). Ainsi, l’initialisation sera directement suivi de l’attribution du premier objet disponible. Il y aura donc un objet disponible en moins dans la liste.
Le schéma qui va suivre représente plusieurs appel de la méthode create(), jusqu’à ce que _availableInPool soit égal à 0.

Création des éléments jusqu'à saturation.

Création des éléments jusqu'à saturation.

Une nouvelle pastille fait son apparition : pastille rouge, pour illustrer un élément “utilisé”.

A la fin du schéma, nous sommes donc à :
- _availableInPool = 0;
- _currentPooledCircle = null;

Sur le schéma, _currentPooledCircle est représenté par la pastille avec la croix orange.

Le prochain appel à la méthode create() se résultera par un nouveau remplissage de la liste.

3) Ré-initialisation de la liste.

Voici le schéma représentatif :

Nouvel appel à la méthode create() à la fin du dernier schéma.

Nouvel appel à la méthode create() à la fin du dernier schéma.

Ainsi, on conserve bien nos anciens objets. Ils sont toujours “là”.
Mais de nouveaux font leurs apparition, et eux ils sont disponibles à l’utilisation. A cet instant, la valeur de _availableInPool vient de passer à 4 (qui est la valeur de GROWTH_RATE).

Les deux dernières étapes se répèteront infiniment, au besoin.

4) Suppression d’un élément de la liste.

L’Object Pooling offrant comme principal avantage l’économisation de l’instanciation d’un objet (on privilégie la réutilisation), il serait intéressant de pouvoir rendre de nouveau disponible un objet qui ne l’est plus.

Dans la structure proposé par Joa Ebert, on a le modèle suivant :

public static function release( pooledCircle:PooledCircle ):void
{
pooledCircle._next = _currentPooledCircle;
_currentPooledCircle = pooledCircle;

++_availableInPool;
}

public function dispose():void
{
release( this );
}

Ainsi, rendre disponible un objet se fera par l’appel de la méthode dispose() (public) de celui ci.
Ex : pooledCircle.dispose(); // Soit pooledCircle une instance renvoyée par la méthode create();

Ou par la méthode release (public static) de la classe PooledCircle.
Ex : PooledCircle.release( pooledCircle );

Il s’en suit donc la remise en disposition de l’objet qui vient d’être libérer. La valeur de _currentPooledCircle devient donc égale à pooledCircle de nos exemples précédents.
L’ancienne valeur de _currentPooledCircle est attribuée à pooledCircle.next();

Le schéma représentatif :

deletion

Suppression des objets d'une liste via la méthode dispose() ou release()

En 1), l’état de la liste après 3 appels de la méthode create().
En 2), l’état de la liste après un appel de la méthode release( pooledCircle:PooledCircle ) ou dispose() (cf plus haut), sur le second objet qui a été récupéré via la méthode create().
En 3), l’état de la liste après un autre appel de la méthode release( pooledCircle:PooledCircle ) ou dispose(), sur le premier objet qui a été récupéré via la méthode create().

Il faut donc bien noter qu’on ne supprime pas un objet, on le rend de nouveau disponible (ne pas oublier de libérer les références de l’ancienne vie de l’objet, pour libérer la mémoire et éviter les comportements chelou au moment d’une nouvelle attribution).

4) Conclusion

Il est donc intéressant de passer par une liste d’objets de la sorte, notamment lorsqu’un certain nombre d’objets doit être crée.
Tweensy, moteur de tween, utilise d’ailleurs une structure d’Objet Pooling, couplé a un Array pour pouvoir parcourir la liste des objets crées.

C’est aussi ce que j’ai souhaité faire avec M4Tween, couplé a une linked list (plus intéressant d’itérer de cette manière qu’à travers un tableau). Mais je dois revoir quelques trucs sur la classe de M4Tween…

Je pense que je posterai prochainement sur les LinkedList.
En attendant, vous trouverez pas mal d’infos à ce sujet sur wikipedia et encore et toujours sur le wiki de Joa Ebert. A noter que le wiki de Joa Ebert ne propose qu’une représentation unique d’une LinkedList. Vous verrez sur wikipedia qu’il en existe plusieurs ;)

Si des choses ne sont pas claires, n’hésitez pas !