• 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.
Ç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 ), 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 :
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
…
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 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.
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 )
C'est le ROLL_OVER qui est écouté dans la majorité des cas.
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.
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é.
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
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.
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 :
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).
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 :
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.
Il y a - en tout - trois lignes

[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 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); }
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.
vous pouvez reproduire la chose avec une bête fonction test :
function test(me:MouseEvent) { trace(me.eventPhase); }
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.
(Pour archive et pour les curieux, voici les sources de l'animation qui illustre la propagation: propagation-cs4.zip)
A retenir
• 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à !
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.
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
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).
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
me
étant le nom que vous avez donné au paramètre, ça pourrait être un autre nom de votre choix