Dans le chapitre précédent, nous avons commencé à donner vie à nos applications en interagissant avec l'utilisateur au moyen du clavier et de la souris. Grâce à cela, les programmes peuvent agir suivant les décisions prises par l'utilisateur. Néanmoins, il n'est pas toujours nécessaire d'attendre une action de la part de l'utilisateur avant de devoir mettre à jour l'affichage.
Comme vous l'aurez deviné, nous allons ici apprendre à créer des animations. Cela vous permettra d'améliorer la jouabilité de vos jeux ou la fluidité de votre site web. Dans ce chapitre, nous allons donc voir comment animer des objets en utilisant différentes techniques.
L'animation par images-clés
L'évènement Event.ENTER_FRAME
Pour bien comprendre ce qui va suivre, nous allons faire un petit point sur les animations Flash et leur fonctionnement. Une application Flash est une animation, cadencée à un nombre d'images par seconde constant. Toute modification liée à un objet présent sur l'écran entraîne donc une mise à jour de l'affichage à l'image suivante, en générant un nouveau rendu.
À l'origine, ce système d'animations par images successives permettait à l'aide de Flash Professionnal de réaliser des animations complexes. En utilisant les MovieClip
, il était alors possible d'animer des objets à l'aide d'images-clés. Comme nous l'avions dit, cette classe dérivée de la classe Sprite
ajoute une ligne de temps (ou timeline en anglais).
Voici un exemple d'édition d'un MovieClip
à l'intérieur du logiciel Adobe Flash Professionnal :
Revenons maintenant à nos moutons, ou plutôt à l'Actionscript ! Ici, le principe est le même ; l'affichage est mis à jour automatiquement à partir des événements qui se produisent et d'éventuelles modifications réalisées à l'intérieur du code. Les rendus sont donc créés à fréquence constante, définie par le nombre d'images par seconde paramétré pour le projet. Bien entendu, tout ceci est géré directement par Flash Player !
Pour définir le cadencement de l'animation, nous pouvons paramétrer la propriété frameRate
lié au fichier SWF, comme dans la ligne suivante :
1 | [SWF(width="400",height="200",backgroundColor="#ffffff",frameRate="25")] |
Une autre manière de modifier la cadence d'animation du programme est de changer la valeur de la propriété frameRate
de l'objet stage
, comme ceci :
1 | stage.frameRate = 30; |
Créer une animation basique
Animer un objet consiste à faire évoluer au moins l'une de ses propriétés au cours du temps. Par exemple, lors d'un mouvement, la position d'un objet est décomposée en petits incréments répartis tout au long de sa trajectoire au fil du temps. Une bonne idée serait alors de synchroniser l'exécution du code avec la cadence du fichier SWF. En effet, il est inutile de modifier la position d'un objet toutes les millisecondes si un rendu est généré seulement toutes les 40 millisecondes.
Lors de l'exécution du programme, un événement de type Event.ENTER_FRAME
est généré avant chaque nouveau rendu. Il est ainsi possible de réaliser des modifications avant chaque mise à jour de l'affichage. Je vous propose donc de réaliser une petite animation utilisant ce principe.
Dans un premier temps, nous allons créer un petit carré au centre de notre application :
1 2 3 4 5 6 | var objet:Shape = new Shape(); objet.graphics.beginFill(0xAA00FF); objet.graphics.drawRect( -25, -25, 50, 50); objet.x = stage.stageWidth / 2; objet.y = stage.stageHeight / 2; addChild(objet); |
Pour suivre la cadence de l'animation, nous pouvons alors ajouter un écouteur à l'événement Event.ENTER_FRAME
, en associant une fonction d'écouteur qui servira à animer notre objet.
Voici notre instruction :
1 | objet.addEventListener(Event.ENTER_FRAME, animerObjet); |
À l'intérieur de notre fonction, nous pouvons ensuite animer notre carré. Par exemple en incrémentant la propriété rotation
, nous forçons l'objet à tourner à chaque nouveau rendu, comme ceci :
1 2 3 4 | function animerObjet(event:Event):void { objet.rotation++; } |
Au final, nous obtenu un objet en perpétuelle rotation comme illustré sur l'image suivante.
Ce type de manipulation est bien évidemment réalisable avec n'importe quel attribut ou méthode d'un objet. Il est alors possible de modifier sa position, son orientation, sa taille, ou encore les filtres associés à cet objet. La seule limite est votre imagination !
Je vous propose maintenant d'aller un peu plus loin et d'animer un flou sur notre objet. Nous allons également en profiter pour apprendre à stopper une animation grâce à la méthode removeEventListener()
.
Dans un premier temps, plaçons un filtre de type BlurFilter
, sans effet apparent :
1 2 | var flou:BlurFilter = new BlurFilter(0, 0, BitmapFilterQuality.HIGH); objet.filters = new Array(flou); |
Puis, nous allons retravailler notre fonction animer()
, afin d'incrémenter le flou horizontal avant chaque nouveau rendu de notre application. Il nous faudra aussi mettre à jour la propriété filters
de notre objet, pour que le filtre prenne effet. Pour finir nous supprimerons notre écouteur à l'événement Event.ENTER_FRAME
pour stopper l'animation. Dans notre exemple, nous arrêterons d'augmenter le flou horinzontal lorsque celui-ci aura atteint la valeur 50
.
Voyez plutôt :
1 2 3 4 5 6 7 8 | function animerObjet(event:Event):void { flou.blurX++; objet.filters = new Array(flou); if(flou.blurX > 50){ objet.removeEventListener(Event.ENTER_FRAME, animerObjet); } } |
En lançant l'animation, vous verrez alors notre carré se flouter progressivement, puis se figer au bout d'un certain temps, comme sur l'image suivante.
L'utilisation de Timer
La classe Timer
Lorsqu'une animation Flash est lancée, les instructions du programme sont exécutées à une vitesse qui est définie par votre machine. Ainsi quand vous passerez d'une machine à une autre, les instructions ne seront pas exécutées au même rythme. Il est donc impossible à l'intérieur du code de synchroniser correctement les instructions avec le temps. Théoriquement, il serait possible d'utiliser les événements Event.ENTER_FRAME
pour réaliser cela, mais je vous recommande très fortement d'utiliser plutôt les Timer
!
Comme vous n'êtes pas obligés de me croire sur parole, je vais essayer de vous donner diverses raisons qui justifient ce choix. Tout d'abord, les événements Event.ENTER_FRAME ne sont pas générés de manière extrêmement précise. En effet, un rendu trop chargé ou un calcul un peu complexe pourrait ralentir légèrement la cadence de votre animation. Par ailleurs, une modification de la cadence de votre fichier SWF suffirait à dérégler l'intégralité de votre programme. Vous seriez alors dans l'obligation d'effectuer de lourdes modifications à l'intérieur de votre code source, même si l'impact peut être limité en utilisant des constantes. Enfin, il est probable que vous ayez besoin d'exécuter des instructions à une cadence élevée ; cela ne justifie pas à accélérer le rythme de rendu de l'affichage pour autant.
Ainsi lorsqu'on souhaite gérer le temps de manière significative, il est préférable d'utiliser la classe Timer
. Le contrôle sur les intervalles temporels en sera amélioré, et vous facilitera la vie pour la création de « moteur physique » par exemple.
Comme nous l'avons dit, l'utilisation de l'événement Event.ENTER_FRAME
est le meilleur moyen de synchroniser vos actions à l'intérieur du code avec la cadence d'animation du fichier SWF. Néanmoins, l'événement Event.ENTER_FRAME
doit être utilisé uniquement à des fins d'animation et non pour gérer le temps en tant que tel. Préférez alors l'utilisation de Timer
.
La figure suivante décrit cette classe Timer
.
Sans surprise, l'utilisation d'un Timer
se base sur le concept des événements. Son principe de fonctionnement consiste donc à générer des événements à intervalles temporels réguliers. Ces événements sont alors de type TimerEvent
et sont produits à intervalles spécifiés par la propriété delay
de la classe Timer
. La propriété repeatCount
permet de définir le nombre total d'événements qui doivent être créés.
L'instruction suivante est un exemple qui définit un Timer
paramétré à 5 événements générés toutes les secondes :
1 | var minuteur:Timer = new Timer(1000, 5); |
La propriété delay
représente un intervalle de temps en millisecondes. Par ailleurs, la valeur 0
pour repeatCount
sert à répéter la création d'événements un nombre infini de fois.
Lors de l'instanciation de d'un objet de la classe Timer
, le décompte du temps n'est toutefois pas encore lancé. Pour contrôler cela, nous disposons des trois méthodes start()
, stop()
et reset()
.
Enfin, sachant que la classe Timer
hérite de EventDispatcher
, nous pouvons assigner un écouteur à notre objet de type Timer
.
Voici donc le code complet permettant de lancer la génération d'événements TimerEvent
.
1 2 3 4 5 6 7 | var minuteur:Timer = new Timer(1000, 5); minuteur.addEventListener(TimerEvent.TIMER, maFonction); minuteur.start(); function maFonction(event:TimerEvent):void { // Instructions } |
Comme vous l'aurez compris, l'évenement TIMER
de TimerEvent
est le type d'événements générés à chaque intervalle de temps décompté. Mais il existe également le type COMPLETE
qui est produit lorsque le nombre d'itérations repeatCount
est atteint, et que le Timer
arrive à termes.
Exercice : une horloge
Présentation des graphismes
Comme le titre l'indique, nous allons réaliser une horloge analogique. Cet exercice va nous permettre de mettre en pratique tout ce que nous avons vu sur les classes Timer
et TimerEvent
.
Mais avant d'en dire plus sur le fonctionnement et la gestion des écouteurs, je vous présente le résultat de l'exercice sur la figure suivante.
Sachant que l'objectif n'est pas la partie visuelle, mais plutôt la gestion du temps, j'ai décidé de placer une bonne partie du code lié à l'affichage à l'intérieur de deux classes Cadran
et Aiguille
. La première permet de dessiner le cadran de l'horloge en spécifiant le rayon de celui-ci dans le constructeur. La seconde classe sert à tracer une aiguille en fonction de son type : HEURE
, MINUTE
ou SECONDE
.
Je vous laisse découvrir ces deux classes. Voici la première nommée Cadran
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | package { import flash.display.Sprite; import flash.display.Shape; public class Cadran extends Sprite { public function Cadran(rayon:Number) { super(); for (var i:int = 0; i < 60; i++) { var point:Shape = new Shape(); point.graphics.beginFill(0x888888); if((i%5) == 0){ point.graphics.drawCircle(0, 50, 2); }else { point.graphics.drawCircle(0, 50, 1); } point.rotation = i * 6; addChild(point); } } } } |
Cadran.as
Et voici la classe Aiguille
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | package { import flash.display.Shape; public class Aiguille extends Shape { // Constantes public static const HEURE:String = "heure"; public static const MINUTE:String = "minute"; public static const SECONDE:String = "seconde"; // Constructeur public function Aiguille(type:String) { super(); var longueur:Number; if (type == HEURE) { longueur = 30; }else { longueur = 45; } if (type == SECONDE) { graphics.beginFill(0x880000); }else { graphics.beginFill(0x000000); } graphics.moveTo(-1.5, 0); graphics.lineTo(-0.5, longueur / 4); graphics.lineTo(0.5, longueur / 4); graphics.lineTo(1.5, 0); graphics.lineTo(0.5, -longueur); graphics.lineTo(-0.5, -longueur); graphics.endFill(); } } } |
Aiguille.as
Si vous souhaitez tester le code de cette horloge, je vous invite alors à créer deux nouvelles classes et d'y insérer les lignes de code que je viens de vous fournir.
Mise en place des éléments à l'écran
Maintenant, il est temps de placer les différents éléments qui composent l'horloge sur la scène principale. Nous aurons donc besoin d'un cadran, ainsi que trois aiguilles, une de chaque type. Enfin, nous centrerons le tout pour avoir l'horloge au milieu de l'écran.
Il n'y a rien de compliqué pour faire ça, c'est pourquoi je vous expose le code directement :
1 2 3 4 5 6 7 8 9 10 11 | // Mise en place des objets d'affichage à l'écran x = stage.stageWidth / 2; y = stage.stageHeight / 2; var cadran:Cadran = new Cadran(50); var aiguilleHeures:Aiguille = new Aiguille(Aiguille.HEURE); var aiguilleMinutes:Aiguille = new Aiguille(Aiguille.MINUTE); var aiguilleSecondes:Aiguille = new Aiguille(Aiguille.SECONDE); addChild(cadran); addChild(aiguilleHeures); addChild(aiguilleMinutes); addChild(aiguilleSecondes); |
À ce stade, nous avons alors nous horloge entièrement dessinée, avec les aiguilles en position initiale, c'est-à-dire indiquant l'instant « 00:00:00 ».
Mettre l'horloge à l'heure
Il serait grand temps de mettre à jour l'heure affichée sur notre horloge. Pour cela, nous allons faire une brève introduction à la gestion des dates et des heures.
Pour pouvoir manipuler les dates et les heures, nous avons à notre disposition la classe Date
. Cette dernière intègre tout un tas de propriétés et méthodes servant notamment à récupérer des informations sur la date en question.
Voilà comment instancier un objet Date
, en référence au jour et à l'heure actuels :
1 | var maDate:Date = new Date(); |
Parmi l'ensemble des méthodes fournies par cette classe, nous allons avoir recours aux trois suivantes : getHours()
, getMinutes()
et getSeconds()
, dont les noms sont relativement explicites.
Si vous voulez en apprendre davantage sur l'objet Date
, je vous conseille d'aller visiter sa page officielle.
Pour en revenir à notre horloge, voici comment nous pouvons récupérer l'heure actuelle pour mettre celle-ci à jour :
1 2 3 4 5 | // Mise à l'heure des aiguilles var heureActuelle:Date = new Date(); aiguilleSecondes.rotation = 360 * heureActuelle.getSeconds() / 60; aiguilleMinutes.rotation = 360 * heureActuelle.getMinutes() / 60; aiguilleHeures.rotation = 360 * heureActuelle.getHours() / 12; |
Animer l'horloge
C'est maintenant que nous allons utiliser la classe Timer
, afin de générer un événement à intervalle régulier. Dans notre cas, nous voulons pouvoir mettre à jour nos aiguilles à chaque seconde. Également, nous devrons faire en sorte que nos aiguilles tournent indéfiniment.
Si vous avez bien suivi ce qui a été dit avant, vous êtes alors capables de trouver ce code tous seuls :
1 2 3 4 | // Mise en place du Timer var minuteur:Timer = new Timer(1000, 0); minuteur.addEventListener(TimerEvent.TIMER, bougerAiguilles); minuteur.start(); |
Pour animer notre horloge, nous devrons donc mettre à jour l’orientation de chacune des aiguilles toutes les secondes. La seule difficulté est de ne pas se tromper sur la valeur des angles à appliquer pour chaque aiguille.
Notre fonction d'écouteur ressemble alors à ceci :
1 2 3 4 5 6 7 | // Fonction d'écouteur function bougerAiguilles(event:TimerEvent):void { aiguilleSecondes.rotation += 360 / 60; aiguilleMinutes.rotation += 360 / (60 * 60); aiguilleHeures.rotation += 360 / (60 * 60 * 24); } |
Voilà qui est fait !
Les interpolations avec TweenMax
Enfin, il est possible d'utiliser ce que l'on appelle des interpolations (ou tweens en anglais) pour animer des objets. Il n'est dans ce cas pas nécessaire de répéter des instruction, les interpolations vont faire tout le travail à notre place !
Une interpolation est un objet qui va calculer l'animation pour nous sur un ou plusieurs objets, sur une ou plusieurs de leurs attributs. On spécifie la durée de l'animation, et les valeurs finales des attributs. Une fois l'interpolation lancée, ces attributs varierons selon ces paramètres pour créer une animation fluide. Et tout ceci tient en une ligne de code pour nous !
La librairie TweenMax
Présentation et installation
Nous allons utiliser une librairie d'interpolation créée par Greensock qui s'appelle TweenMax ou GSAP (GreenSock Animation Platform) et qui est très connue et utilisée dans le milieu, car elle présente de nombreux avantages en plus d'être facile d'utilisation : il est donc intéressant de savoir s'en servir. Le seul inconvénient de cette librairie est qu'elle est payante pour certains projets commerciaux.
Il existe d'autres librairies d'interpolations très différentes auxquelles vous pouvez également jeter un coup d’œil si vous êtes curieux. Je citerais en exemple Actuate, GTween, Tweener, et bien d'autres…
Commençons par télécharger la librairie sur la page officielle sur le site de GreenSock. Cliquez sur le bouton « Get GSAP » à gauche puis sur le bouton « Download AS3 » et enfin sur le bouton « Download zip ».
Une fois l'archive récupérée, ouvrez-là et extrayez le dossier com dans le dossier des sources de votre projet (par exemple, src).
Voilà ! La librairie est prête à être utilisée !
Importation dans les classes
Pour pouvoir utiliser TweenMax dans une de vos classes, il faut ajouter ces deux lignes en haut du package de la classe :
1 2 | import com.greensock.*; import com.greensock.easing.*; |
La première ligne importe toutes les classes de base de TweenMax, tandis que la seconde importe les classes des courbes d'animation permettant de modifier les interpolations. Nous les détaillerons un peu plus loin.
Les bases de l'animation par Tween
Mise en place
Avant de réellement utiliser TweenMax, nous allons préparer un peu le terrain : il nous faut bien quelque chose à animer ! Commençons par créer un beau carré ainsi :
1 2 3 4 5 6 | var carre:Sprite = new Sprite(); carre.graphics.beginFill(0xAA00FF); carre.graphics.drawRect(-25, -25, 50, 50); carre.x = stage.stageWidth / 2; carre.y = stage.stageHeight / 2; addChild(carre); |
Pour l'instant, nous avons ceci comme résultat :
Notre objectif est d'animer le carré au passage de la souris. Ajoutons des écouteurs d'événement comme nous l'avons vu dans les chapitres précédents :
1 2 3 4 5 6 7 8 9 10 11 12 | carre.addEventListener(MouseEvent.ROLL_OVER, onOver); carre.addEventListener(MouseEvent.ROLL_OUT, onOut); function onOver(e:MouseEvent):void { } function onOut(e:MouseEvent):void { } |
Utilisation de TweenMax
La librairie s'utilise principalement avec la classe TweenMax
(du package com.greensock
). Il y a également des versions allégées de cette classe pour réduire la taille de votre application : TweenLite
et TweenNano
. Nous ne les détaillerons pas ici car elles fonctionnent exactement de la même manière, certaines fonctionnalités en moins.
Les méthodes de la classe TweenMax
que nous allons manipuler sont statiques : il n'est donc pas nécessaire d'instancier la classe à l'aide du mot-clé new
.
La première (et sans doute la plus utilisée) est la méthode statique TweenMax.to
, to signifiant vers en anglais. Voici sa signature :
1 | TweenMax.to(cible:Object, duree:Number, parametres:Object):TweenMax |
Cette méthode anime les attributs spécifiés de la ou les cibles pendant une certaine durée, vers les valeurs que l'on a spécifiées. Le premier paramètre que l'on doit passer est l'objet à animer ; il est également possible de passer un tableau d'objets pour en animer plusieurs en même temps. Le second spécifie la durée en secondes de l'animation. Enfin, on passe en troisième paramètre un objet basique (de classe Object
) de configuration de l'animation, qui contient notamment les attributs à animer et leurs valeurs finales.
Notez que vous n'êtes pas obligés de passer un objet d'affichage : tout objet est accepté. Par exemple, vous pouvez "animer" l'attribut pv
de votre objet Personnage
pour le faire varier pendant 10 secondes si cela vous chante.
L'objet de configuration
Le dernier paramètre est donc un objet anonyme (pas besoin de classe spécifique) qui va contenir les attributs à animer et leur valeur finale. Par exemple, si nous voulons animer l'opacité de notre carré pour qu'elle passe à 0.5 en 1 seconde :
1 2 3 | var parametrages:Object = new Object(); parametrages.alpha = 0.5; TweenMax.to(carre, 1, parametrages); |
Nous pouvons aussi écrire l'objet sous forme abrégée avec des accolades :
1 2 | var parametrages:Object = { alpha:0.5 }; TweenMax.to(carre, 1, parametrages); |
Ou en se passant de variable intermédiaire :
1 | TweenMax.to(carre, 1, { alpha:0.5 }); |
Animons notre carré
Maintenant que notre carré est prêt, nous pouvons l'animer au passage de la souris à l'aide de la méthode statique to
de la classe TweenMax
comme nous venons de le voir :
1 2 | // Anime l'opacité vers 50% TweenMax.to(carre, 1, { alpha:0.5 } ); |
Lorsque la souris sortira du carré, on animera son opacité vers sa valeur initiale de 1 (pleinement opaque) :
1 2 | // Anime l'opacité vers 100% TweenMax.to(carre, 1, { alpha:1 } ); |
Il ne nous reste plus qu'à placer ces lignes dans les écouteurs correspondants :
1 2 3 4 5 6 7 8 | function onOver(e:MouseEvent):void { TweenMax.to(carre, 1, { alpha:0.5 } ); } function onOut(e:MouseEvent):void { TweenMax.to(carre, 1, { alpha:1 } ); } |
Ainsi, notre carré va devenir à demi transparent au passage de la souris !
Animer plusieurs objets en même temps
Il est possible d'animer plusieurs objets en même temps en passant un tableau d'objet dans le paramètre cible. Ces objets seront animés de la même façon sur les mêmes attributs de manière synchronisée.
Il faut bien faire attention à ce que les attributs que l'on veut animer soient présents et accessibles sur tous les objets animés.
Par exemple, si nous disposions d'un deuxième carré, nous pourrions les animer de la même manière à l'aide d'un tableau :
1 2 3 4 5 6 | var cibles:Array = new Array(); // On ajoute les carrés à animer cibles.push(carre); cibles.push(carre2); // Anime l'opacité des objets contenus dans le tableau vers 50% TweenMax.to(cibles, 1, { alpha:0.5 } ); |
On peut, bien entendu, utiliser la forme abrégée des tableaux pour rendre le code plus facile à écrire et à lire :
1 2 | // Anime l'opacité des deux objets vers 50% TweenMax.to([carre, carre2], 1, { alpha:0.5 } ); |
Animation avec initialisation
La deuxième méthode statique très utile est TweenMax.fromTo
, elle permet d'initialiser les valeurs d'attributs avant l'animation. Voici sa signature :
1 | TweenMax.fromTo(cible:Object, duree:Number, initialisation:Object, parametres:Object):TweenMax |
Un nouveau paramètre est demandé après la durée de l'animation : il s'agit d'un autre objet anonyme contenant les attributs à initialiser sur l'objet cible au début de l'animation.
Par exemple, on peut initialiser les attributs scaleX
et scaleY
à 0 pour que l'objet soit minuscule, puis animer ces mêmes attributs vers 1 (taille normale) :
1 2 | // Les attribus scaleX et scaleY vont varier de 0 à 1 en une seconde TweenMax.fromTo(carre, 1, {scaleX:0, scaleY:0}, {scaleX:1, scaleY:1} ); |
Avec cette instruction, notre carré apparaîtra en grossissant progressivement.
Utilisation avancée de TweenMax
Les propriétés des animations
En plus des attributs de l'objet cible (ou des objets cibles), on peut ajouter quelques propriétés relatives à l'animation dans l'objet de paramétrage. Par exemple, on peut définir un nombre de répétition de l'animation à l'aide de l'attribut repeat
(en mettant -1, on répète l'animation infiniment) :
1 2 | // L'animation se répètera infiniment TweenMax.to(carre, 1, { alpha:0.5, repeat:-1 }); |
On peut également retarder le début de l'animation avec delay
(durée exprimée en secondes) :
1 2 | // L'animation se lancera dans 3 secondes TweenMax.to(carre, 1, { alpha:0.5, delay:3}); |
Bien sûr, il est possible de combiner plusieurs de ces paramètres :
1 2 | // L'animation se répètera infiniment avec 3 secondes de délai entre chaque répétition TweenMax.to(carre, 1, { alpha:0.5, repeat:-1, delay:3}); |
Il existe d'autres paramètres disponibles comme la documentation de la librairie le décrit (en anglais).
Utiliser les évènements
Certaines propriétés pouvant être spécifiées dans l'objet de configuration de l'animation sont des fonctions qui peuvent être appelées automatiquement par l'animation lors de certains événements. Nous allons voir les deux plus importantes.
Tout d'abord, la propriété onStart
qui est une fonction appelée lorsque l'animation commence :
1 2 3 4 5 | TweenMax.to(carre, 1, { x: 0, y: 0, onStart: debutAnimation}); function debutAnimation():void { trace("Début de l'animation"); } |
La fonction debutAnimation
sera appelée dès que l'animation commencera ou sera relancée, hors répétitions.
L'autre propriété est onComplete
, représentant une fonction appelée une fois l'animation terminée (après répétitions) :
1 2 3 4 5 | TweenMax.to(carre, 1, { x: 0, y: 0, onComplete: finAnimation}); function finAnimation():void { trace("Fin de l'animation"); } |
Valeurs relatives
Il est possible d'animer des attributs numériques de manière relative en passant l'opération d'incrémentation ou de décrémentation entre guillemets. Par exemple, si l'on veut animer la position horizontale du carré pour qu'il avance de 100 pixels, nous pouvons écrire ceci :
1 | TweenMax.to(carre, 1, { x:"+=100" } ); |
Ainsi, nous n'avons pas besoin de savoir quelle est la valeur initiale de sa position pour calculer la nouvelle, TweenMax se charge de le faire à notre place. Nous pouvons également réduire la position horizontale comme ceci :
1 | TweenMax.to(carre, 1, { x:"-=100" } ); |
Easing
On peut faire varier la vitesse de l'animation à l'aide de fonctions mathématiques, appelées fonctions de easing. L'animation suit alors cette fonction pour modifier les différentes valeurs successives. Ces fonctions peuvent intervenir en entrée d'animation, en sortie ou les deux. Par exemple, avec la fonction $x^2$ en début d'animation, la vitesse sera au départ nulle puis va augmenter petit à petit (suivant la fonction mathématique de la puissance deux).
Pour illustrer cette notion de fonction de easing, voici quatre exemples où l'on a dessiné les différentes positions d'un point animé. Dans le premier, nous utilison la fonction $x$, également appelée fonction linéaire.
Les différentes positions sont calculées avec la fonction linéaire, donc l'animation est linéaire.
Maintenant, nous utilisons la fonction $x^3$ (ou fonction cubique) en début d'animation :
Les différentes position sont calculées avec la fonction cubique en entrée ce qui donne un effet d'accélération.
Cette fois-ci, nous utilisons la fonction cubique en sortie :
Cela nous donne alors un effet de décélération.
Enfin, nous utilisons la fonction cubique en entrée et en sortie :
On a alors un effet d'accélération puis de décélération.
Pour utiliser ces différentes fonctions easing, nous devons spécifier la propriété ease
dans la configuration de l'animation, en utilisant comme valeur les attributs statiques des classes contenues dans le package com.greensock.easing
que nous avons ajouté en haut de notre classe :
1 2 3 | import com.greensock.*; // On inclut les différentes fonctions de easing grâce à l'étoile import com.greensock.easing.*; |
Dans ce package, une classe par fonction de easing est définie, avec une méthode statique correspondant à l'entrée, la sortie ou les deux, à passer dans la propriété ease
:
1 2 3 4 5 6 7 8 9 10 11 | var choixEasing:Function; // Animation linéaire choixEasing = Linear.easeNone; // Animation cubique en entrée choixEasing = Cubic.easeIn; // Animation cubique en sortie choixEasing = Cubic.easeOut; // Animation cubique en entrée et en sortie choixEasing = Cubic.easeInOut; // Animation avec le paramètre ease TweenMax.to(carre, 1, { x:"+=100", ease:choixEasing } ); |
Nous pouvons, bien sûr, abréger ce code en se passant de variable intermédiaire :
1 2 | // Animation linéaire TweenMax.to(carre, 1, { x:"+=100", ease:Linear.easeNone } ); |
Par défaut, la fonction de easing utilisée pour toutes les animations est la fonction x2 (ou fonction Carré) en sortie, c'est-à-dire Quad.easeOut
. Il y a donc une légère décélération par défaut.
Il est possible d'utiliser des fonctions de easing plus exotiques comme la fonction Bounce
qui fait rebondir les valeurs des attributs animés. Essayons cette fonction en sortie :
1 | TweenMax.fromTo(carre, 1, {scaleX:5, scaleY:5}, {scaleX:1, scaleY:1, ease:Bounce.easeOut} ); |
Ici, on anime la taille relative de notre carré de cinq fois plus grand à sa taille normale avec un effet de rebondissement intéressant.
Vous pouvez consulter la liste de toutes les fonctions de easing disponibles sur la documentation de la librairie.
Contrôle d'une animation
Lorsque nous utilisons les méthodes statiques de la classe TweenMax
pour animer des objets, nous pouvons récupérer une instance de cette classe représentant l'animation. En effet, les méthodes TweenMax.to
et TweenMax.fromTo
que nous avons vues retournent un objet de classe TweenMax
:
1 2 | // Animation de rotation de notre carré, affectée à une variable var animation:TweenMax = TweenMax.to(carre, 5, {rotation:360, repeat:-1, ease:Linear.easeNone}); |
A partir de là, nous pouvons accéder aux propriétés des instances de la classe TweenMax pour contrôler l'animation. Nous pouvons ainsi la mettre en pause à l'aide de sa méthode pause comme ceci :
1 | animation.pause(); |
Il est possible de reprendre la lecture de l'animation à l'aide de la méthode resume
:
1 | animation.resume(); |
La méthode restart
, quant à elle, permet de recommencer l'animation depuis le début :
1 | animation.restart(); |
Enfin, la méthode kill
permet d'arrêter complètement l'animation qui est alors supprimée :
1 | animation.kill(); |
Animer une animation
Vous souvenez-vous que les animations prenaient en paramètre n'importe quel objet ? Y compris d'autres animations ?
En effet, il est tout à fait possible d'animer des propriétés d'une animation à l'aide d'une nouvelle animation. L'attribut qui nous intéresse alors est le nombre flottant timeScale
, qui permet d'accélérer ou de ralentir une animation de manière arbitraire. A zéro, l'animation est en pause, à un, l'animation avance à la vitesse normale, à deux, elle se déroule deux fois plus vite, etc.
1 | animation.timeScale = 0.5; // On ralentit l'animation de moitié |
Ainsi, il nous est possible d'animer cet attribut à l'aide d'une nouvelle animation :
1 2 | // On anime l'attribut timeScale de notre animation de rotation TweenMax.fromTo(animation, 5, {timeScale:0}, {timeScale:1}); |
Grâce à cette instruction, l'animation de rotation de notre cube sera initialement en pause puis va s'accélérer jusqu'à atteindre la vitesse normale. Cela signifie que le carré va progressivement se mettre à tourner sur lui-même !
En résumé
- Animer un objet consiste à modifier au moins l'une de ses propriétés à intervalles réguliers.
- L'événement
Event.ENTER_FRAME
est généré avant que chaque image ne soit rendue. - La propriété
frameRate
de l'objetstage
permet de gérer le cadencement des images au sein de l'animation. - La classe
Timer
offre un moyen de contrôler le temps de manière précise, contrairement à l'événementEvent.ENTER_FRAME
. TweenMax
est une célèbre librairie d'interpolation qui permet de simplifier au maximum l'animation de vos objets.