Forums Développement Multimédia

Les formations Mediabox
Les formations Mediabox
Compatible ActionScript 3. Cliquer pour en savoir plus sur les compatibilités.Compatible Flash CS3. Cliquer pour en savoir plus sur les compatibilités.Par Nataly, le 25 janvier 2011

Variables, fonctions, syntaxe
Objets, méthodes, propriétés…
Clics et événements souris sur les boutons et les clips (ici)
Champs texte et objet String
Structures de contrôle alternatives : if
MovieClip : manipuler
• Structures de contrôle alternatives : switch
• Structures de contrôle répétitives : for - while
Liste d'affichage, imbrication et profondeur des objets
Liste d'affichage, gestion dynamique des objets
Les tableaux

MouseEvent / AddEventListener : Clics et événements souris sur les boutons et les clips

Bonjour à vous, ô intrépides voyageurs qui abordez l'archipel Flash et plus précisément l'île ActionScript3 :)

Puisque flash permet de fabriquer des contenus animés et interactifs, aussitôt l'étape 'contenus animés' franchie, on se confronte à l'interaction : comment réagir aux interventions de l'utilisateur ? Comment déclencher telle ou telle action quand il clique quelque part, survole un clip ou double clique ailleurs ?

L'objet de ce tuto est donc d'apprendre à associer des “commandes” aux boutons et aux clips.

Je considère que vous avez fait connaissance avec l'interface utilisateur, que vous savez construire des clips et des boutons, utiliser les outils de dessin, que les quelques propriétés et méthodes fondamentales vous sont familières (propriétés x, y… méthodes play() stop()), et guère plus.
Ça nous suffira pour ce qui va suivre :)



Ecouter les événements et y réagir

Il donc va s'agir d'être à l'écoute de l'utilisateur, savoir ce qu'il fabrique sur notre belle animation afin de réagir en fonction, non pas de l'utilisateur lui même, mais au moins de la souris qu'il balade partout.

Formulé ainsi, ça peut induire en erreur. En effet, il va s'agir d'être à l'écoute non pas de la souris elle même - qui mène sa vie - mais des différents éléments (objets) qui constituent l'animation. C'est eux qui vont nous prévenir de ce qu'il se passe, de ce qu'il arrive avec la souris. Ce sont les clips et les boutons qui vont nous dire : tiens le pointeur me survole, je viens de recevoir un clic, un double clic…
Les clips et les boutons sont de grands bavards, ils “disent” à longueur de temps ce qu'il leur arrive, seulement personne ne les écoute (comme moi :mrgreen:), ils crient dans le vide, les pauvres…
Mais rien ne nous empêche d'être attentifs, d'écouter ce qu'ils racontent et de préciser dans la foulée ce qu'il doit se passer quand tel ou tel événement se produit.

Et bien on y est.
En français j'ai tout dit : il s'agit d'écouter un événement et de préciser ce que nous voulons faire quand cet événement est déclenché (quand le bouton ou le clip crie “on m'a cliqué dessus” par exemple).

On dit des clips et des boutons qu'ils diffusent des événements (to dispatch events pour ceux qui préfèrent l'anglais)



addEventListener

Ce tout premier exemple va se contenter d'afficher quelques mots dans la fenêtre de sortie quand on clique sur un bouton.

Pour tester ce qui va suivre fabriquez un symbole bouton, glissez en une instance (occurrence) sur la scène, image 1, et nommez là (ici btDemo).



