Introduction
Classe de base (#1)
Classe de base (#2)
Héritage et surcharge (#1)
Héritage et surcharge (#2)
Diffuser des événements
Classe liée
Classe de document
Classe externe : une visionneuse (#1)
Classe externe : une visionneuse (#2)
Classe externe : méthodes statiques
Visionneuse (#2) Finitions
le sens de défilement
Changer le sens de défilement, ce n'est pas sorcier : il suffit de décrémenter idxSup (idxSup–). Il y a différente façon de s'y prendre, toutes passeront par un test sur une variable précisant le sens… Les moins aguerris auront peut être l'idée de valoriser une variable sens avec une chaine (genre avant ou arrière) et d'écrire dans un if soit idxSup++, soit idxSup–. Pas mal, mais peut mieux faire. On va décider d'une propriété qui vaudra 1 ou -1, ainsi la seule ligne idxSup+=laPropSens; permettra-t-elle d'incrémenter ou de décrémenter selon la valeur de la proriété.
Il faut aussi faire attention à ce qui se passe quand tous les visuels ont défilé. Il faut que idxSup vaille 1 quand on avance et le dernier numéro d'index quand on recule. Ça peut s'écrire comme ça :
idxSup+=_sens; if (_sens==1) { idxSup=idxSup>NumPhotoMax?1:idxSup; } else { idxSup=idxSup<1?NumPhotoMax:idxSup; } idxInf=idxTmp;
Si les opérateurs ternaires ne vous plaisent pas, ne vous privez pas de les remplacer par la structure de contrôle if…else
Tant qu'à faire, faisons en sorte de pouvoir préciser le sens à l'initialisation.
Un paramètre supplémentaire et facultatif dans le constructeur fera l'affaire. Je vous laisse écrire un couple d'accesseur pour la propriété sens.
// [...] private var _sens:int=1; // Le constructeur, un sens par défaut public function VisioPazapa(pTabVisuels:Array, pMasque:MovieClip,pSens:int=1):void { NumPhotoMax=pTabVisuels.length-1; tabVisuels=pTabVisuels; // _sens=pSens; // index image sup selon sens idxSup= pSens==1?1:NumPhotoMax; // [...] // Les accesseurs public function set sens(pSens:int):void { _sens=pSens; } public function get sens():int { return _sens; } // [...]
C'est parfait aussi longtemps qu'on le teste à l'initialisation, mais si nous vient l'idée d'installer sur le fla deux boutons pour changer le sens en cours de route :
… il y a un décalage, la commande n'est pas prise en compte tout de suite.
Je m'explique : l'enchainement des images sur la démo publiée c'est cabane, château, chemin. Si on modifie la propriété sens (à -1) pendant que la transition s'effectue de château à chemin, on aimerait voir château dès la transition suivante, or il faut attendre un tour de plus. C'est cabane qui s'affiche, ensuite seulement le défilement se fera à reculons.
En effet, c'est bien gentil de modifier _sens via l'accesseur set, mais les valeurs de idxSup et idxInf ne seront modifiées qu'à la sortie de la fonction chargeVisuels, d'où le décalage. Il faut donc intervenir dans le setter afin de modifier certes la variable sens, mais aussi idxSup et idxInf.
public function set sens(pSens:int):void { var supTemp:int=idxInf+pSens; _sens=pSens; if (_sens==1) { idxSup=supTemp>NumPhotoMax?1:supTemp; } else { idxSup=supTemp<1?NumPhotoMax:supTemp; } }
Temps de pause
Plus rigolo : le temps d'exposition du visuel avant enchainement, ce que j'appelle temps de pause.
Tel qu'écrit, la transition se fait dès que la lecture du masque revient image 1. Si on veut un temps de pause il va s'agir, arrivé à la dernière image, d'arrêter la lecture et d'attendre le temps précisé avant de la relancer (la lecture).
Arrêter c'est stop() (wo ! la découverte )
Attendre avant d'invoquer une fonction il nous faut un objet Timer (petit rappel ici)
Quant à la fonction invoquée le délai passé, on l'a déjà c'est marche.
L'objet Timer va être déclaré en variable globale et privée
import flash.utils.Timer; import flash.events.TimerEvent;
private var _tpsPause:int=2000; private var tmPause:Timer=new Timer(_tpsPause,1);
On a anticipé bien sûr une variable globale privée (_tpsPause
) pour le temps de pause, toute prête à être modifiée via accesseurs tpsPause
.
Dans le constructeur on va ajouter un écouteur à tmPause
et lui souscrire la fonction marche
.
tmPause.addEventListener(TimerEvent.TIMER,marche);
Voilà, il n'y aura plus qu'à appliquer la méthode start
à tmPause
pour remettre la visionneuse en route après un délai de deux secondes. On va le faire dans une fonction attendre :
private function attendre() { masque.stop(); trace("j'attends"); tmPause.reset(); tmPause.start(); }
Cette toute bête fonction attendre
, il ne nous reste plus qu'à l'appeler sur la dernière image du masque. Dit autrement : il faut ajouter du script sur la dernière image de masque…
Et hop ! On re-dégaine la fameuse addFrameScript.
public function VisioPazapa(pTabVisuels:Array, pMasque:MovieClip,pSens:int=1):void { trace("constructeur visio : " + pTabVisuels + "\nmasque : "+pMasque); tmPause.addEventListener(TimerEvent.TIMER,marche); NumPhotoMax=pTabVisuels.length-1; tabVisuels=pTabVisuels; _sens=pSens; idxSup=pSens==1?1:NumPhotoMax; masque=pMasque; masque.filters=[new BlurFilter(10,10,BitmapFilterQuality.HIGH)]; masque.gotoAndStop(2); // ajouter du code première et dernière image de masque masque.addFrameScript(0,chargeVisuels); masque.addFrameScript(masque.totalFrames-1,attendre); addChild(masque); masque.gotoAndStop(1); }
C'est un peu crétin de forcer le temps de pause sur 2 secondes, je vous laisse donc créer une propriété tpsPause d'un couple d'accesseurs :
private var _tpsPause:int=2000; //[...] public function set tpsPause(pTpsPause:int):void { _tpsPause=pTpsPause; tmPause.delay=_tpsPause; } public function get tpsPause():int { return _tpsPause; }
Depuis le .fla on peut tester nos dernières sophistications.
// .fla function nouvelleVisionneuse() { // nouvelle instance initialisée avec le tableau et le masque visio=new VisioPazapa(tabPhotos,leMasque,-1); visio.addEventListener(MouseEvent.CLICK,marcheArret); // fixer le temps de pause visio.tpsPause=5000; addChild(visio); }//
En testant en l'état on constate que la méthode arrêt semble ne pas toujours fonctionner : si on arrête la lecture en cliquant pendant le temps de pause, et bien… la lecture s'enchaine malgré tout.
Rien de plus normal : sur la dernière image tmPause est lancé, si on veut l'empêcher de faire son boulot et d'appeler la méthode marche après le délai, il faut l'arrêter :
public function arret() { trace("arret"); tmPause.stop(); masque.stop(); }
AddFrameScript deux trois choses à savoir
On l'a dit et répété, la méthode addFrameScript n'est pas documentée, ce qui lui vaut mauvaise presse : est-ce bien sérieux ? Ne risque-t-elle pas de disparaître un jour sans préavis ?
Je n'en sais rien : j'ai bien des qualités (en tout premier lieu la modestie) mais je ne suis pas médium… En revanche je suis butée. J'ai donc pris le temps d'observer son comportement, et voici les conclusions surprenantes auxquelles je suis arrivée. Allez y jeter un coup d'œil avant de l'utiliser à nouveau
Diffuser un événement "image suivante"
Si on veut pouvoir l'utiliser avec suffisamment de souplesse cette visionneuse, il serait bon qu'elle diffuse pour le moins un événement au chargement de chaque nouvelle image, ne serait-ce que pour pouvoir adjoindre des commentaires aux photos, par exemple…
Du côté du .fla il faut :
• Un champ texte dynamique sur le scenario, et un tableau pour stocker les chaines de commentaires.
var tabLegendes:Array=new Array("Cabane","Chateau","Chemin");
• Ecouter l'événement qu'on se propose d'ajouter à la classe pour écrire dans le champ texte.
function nouvelleVisionneuse() { var leMasque:Mv_Masque0=new Mv_Masque0(); visio=new VisioPazapa(tabPhotos,leMasque); visio.addEventListener(MouseEvent.CLICK,marcheArret); // --- > Ecouter l(e futur) événement SUIVANT visio.addEventListener(VisioPazapa.SUIVANT,imgSuivante); visio.tpsPause=2000; // ajouter à la liste d'afichage addChildAt(visio,0); } // la fonction de rappel function imgSuivante(e:Event) { txtLegende.text=tabLegendes[visio.visuelCourant-1]; }
Du côté de la classe
Ce qui implique pour vous de modifier la classe afin qu'elle diffuse l'événement idoine et qu'elle expose une propriété visuelCourant
en lecture seule.
J'ai bien dit “ce qui implique pour vous“… Puisque tout ça vous savez le faire ! J'avais prévenu, c'est une excuse à réemployer ce qui a été vu jusqu'ici, une excuse pour jouer avec tout ce petit monde fraichement rencontré .
Diffuser un événement on l'a fait là.
Une propriété en lecture seule c'est un accesseur get sans le set, voilà tout
Et dans l'élan vous pourrez, si le cœur vous en dit, ajouter un événement diffusé lorsque la visionneuse atteint le dernier visuel. Ça permettra, le cas échéant, d'arrêter le défilement en fin de lecture - depuis le .fla.
Allez je vous laisse, je vais chercher les cafés, tout le monde en veut ?
Ecouter l'affichage
Oui, il y avait un piège… Rhooo… A peine, une petite chose à réfléchir pour que l'événement SUIVANT soit diffusé dès le chargement.
Si vous l'avez diffusé à l'entrée de chargeVisuels et rien modifié d'autre, forcément il semble n'être diffusé qu'au deuxième passage dans la fonction. Regardons le code du fla :
visio=new VisioPazapa(tabPhotos,leMasque); [...] visio.addEventListener(VisioPazapa.SUIVANT,imgSuivante); [...] addChildAt(visio,0);
Première ligne reproduite : on crée une instance, donc le constructeur est sollicité et la fonction chargeVisuels
invoquée. Elle diffuse bien l'événement, mais on ne l'écoute pas encore…
Pour régler le problème il suffit de passer dans chargeVisuels
après avoir souscrit l'écouteur, au addChild par exemple Et c'est là qu'on se souvient de addedToStage diffusé par les DisplayObject quand ils sont ajoutés à la liste d'affichage.
public function VisioPazapa(pTabVisuels:Array, pMasque:MovieClip,pSens:int=1):void { [...] addEventListener(Event.ADDED_TO_STAGE,chargePhotos); } private function chargePhotos(e:Event=null) { trace("chargePhotos "); dispatchEvent( new Event ("suivant" )); [...]
Et par la même occasion plus utile d'appeler chargeVisuels
. Que vous ayez utilisé chargeVisuels()
ou gotoAndStop(1)
, vous pouvez supprimer cette ligne.
Mais… Du coup…
Ne vous y trompez pas: Ronchon ne ronchonne pas il se demande, nuance…
L'œil rivé aux deux exemples de classes document du chapitre précédent, il se dit que somme toute… c'est la même structure qu'une classe “tout court”.
Bien vu La seule différence entre une classe de document et une classe que j'ai dite classique en intro (souvenez vous c'était il y a longtemps…) c'est le contexte dans laquelle on va l'utiliser. Pour pouvoir l'associer à un clip ou à un fla (ce qui revient au même, maintenant vous n'êtes plus dupes), il convient seulement qu'elle étende MovieClip ou Sprite.
Pour peu qu'elle ne fasse pas référence à des objets (clips, champ texte…) présupposés sur le scénario, rien n'empêche d'utiliser une classe originellement conçue en tant que classe de document, pour instancier un objet.
On ne l'associe à rien, on invoque son constructeur à l'aide de l'opérateur new. Testez donc avec la classe issue de la doc : ColorTransformExample.as
var ct:ColorTransformExample=new ColorTransformExample() addChild(ct) var ct2:ColorTransformExample=new ColorTransformExample() ct2.x=50; addChild(ct2)
Vous obtenez deux instances de ColorTransformExample, chacune accepte le clic et se modifie de la couleur… Mais seulement elle…
Quand la classe était associée à MainTimeline, this c'était tout le scénario. Maintenant this c'est l'instance.