Si on exécute (teste) l'animation, le bouton réagit déjà visuellement au survol, au clic, quand on quitte le survol… mais rien d'autre ne se passe (remarquez, on aime autant, manquerait plus que ça qu'il se passe des choses sans notre accord !)
Pour reprendre une dernière fois la métaphore des boutons parlant, on sait maintenant qu'il se passe plus de choses que nous ne le voyons : non seulement le bouton réagit “visuellement”, mais en plus il crie dans le vide ce qu'il lui arrive, il diffuse des événements.

A nous d'écouter ce qu'il lui arrive et de préciser ce qui doit être fait.

Ecouter la diffusion des événements c'est ajouter (associer) au bouton considéré un écouteur d'événements.

On s'y attèle sans plus attendre.
Puisqu'on s'apprête à coder, autant faire propre du premier coup et ajouter un calque dédié (je le nomme code racine et je le verrouille au passage pour ne pas risquer d'ajouter des éléments graphiques par inadvertance - ce qui ne serait pas bien grave, mais je suis un peu maniaque)
Affichez le panneau Action, le calque code racine et à vos claviers !




On s'intéresse au bouton btDemo

btDemo



… on veut écouter ce qu'il diffuse, donc on lui ajoute un écouteur d'événements

btDemo.addEventListener


… et on précise quel événement on écoute.
Il existe de nombreux événements différents, ceux qui nous intéressent ici, ce sont les événements de type souris (MouseEvent) et plus précisément parmi eux l'événement Click :

btDemo.addEventListener(MouseEvent.CLICK


Pour finir, on indique ce qui doit être fait quand cet événement est diffusé (en vrai ce qu'on indique c'est le nom d'une fonction1)).

btDemo.addEventListener(MouseEvent.CLICK,qdClicBtDemo);


Voilà, c'est fait :)
En français : écouter les événements diffusés par btDemo, quand il s'agit d'un clic, exécuter la fonction qdClicBtDemo.

Dans le vocabulaire consacré, cette fonction s'appelle fonction de rappel ou encore fonction écouteur.

Enfin, c'est fait et c'est pas fait… Il faut encore écrire la fameuse fonction qdClicBtDemo



La fonction de rappel

C'est une fonction dite de rappel, elle doit respecter une signature précise (une règle d'emploi), à savoir : elle attend un paramètre. Et pas n'importe quel paramètre encore, un paramètre de type… de type ?…
… du type de l'événement à la suite duquel elle sera invoquée. A nous de prévoir à quel écouteur - donc événement - elle sera souscrite (associée). Dans le cas qui nous intéresse c'est un événement de type MouseEvent.

On va donc écrire :

function qdClicBtDemo(me:MouseEvent):void{
   //ici le code qui sera exécuté
   trace("j'ai cliqué");
}


Voilà, vous pouvez tester… On tient le bon bout !

Par exemple, pour ouvrir une page google :

btGoogle.addEventListener(MouseEvent.CLICK,qdClicBtGoogle);
 
 
function qdClicBtGoogle(me:MouseEvent):void {
	navigateToURL(new URLRequest("http://google.fr"));
}


Si vous connaissez les méthodes play, stop et gotoAndStop, vous pouvez d'ores et déjà vous faire une récrée avec quelque chose comme ça :
L"extension Adobe Flash Plugin est nécessaire pour afficher ce contenu.

Un skieur dévale la montagne (guides de mouvement) dans un clip (anim). C'est anim que les boutons pilotent…



Les événements MouseEvent

On vient d'écouter le clic, mais vous le savez, il existe nombre d'autres événements souris.
On dirait que j'étais pas là pour tout vous dire… Comment les retrouver ?
Tout bonnement avec la touche F1 après avoir posé le curseur sur le “mot” MouseEvent dans le code. Ça vous ouvre la doc à la page concernée, yapuka consulter la liste des constantes : MOUSE_DOWN, MOUSE_UP, MOUSE_MOVE, ROLL_OVER, DOUBLE_CLICK…

DOUBLE_CLICK


Et bien tiens par exemple, juste pour jouer puisqu'on l'a sous les yeux, un petit trace sur double-clic ?
Je vous laisse faire ? Je sais : trop facile… mais je veux pas brusquer non plus :mrgreen:

Comment ça, ça ne fonctionne pas ?
Ah, si… Je me disais bien aussi :)

Vous avez été attentif, vous avez lu les deux lignes de la doc et repéré que :

La doc:
La propriété doubleClickEnabled doit être true pour qu'un objet puisse générer l'événement doubleClick.


Vous avez donc pris soin de le faire, bravo ;)

btDemo.addEventListener(MouseEvent.DOUBLE_CLICK,qdClicBtDemo);
btDemo.doubleClickEnabled=true;
function qdClicBtDemo(me:MouseEvent):void {
	trace("Mais si ça marche !");
}



MOUSE_OVER vs ROLL_OVER


Tout ce qui a été dit jusque là est valable indifféremment pour les boutons (SimpleButon) ou les clips (MovieClip).
Maintenant il va convenir d'établir une nuance d'importance.
Je vous propose donc de fabriquer un symbole MovieClip qui va lui même en contenir d'autres. Partisane du moindre effort je me contenterai pour la démo d'une forme pour le fond et de deux instances d'un autre symbole (un malheureux rectangle, ça ira bien).


Ajoutez une instance de ce tout nouveau symbole sur la scène (image 1 toujours), et nommez cette instance cont (comme conteneur) dans le panneau des propriétés.

Selon le même principe que pour un bouton, on peut lui associer un écouteur et écouter n'importe quel événement MouseEvent.
Intéressons nous cette fois au survol. Deux constantes nous sont proposées : MOUSE_OVER et ROLL_OVER.
Plutôt qu'un long discours, jouons les St Thomas et livrons nous à nos expérimentations en testant l'une et l'autre des constantes.

ROLL_OVER


cont.addEventListener(MouseEvent.ROLL_OVER, survole);
function survole(me:MouseEvent):void {
	trace("dessus");
}


Vous constatez sans surprise que lorsque le pointeur arrive sur le clip, la fonction est invoquée (dessus “s'écrit” dans la fenêtre de sortie), et vous pouvez agiter le pointeur tant que vous voulez dans les limites du clip, rien d'autre ne se produit. Le survol a été reconnu, une fois, quand vous êtes “entrés dans le territoire du clip”, il ne sera à nouveau reconnu (et la fonction invoquée) que si vous (le pointeur ;)) en sortez préalablement.

Normal, ronchonne Ronchon pas épaté du tout, pourquoi tant de blablas ?

Parce qu'il en va différemment du MOUSE_OVER.


MOUSE_OVER

Vérifions :

cont.addEventListener(MouseEvent.MOUSE_OVER, survole);
function survole(me:MouseEvent):void {
	trace("dessus");
}


Et là, ce qui se produit, c'est que la fonction de rappel est invoquée quand on entre sur le clip (on étant toujours le pointeur), puis si on passe par dessus le rectangle vert (le clip enfant) la fonction est de nouveau invoquée et encore quand on survole à nouveau le fond rouge, puis encore si on passe sur le deuxième rectangle (clip)…
Hé oui : un événement MOUSE_OVER est diffusé chaque fois qu'un enfant du clip ou le clip lui même fait l'objet d'un survol.

La doc :
L'événement mouseOver est envoyé chaque fois que la souris pénètre dans la zone d'un objet enfant du conteneur d'objet d'affichage, même si la souris survolait déjà un autre objet enfant du conteneur d'objet d'affichage. Ce comportement est différent de celui de l'événement rollOver […]


Les boutons


En revanche pour les boutons il n'y a aucune différence entre MOUSE_OVER et ROLL_OVER (et là je vous laisse faire les tests tous seuls, zetes grands aussi ;))


En résumé :
C'est le ROLL_OVER qui est écouté dans la majorité des cas.
ROLL_OUT vs MOUSE_OUT
Pour écouter le moment où le curseur de la souris ne survole plus les objets, on utilise ROLL_OUT ou MOUSE_OUT.
Le principe est le même.



Associer un même écouteur à plusieurs objets

Intéressons nous maintenant à ce paramètre que les fonctions de rappel se doivent d'attendre.
C'est donc un objet de type MouseEvent (dans le cas qui nous intéresse) qui met à notre disposition quelques propriétés bien utiles, pour ne pas dire indispensables.

Commençons par la propriété target ainsi que sa cousine currentTarget. Toutes deux nous renvoient l'objet cible du pointeur, ou pour le dire autrement : l'objet qui a reçu le clic, par exemple.

Nous allons grâce à elles pouvoir coder plus malin et plus efficace, en associant une même fonction de rappel à un même évènement diffusé par des objets différents.


currentTarget

Observons sans plus attendre :

btDemo.addEventListener(MouseEvent.CLICK,qdClicBtDemo);
function qdClicBtDemo(me:MouseEvent):void {
	trace("currentTarget : "+me.currentTarget);
}
currentTarget : [object SimpleButton]

On a tracé la propriété currentTarget du paramètre, on voit qu'il s'agit d'un objet de type SimpleButton.

Si vous avez la curiosité de tester sur le clip qu'on a fabriqué à l'instant (rouge avec ses enfants vert, nommé cont) :

cont.addEventListener(MouseEvent.CLICK,qdClicBtDemo);
currentTarget : [object MovieClip]

me.currentTarget est alors un objet de type MovieClip. Jusque là tout va bien :)

En conséquence, puisque la propriété currentTarget nous renvoie un objet, on doit bien pouvoir lire ses propriétés…
Clips et boutons exposent une propriété name, ça on le sait, on peut donc vérifier. On va la tracer :

function qdClicBtDemo(me:MouseEvent):void {
	trace("currentTarget : "+me.currentTarget);
        trace("currentTarget.name : "+me.currentTarget.name);
}


• Pour le clip :

currentTarget : [object MovieClip]
currentTarget.name : cont


• Pour le bouton :

currentTarget : [object SimpleButton]
currentTarget.name : btDemo



target

Continuons sur les sentes de l'empirisme en nous attaquant maintenant à target.

function qdClicBtDemo(me:MouseEvent):void {
	trace("currentTarget : "+me.currentTarget);
        trace("currentTarget.name : "+me.currentTarget.name);
	// target -----------
	trace("target : "+me.target);
        trace("target.name : "+me.target.name);
}

Vous pouvez dès maintenant constater que si l'écouteur est associé à un bouton, target et currentTarget renvoient la même chose : le bouton.

En revanche si vous écoutez le clip, currentTarget renvoie toujours le clip écouté, alors que target renvoie précisément l'objet qui a reçu le clic, donc soit le clip lui même (clic ds la forme) soit l'un des clips qu'il contient.

// ici j'ai cliqué sur le fond rouge du clip, lui même
currentTarget : [object MovieClip]
currentTarget.name : cont
target : [object MovieClip]
target.name : cont
 
// ici j'ai cliqué sur un rectangle (clip enfant)
currentTarget : [object MovieClip]
currentTarget.name : cont
target : [object MovieClip]
// c'est une instance que je n'ai pas nommée elle l'est automatiquement
target.name : instance2



Couplage fort ou faible

La question qui pourrait vous venir c'est “mais quel intérêt de chercher à récupérer la cible puisqu'on la connait ?”.
En effet quand on écrit, comme tout à l'heure :

btGoogle.addEventListener(MouseEvent.CLICK,qdClicBtGoogle);
 
 
function qdClicBtGoogle(me:MouseEvent):void {
	navigateToURL(new URLRequest("http://google.fr"));
}

On sait bien que c'est le bouton btGoogle qui a été cliqué, puisque c'est ce qu'on a prévu.

Certes, prenons alors un autre cas de figure : on veut faire un jeu de puces, quand on clique sur un clip il se déplace - mettons de 10 pixels sur la droite (bon on débute hein ! On fait simple avec le peu de vocabulaire que je suppose à notre disposition).
On fabrique donc un clip minimaliste avec une forme, on en glisse une instance sur la scène, on la nomme puce. Puis on associe un écouteur sur clic et on écrit la fonction de rappel :

puce.addEventListener(MouseEvent.CLICK, avanceQdClic);
function avanceQdClic(me:MouseEvent):void{
       puce.x+=10;
       // identique à puce.x= puce.x+10
}

D'accord ça marche, mais vous verrez à l'usure que ce n'est vraiment pas un argument pour juger de la qualité d'un parti-pris de programmation.

Imaginons maintenant qu'on souhaite une autre puce.
On va donc fabriquer un autre clip et en poser une instance sur la scène, qu'on nommera puce2 (si vous avez la flemme une deuxième instance du même clip fera l'affaire tout pareil).
Parfait, et maintenant ? On recommence ? On ajoute un écouteur et on écrit une deuxième fonction de rappel ?
Et si on veut vingt puces, vingt fonctions de rappel ?
Et quand on change d'idée et qu'on veut un déplacement de 9 pixels plutôt que 10, on modifie les vingt fonctions ??!
Bien sûr que non :)
Ce qu'on veut - en français - c'est que le clip auquel on a associé l'écouteur (la puce) se déplace. Or ce clip… on le connait : c'est me.currentTarget2).

puce.addEventListener(MouseEvent.CLICK, avanceQdClic);
puce2.addEventListener(MouseEvent.CLICK, avanceQdClic);
 
function avanceQdClic(me:MouseEvent):void{
       me.currentTarget.x+=10;
}

Faire référence à l'objet renvoyé par la propriété target ou currentTarget, c'est utiliser un couplage faible. A l'inverse quand on spécifie précisément un objet dans la fonction de rappel, on dit qu'il y a couplage fort.

Privilégiez autant que faire se peut le couplage faible.

En exploitant la propriété currentTarget de l'évènement, nous avons donc réussi à écrire une seule fonction de rappel, commune à toutes les puces. Mais nous devons encore ajouter l'écouteur à chaque puce. Pour 5 puces cela donnerait:

puce.addEventListener(MouseEvent.CLICK, avanceQdClic);
puce2.addEventListener(MouseEvent.CLICK, avanceQdClic);
puce3.addEventListener(MouseEvent.CLICK, avanceQdClic);
puce4.addEventListener(MouseEvent.CLICK, avanceQdClic);
puce5.addEventListener(MouseEvent.CLICK, avanceQdClic);

Nous pourrions nous économiser ces lignes en mettant toutes les puces dans un même clip nommé puces, et en écoutant l'évènement pour ce conteneur:

puces.addEventListener(MouseEvent.CLICK, avanceQdClic);

Mais dans ce cas la fonction de rappel ne devrait pas faire avancer l'objet pour lequel nous écoutons l'évènement (currentTarget), mais bien faire avancer l'objet qui a reçu le clip (target). Elle deviendrait:

function avanceQdClic(me:MouseEvent):void{
       me.target.x+=10;
}

Ce qui règle notre problème en 4 lignes, quelque soit le nombre de puces, mais pose comme contrainte que les puces ne peuvent pas contenir elles-même de sous-clips. En fait elles peuvent contenir des clips mais il faudrait que les enfant ne soient pas reconnus comme tels. La solution c'est la propriété mouseChildren. Valorisée à faux, elle invalide la diffusion des événements par les enfants du clip considéré.

La doc :
Détermine si les enfants de l'objet prennent ou non en charge la souris.


Rapporté à notre exemple, il suffira d'écrire :

mouseChildren=false;

dans l'image 1 du symbole Mv_Puce ;)


En utilisant target et currentTarget nous avons sans le dire fait appel à ce qui s'appelle le mécanisme de propagation des évènements. Nous en apprendrons d'avantage à ce sujet dans le chapitre dédié.



Transmettre des variables à la fonction de rappel

On se résume :
On peut associer un même écouteur à plusieurs objets
On peut récupérer l'objet auquel est associé un écouteur et en lire les propriétés.

Mézalors… On pourrait peut-être en profiter…

Imaginons qu'on souhaite “piloter” la tête de lecture de l'animation courante, l'envoyer image 1 ou 10, voire étiquette “accueil”, “contact”, “page1”…

Allez, tiens ! On le fait :)
L'idée ici c'est d'écrire une seule fonction de rappel sur clic pour trois clips, l'un envoyant la tête de lecture image “accueil”, l'autre image “contact” le dernier image “page1”, en ne perdant pas de vue que je considère qu'on ne sait même pas encore coder un if.

Pour vous y essayer posez une étiquette sur trois images de votre choix. Griffonnez un visuel identifiable sur l'image correspondante, et pensez à mettre un stop image 1 (tant qu'à faire je nomme “accueil” l'image 1).



Fabriquez un clip minimaliste qui servira de bouton - Oui Ronchon, j'ai bien dit un clip - Glissez trois instances du clip et nommez les, chez moi btAccueil, btContact, btPage1

Ah non s'insurge le toujours même, ravi d'éventer le piège, et soucieux de ne pas avoir à tout renommer : on baptise les boutons comme les images et il n'y plus qu'à écrire une bête fonction de rappel qui utilise la propriété name dans un gotoAndStop(…) !

Comme suit :

accueil.addEventListener(MouseEvent.CLICK, allerA);
contact.addEventListener(MouseEvent.CLICK, allerA);
page1.addEventListener(MouseEvent.CLICK, allerA);
//
function allerA(me:MouseEvent):void {
	gotoAndStop(me.currentTarget.name);
}


Ah oui, c'est une idée :)
Même si ça n'arrange pas ma démo, c'est malin d'avoir pensé à détourner la propriété name pour y stocker une chaine utilisable dans la fonction. Mais ça reste une ruse qui a ses limites.
Imaginons par exemple (je remballe mon exemple au profit d'un autre) qu'on veuille que ces trois boutons ouvrent chacun une page web3), disons google, mediabox, et la doc.

L"extension Adobe Flash Plugin est nécessaire pour afficher ce contenu.

Impossible de tricher en détournant la propriété name pour y stocker une adresse URL (elle n'aime pas les slash).

Et bien au même titre qu'on peut lire une propriété native (name) on peut lire n'importe quelle autre propriété qu'on aurait “fabriquée” nous même.
Décidons donc d'une propriété lien :

btGoogle.lien="http://google.fr";
btMediaBox.lien="http://flash.mediabox.fr";
btDoc.lien="http://help.adobe.com/fr_FR/ActionScript/3.0_ProgrammingAS3/";
 
btGoogle.addEventListener(MouseEvent.CLICK, qdClicBouton);
btMediaBox.addEventListener(MouseEvent.CLICK, qdClicBouton);
btDoc.addEventListener(MouseEvent.CLICK, qdClicBouton);
 
function qdClicBouton(me:MouseEvent):void {
   // lire la propriété crée dynamiquement
   navigateToURL(new URLRequest(me.target.lien));
}

Classes dynamiques ou statiques


Evidemment, au fond ça s'agite. Ça ne marche pas parait-il. On a une erreur :

1119: Accès à la propriété lien peut-être non définie, via la référence de type static flash.display:SimpleButton.


SimpleButton !
J'avais demandé de s'entrainer avec des clips, forcé il fallu que Ronchon utilise des instances d'un bouton précédemment fait… Et voilà, normal, ça crie.
Il se trouve que les boutons n'acceptent pas qu'on leur ajoute des propriétés (variables, c'est pareil), c'est le propre de la classe MovieClip (et de quelques autres).

On peut ajouter des propriétés “à la volée” à un MovieClip parce que c'est une classe dite dynamique.

Voilà ce qu'en dit la doc :
Une classe dynamique définit un objet qui peut être modifié lors de l'exécution en ajoutant ou en modifiant des propriétés et des méthodes. Une classe qui n'est pas dynamique (la classe String, par exemple), est une classe scellée. Vous ne pouvez pas ajouter de propriétés ni de méthodes à une classe scellée lors de l'exécution.


Pour savoir si une classe est dynamique ou non, il suffit de consulter la doc (comme d'hab) :





Le beurre et l'argent du beurre : buttonMode


Oui mais voilà, ça ne lui convient pas à notre Ronchon national : les boutons c'est rudement pratique : on dessine trois visuels, pas une ligne de code et hop il y a un effet de survol et de clic. Franchement un clip c'est moins bien. Il se doute bien qu'on peut programmer l'interaction, mais rien qu'y penser ça le déprime.

Et bien non, rien à coder du tout ou presque, pour avoir un clip qui se comporte comme un bouton. Il suffit de respecter quelques contraintes de construction : dessiner les trois états sur trois images différentes et nommer (étiquette) chacune de ces images.
_up : visuel “normal” (image Haut d'un bouton)
_over : visuel de survol (Dessus)
_down : visuel quand souris enfoncée (Abaissé)



Pensez à ajouter un stop() image 1, et voilà ! Les instances de ce clip sont prêtes à être utilisées comme des boutons, pour peu qu'on le précise à l'aide le propriété buttonMode.
Valorisée à true, cette propriété permet qu'un clip, seulement équipé des étiquettes idoines se comporte comme un bouton.
On peut l'écrire avec le reste du code :

// on précise le comportement des clips/boutons
btGoogle.buttonMode=true;
btMediaBox.buttonMode=true;
btDoc.buttonMode=true;
 
 
// la suite rien ne change
btGoogle.lien="http://google.fr";
btMediaBox.lien="http://flash.mediabox.fr";
btDoc.lien="http://help.adobe.com/fr_FR/ActionScript/3.0_ProgrammingAS3/";
 
btGoogle.addEventListener(MouseEvent.CLICK, qdClicBouton);
btMediaBox.addEventListener(MouseEvent.CLICK, qdClicBouton);
btDoc.addEventListener(MouseEvent.CLICK, qdClicBouton);
 
function qdClicBouton(me:MouseEvent):void {
   // lire la propriété crée dynamiquement
   navigateToURL(new URLRequest(me.target.lien));
}

On peut aussi valoriser cette propriété directement “dans le clip”. Après tout si on a pris soin de poser les étiquettes d'état (_up, _down…) c'est bien pour faire de ses instances de vrai-faux boutons.



Autres propriétés du paramètre de la fonction de rappel

En ce qui concerne les deux propriétés target et currentTarget cette fois tout est dit, mais il y en a d'autres - des propriétés - bien intéressantes. Vous trouverez la liste, comme d'habitude dans la doc, ici celles du clic.
Un autre moyen de les retrouver - et d'en voir la valeur à l'instant T - c'est de tracer l'événement lui même :

leClip.addEventListener(MouseEvent.CLICK, QdClic);
function QdClic(me:MouseEvent):void{
       trace(me);
}


[MouseEvent type="click" bubbles=true cancelable=false eventPhase=2 localX=-2 localY=-5 stageX=228 stageY=34
relatedObject=null ctrlKey=false altKey=false shiftKey=false buttonDown=false delta=0]


Voici qui nous confirme qu'il s'agit d'un événement MouseEvent de type “click”, et nous donne un certain nombre d'informations (propriétés) supplémentaires :

Attention, ceci n'est qu'à titre d'information.
En fait la propriété toString est appliquée par défaut quand on passe un objet à la fonction trace.

Examinons maintenant certaines de ces propriétés supplémentaires.



Clavier et position de la souris

Etat du clavier: ctrlKey, shiftKey, altKey

Dans notre trace(me) précédent nous lisions par exemple:

ctrlKey=false

Ceci signifie que la touche contrôle n'était pas enfoncée au moment du click.

Selon le même principe, si vous vouliez tester l'état de la touche majuscule, il faudrait utiliser la propriété shiftKey :

trace("Majuscule enfoncée : "+me.shiftKey);



Allez ! Pour jouer histoire de s'y essayer : la consigne est simple, les pétales disparaissent si on clique dessus avec la touche majuscule enfoncée.

Vous imaginez bien que je ne me suis pas amusée à ajouter un écouteur à chaque pétale !
Il y a - en tout - trois lignes ;)

L"extension Adobe Flash Plugin est nécessaire pour afficher ce contenu.

[mode auto-promo] Pour faire une marguerite en deux coups de cuillère à pot

Alors vous avez trouvé la ruse pour ne pas écrire autant d'écouteurs que de pétales ? Si je vous dit target/currentTarget… ça vous rappelle quelque chose ?

Hé oui, j'ai mis tous les pétales dans un même clip (ici nommé fleur) et utilisé target.

fleur.addEventListener(MouseEvent.CLICK,sup);
 
function sup(me:MouseEvent) {
	me.target.visible=! me.shiftKey;
}



Position du curseur de la souris: localX et localY

Prenez garde : la doc peut induire en erreur :

localX : Coordonnée horizontale à laquelle l'événement s'est produit par rapport au sprite conteneur.



localX et localY renvoient la position x du pointeur sur l'objet écouté. Pour vous en convaincre vous pouvez tracer ces deux propriétés : ici un clip nommé bille dans un clip nommé carre. Les coordonnées 0/0 de carre sont en haut à gauche, bille est centré.

carre.bille.addEventListener(MouseEvent.CLICK,marche);
 
function marche(me:MouseEvent):void {
	trace("localX : "+me.localX);
	trace("localY : "+me.localY);
}


L"extension Adobe Flash Plugin est nécessaire pour afficher ce contenu.



La propagation événementielle

eventPhase


Le nom de la plupart des propriétés du paramètre de la fonction de rappel est suffisamment parlant pour que je vous en épargne la description, en revanche eventPhase mérite qu'on en dise deux mots.

Et tout d'abord il va s'agir d'être précis du vocabulaire ou plutôt de ce qu'il recouvre. Depuis le début de ce tuto, je parle d'objets qui diffusent des événements, ce qui pourrait laisser imaginer que les événements sont émis par les objets qu'on écoute, qu'ils partent d'eux, voyagent depuis les objets (tout comme la lumière sort du projecteur qui la diffuse, ou le son du haut-parleur qui diffuse la musique)… Il n'en est rien :

Quand un événement est diffusé le lecteur Flash le propage depuis la scène (Stage) jusqu’à l’objet qui en a déclenché la diffusion, puis l'événement remonte vers la scène.

On isole trois phases dans le flux événementiel : d'abord l'événement descend depuis la scène, c'est la phase de capture4). Arrivé à l'objet considéré on est en phase cible5), puis l'événement remonte, tel une bulle de champagne, jusqu'en haut. C'est la phase bubbling6).

Sur ce coup là un bon schéma sera plus parlant qu'un long discours.

Faisons simple : sur le scénario principal on imagine un clip (clipA), et dans ce clip un autre (clipB). L'idée c'est de cliquer sur l'un ou l'autre, de regarder le cheminement de l'événement afin de “voir ce qu'on entend” selon qu'on écoute et comment on l'écoute : en phase de capture ou non.
La phase de capture, c'est la 1, et il faut explicitement l'écouter. Par défaut on écoute le reste, donc la 2 ou la 3.

L"extension Adobe Flash Plugin est nécessaire pour afficher ce contenu.
vous pouvez reproduire la chose avec une bête fonction test :

function test(me:MouseEvent) {	
	trace(me.eventPhase);
}


Et puisqu'on n'arrête pas le progrès je vous propose une démonstration, commentaires et explications en vidéo dans le blog Annexe




Lilive s'est aussi attelé7) à une représentation du flux événementiel, en voici donc une illustration animée.
Posez un écouteur où vous voulez et cliquez sur l'un des clips contenus ou lui même . Vous pourez observer le “trajet” de l'événement et lire la valeur de eventPhase dans la zone de sortie reproduite.

L"extension Adobe Flash Plugin est nécessaire pour afficher ce contenu.

(Pour archive et pour les curieux, voici les sources de l'animation qui illustre la propagation: propagation-cs4.zip)

A retenir

Associer un écouteur à un objet, c'est écouter, non pas ce que cet objet crie, comme je l'ai abusivement dit jusqu'à maintenant, mais c'est écouter tout ce qui est audible depuis cet objet.

Par défaut on intercepte tous les événements du flux qui ne sont pas en phase capture (descendante).

• Intercepter la phase de capture d'un objet c'est n'intercepter que les événements diffusés par les enfants.



Phase de capture

On peut donc choisir d'écouter les événements en phase descendante, dite de capture.
Il faut alors passer à l'écouteur ce fameux troisième paramètre, en le valorisant à true.


Evidemment eventPhase vaut toujours 1, c'est le principe : on écoute uniquement la phase de capture.
Ce qui est caractéristique, c'est que la phase de capture s'arrêtant au parent direct de l'objet auquel on a associé un écouteur, le clic sur lui même n'est pas intercepté. On n'entendra que les événements diffusés par ses enfants.

Quel intérêt ?

Reprenons, pour illustrer, le menu de navigation bricolé au chapitre précédent.
Somme toute, c'est la même fonction de rappel qui est invoquée pour chacun des boutons. Plutôt qu'ajouter un écouteur par bouton, il est plus judicieux d'associer un écouteur au clip menu, et d'intercepter la phase de capture.

// autant de boutons qu'on veut
menu.btGoogle.lien="http://google.fr";
menu.btMediaBox.lien="http://flash.mediabox.fr";
menu.btDoc.lien="http://help.adobe.com/fr_FR/ActionScript/3.0_ProgrammingAS3/";
 
// un seul écouteur
menu.addEventListener(MouseEvent.CLICK,qdClicBouton,true);
// une seule fonction de rappel
function qdClicBouton(me:MouseEvent):void {
	navigateToURL(new URLRequest(me.target.lien));
}





Et voilà ! :D
Ajouter un bouton de navigation au menu c'est seulement - du point de vue du code - ajouter une ligne pour renseigner la propriété lien du-dit bouton. Pas d'écouteur à ajouter, rien à modifier à la fonction de rappel.

La touche finale :
Si vous construisez les clips/boutons de façon un peu sophistiquée, en y ajoutant d'autres clips, vous allez vous heurter à un problème : en effet target renvoie l'enfant (de menu) qui a reçu le clic… Du coup, vous risquez une erreur quand il s'agit d'un des clips enfants des “boutons”.
La solution c'est de valoriser la propriété mouseChildren du clip/bouton à false :

Dans notre exemple, image 1 du clip/bouton :

stop();
buttonMode=true;
mouseChildren=false;



C'est bien joli, mais ça sert à quoi tout ça ?



EventPhase


Pour une fois où ce n'est pas Ronchon qui ronchonne, rendons à Lilive ce qui appartient à lui même :mrgreen:

Les clips imbriqués, et l'idée qu'on peut avoir envie de détecter le survol sur un clip qui en contient d'autre. Si je fais mon naïf je dis “mais pourquoi?”
Ma réponse de pas naïf serait: “Ben par exemple tu as un clip animé, pour faire ton animation tu as imbriqué des clips dedans. Disons tu as fait un bonhomme, il a les bras qui bougent, les yeux qui roulent, mais ce que tu veux savoir c'est quand on survole le bonhomme.”

Un autre exemple, à partir de ce qu'on a fait :
Imaginons qu'on souhaite que notre menu de navigation soit une palette déplaçable (glissé/lâché). Il faudrait alors faire la différence entre (vite dit) je clique sur le fond du menu dans l'idée de le déplacer : eventPhase=2, et je clique sur un bouton pour ouvrir une page : eventPhase=3.


Argument capture


J'ai récemment croisé sur le forum, une question sur la fabrication d'un clavier.
Dans un élan valeureux, l'auteur de la question avait avancé le travail en associant un écouteur à chaque touche, et une fonction de rappel par écouteur. Si vous considérez ne serait-ce que 26 boutons pour les 26 lettres de l'alphabet, et que faute de savoir qu'il existe une propriété shiftKey, vous compliquez le travail en écoutant aussi le clavier, vous voilà avec 27 écouteurs, autant de fonctions de rappel, chacune avec son if, là où vous pourriez n'en avoir qu'une pour un seul écouteur.

Ça motive non ?! ;)

Et vous trouverez là, avec l'alibi d'un coloriage, un autre exemple où on est fichtrement content qu'elle existe cette fichue phase de capture…

A vous de jouer


Vous vous souvenez de la puce de tout à l'heure ?
La pastille qu'on faisait avancer en cliquant dessus… Maintenant que vous savez tout des événements souris, vous pouvez commencer à faire de la puce sophistiquée, de la puce qui avance dans le sens où on la pousse et d'autant plus loin qu'on clique loin de son centre.
Comme celle-ci - en oubliant d'en restreindre les déplacements, puisque “on dirait qu'on ne connaissait pas les structures de contrôle” (et encore moins les Tween auxquelles je n'ai pas pu résister pour ramener la puce quand elle touche les bords).

L"extension Adobe Flash Plugin est nécessaire pour afficher ce contenu.






puce.addEventListener(MouseEvent.CLICK,saute);
 
function saute(me:MouseEvent):void {
	// se déplace de deux fois la distance du clic à son centre
	me.currentTarget.x-=me.localX*2;
	me.currentTarget.y-=me.localY*2;
}



Pour aller plus loin

Ici, je me suis attachée à décrire les écouteurs d'“événement souris”, mais tout ce que vous avez compris, en terme de diffusion, d'écoute, de couplage, etc… est valable pour tous les types d'événements. Vous en rencontrerez pléthore, qu'il s'agisse d'événements diffusés quand on charge des fichiers (classes URLLoader, Loader), quand on sollicite le clavier (classe Keyboard), qu'il s'agisse d'événements récurrents générés par la classe Timer ou du fameux EnterFrame… Le principe est toujours le même.

Vous voici donc parés, maintenant équipés d'un des prérequis à la plupart des tutos de ce wiki. Et puisque vous êtes arrivés ici préoccupés d'interaction, l'étape suivante sera probablement pour vous la gestion du clavier. Ça tombe bien : BillyBen s'en est occupé ! \o/.

Je vous laisse donc en sa compagnie ;)

1) Une fonction c'est un moyen de regrouper sous un même nom un ensemble d'instructions
2) me étant le nom que vous avez donné au paramètre, ça pourrait être un autre nom de votre choix
3) comme on l'a fait avec le premier exemple du “bouton Google”
4) et eventPhase vaut 1
5) eventPhase vaut 2
6) eventPhase vaut 3
7) qu'il soit cent fois remercié