Masques et ActionScript
Si vous êtes ici c'est, soit que vous venez du tuto masque, et merci d'être resté - si si, ça me touche -, soit que vous saviez déjà gérer du masque et que vous vous intéressez à la façon de le coder. Alors bonjour, soyez le (la) bienvenu(e).
Quoiqu'il en soit, je considère que les techniques du masque n'ont plus rien de mystérieux pour vous et je ne développerai pas.
Ce qui va nous intéresser ici c'est d'obtenir la même chose à l'aide code.
Le ronchon de service : “Pour quoi faire ? C'est tout facile comme ça, vraiment, se compliquer la vie pour le plaisir de se la compliquer, ça va ! J'ai ma vie de famille pour ça…”.
Certes, mais je me suis mal exprimée, je veux dire faire la même chose et plus
Par exemple ça :
Obtenir un effet de halo sur la lumière (pour le plaisir on la rendra accessible au glissé-laché)
Bords de masque flous
Dans un premier temps on s'occupe de la construction.
On va travailler dans un clip, rien n'y oblige mais ça m'arrange pour l'étape d'encore après.
Dans ce clip il nous faut :
• Une photo (dans un clip que je nomme photo
)
• Une pastille pour la lumière -le masque- (un clip aussi, mettons lumiere
)
Pour l'instant ça suffit à nos tests.
Si vous doutez quand même du bien fondé de ce qui va suivre, appliquez donc un filtre flou à la pastille, masquez la photo avec le calque qui porte la pastille, exécutez, constatez que le flou n'est pas pris en compte et rejoignez nous la conscience tranquille
Dans le clip nous avons donc trois calques, un pour la lumière, un pour la photo, un autre pour le code. On pourrait tout mettre sur le même calque, puisqu'on va coder et que les calques n'existent que pour l'interface utilisateur (IDE), mais franchement, ça ne mange pas de pain de travailler propre pour ses yeux.
Appliquer un filtre
C'est la propriété filters disponible pour tous les objets d'affichage (DisplayObjects) qui permet d'appliquer un filtre.
On la valorise d'un tableau de filtres. Si vous voulez superposer des filtres comme on le fait “à la main”, il suffit de mettre plusieurs filtres dans ce tableau.
Ici, on se contente d'un seul filtre, le filtre flou. BlurFilter pour AS.
Il se construit à l'aide de 3 paramètres (les mêmes que sur l'interface utilisateur) : Flou X, Flou Y et Qualité.
var filtreFlou25:BlurFilter= new BlurFilter(25,25,BitmapFilterQuality.LOW);
Cette ligne génère un filtre en qualité inférieure avec 25 en Flou X et Flou Y.
Pour l'appliquer à un clip il suffit de l'ajouter à un tableau que l'on utilisera pour valoriser la propriété filters
du clip considéré.
monClip.filters=[filtreFlou25];
Essayons (si ce n'est déjà fait) et déclinons au cas qui nous intéresse : il s'agit d'appliquer au clip lumiere
un flou de 10 par 10 en qualité supérieure. Comme il n'y a qu'un pauvre flou à utiliser, on peut simplifier l'écriture :
lumiere.filters=[new BlurFilter(10,10,BitmapFilterQuality.HIGH)];
C'est fait pour le flou.
Appliquer un masque
Maintenant, il s'agit de continuer en faisant de cette pastille floutée un masque pour le clip photo
. Rien de plus facile :
photo.mask=lumiere;
Pas plus, c'est la propriété mask, elle attend un displayObject… Trop facile
(pour “oter” un mask il suffit de valoriser la propriété à null :
leClip.mask=null
)
Ah, Ronchon ronchonne : “Ça ne marche pas ! Ça masque mais pas en flou”.
Ai-je dis le contraire ?
Il reste une toute petite subtilité à connaitre (faut quand même bien que je frime de temps en temps) : mettre l'objet masqué en cache sous forme de bitmap. cacheAsBitmap en bon anglais. C'est une propriété booléenne.
photo.cacheAsBitmap=true;
Cette fois on peut tester et se réjouir.
Yapuka poser une photo sombre sous la photo masquée et coder le glisser-lacher.
lumiere.filters=[new BlurFilter(10,10,BitmapFilterQuality.HIGH)]; photo.mask=lumiere; photo.cacheAsBitmap=true; lumiere.buttonMode=true; lumiere.addEventListener(MouseEvent.MOUSE_DOWN,enfonce); lumiere.addEventListener(MouseEvent.MOUSE_UP,lache); function enfonce(me:MouseEvent):void { me.target.startDrag(true, new Rectangle(0,0,width,height)); } function lache(me:MouseEvent):void { stopDrag(); }
Autre exemple
Notez la différence :
• en haut le masque en code avec flou
• en bas le clip réalisé ici, sans code avec un filtre flou “à la main”.
Du coup Ronchon ne ronchonne plus et décline le principe à tout va
Ça ne prend pas 10 minutes avant qu'il recommence à souffler : pff, les trois toujours mêmes lignes de code c'est lassant. Il a fait graphiste, lui, pas dactylo !
Une visionneuse
Passons maintenant à une autre consigne (quasi) irréalisable sans code : transition d'une image sur l'autre, à la chaine.
Comme à chaque fois, je compte sur vous pour faire bien mieux. La seule chose qu'illustre cet exemple c'est un effet masque sinon complexe, du moins personnalisé avec effet flou en prime (maintenant qu'on sait faire, ce sera l'occasion de s'y entrainer )
Etape 1 : construire en "dur"
Pour commencer il va s'agir de construire le clip qui permet la transition d'une image sur l'autre. Ensuite nous verrons comment se débrouiller pour que “ça marche” avec autant de clips qu'il nous siéra. Puisqu'on anticipe qu'à terme on aura à charger depuis la bibliothèque des clips contenant une photo, autant se débarrasser de ça tout de suite. Fabriquons trois clips Mv_Photo1, Mv_Photo2, Mv_Photo3, et puis tiens, Mv_Photo0 qui contiendra un visuel spécifique (affiché une fois seulement).
On va, très provisoirement, construire l'animation avec deux photos sans se préoccuper du code, et quand on aura le principe sous les yeux il ne restera plus qu'à “remplacer” par quelques lignes de code.
Il nous faut : un calque qui portera le masque, un pour la photo qui recouvrira, un autre pour la photo de fond (recouverte).
Le masque est construit avec plusieurs formes chacune équipée d'une interpolation de mouvement (comme pour l'exemple des trois cercles sur ballons là). Nous allons donc construire cette animation dans un clip et poser ce clip sur le calque masque.
Les trois lignes qui vont bien pour faire un masque flou et le tour est joué.
On en fait une là. Il n'y a plus ensuite qu'à distribuer les formes sur des calques différents, cmd-Maj-D (Modification/Scénario/Répartir vers les calques).
Sans l'intervention de Ronchon on aurait été presque déçus : Pourquoi 75 images ? Lui il a fait comme pour les cercles, une seule et ça marche très bien !
Oui
Mais dès l'étape suivante, ça va nous arranger furieusement. Si sur l'exemple que je vous propose il y a 75 images c'est parce que l'animation du masque en compte elle même 75. Je préfère que le clip visionneuse boucle en fonction de la transition mais on est d'accord, jusqu'alors c'était pareil.
Etape 2 : coder
On a la construction, l'idée serait maintenant que quand la tête de lecture boucle, la photo qui recouvrait (celle qui reçoit le mask=true) devienne la photo recouverte et qu'une nouvelle photo soit masquée.
Il va falloir ajouter les photos dynamiquement.
On n'a donc plus besoin ni des clips ni des calques qui les portent. Il nous faut a minima un calque pour le code (c'est plus propre) et un pour le masque. Si on veut peaufiner d'un cadre on peut lui attribuer aussi son calque.
Procédons par ordre, avant de se lancer dans les grandes manœuvres vérifions qu'on sait le faire avec deux clips bien précis, ensuite on verra comment jongler avec une série de clips ingénieusement nommés.
Pour ajouter des clips en code, on utilise la méthode addChild() ou addChildAt() à qui on passe le nom de classe d'un clip. Si ce n'est déjà fait, appelez la fenêtre propriétés de chacun des symboles (clic droit dans la bibliothèque) et cochez la case exporter pour actionScript. C'est le nom dans le champ classe qui est utilisé par les deux méthodes qui nous intéressent.
Par exemple pour un clip nommé Mv_Test (nom de classe !) on en ajoute une instance au dessus des autres comme suit :
var leClip:Mv_Test=new Mv_Test(); addChild(leClip);
Pour ajouter une instance de Mv_Test à l'index 0, soit sous tous les autres :
addChildAt(leClip,0);
Voilà, vous savez tout
Les sources et le code d'illustration utilisent une variable photoRecouvre
qui vaut le clip masqué et une variable photoFond
qui vaut le clip recouvert.
var photoFond:Mv_Photo0=new Mv_Photo0(); var photoRecouvre:Mv_Photo1=new Mv_Photo1(); masque.filters=[new BlurFilter(10,10,BitmapFilterQuality.HIGH)]; photoRecouvre.mask=masque; addChildAt(photoRecouvre,0); addChildAt(photoFond,0); photoRecouvre.mask=masque; photoRecouvre.cacheAsBitmap=true;
Parce que j'ai choisi de mettre un cadre, j'ajoute les clips à l'aide addChildAt, pour qu'ils soient sous le cadre dans la liste d'affichage. Sans cadre on peut se contenter d'un addChild. If faut seulement être attentif à ce que photoRecouvre soit par dessus photoFond, donc :
addChild(photoFond); addChild(photoRecouvre);
Jusque là… Ça va, si ce n'est qu'on n'est pas avancés. Il faut que chaque fois que la lecture reprend image 1 ce soient des clips différents qui soient ajoutés (leurs instances, parlons juste).
Première fois : Photo1 masqué, au dessus de Photo0
puis Photo2 masqué, au dessus de Photo1
puis Photo3 masqué, au dessus de Photo2
etc.
En gros il faut obtenir un nom de classe qui change pour ces deux ligne :
var photoFond:Mv_Photo0=new Mv_Photo0()
var photoRecouvre:Mv_Photo1=new Mv_Photo1();
Quelle chance getDefinitionByName(name:String) nous renvoie une référence à l'objet de la classe spécifiée par le paramètre (c'est pas moi qui le dit c'est la doc
). Ainsi :
var classMv_Rond:Class = getDefinitionByName(“Mv_Rond”) as Class;
nous renverrait une référence au clip Mv_Rond de la bibliothèque.
Il ne resterait plus qu'à ajouter une instance de cette classe à la liste d'affichage, “normalement” :
var leRond:MovieClip= new classMv_Rond();
Imaginons qu'on dispose de deux variables :
var idxSup:int=1;
var idxInf:int=0;
Pas grand chose à modifier pour obtenir non pas toujours des instances de Mv_Photo0 et Mv_Photo1, mais des instances de “Mv_Photo-plus-sufixe”
var clssRecouvre:Class=getDefinitionByName("Mv_Photo"+idxSup) as Class; var photoRecouvre:MovieClip=new clssRecouvre(); var clssFond:Class=getDefinitionByName("Mv_Photo"+(idxInf)) as Class; var photoFond:MovieClip=new clssFond();
Ronchon qui trouve que je traine de l'explication a pris de l'avance ajouté deux lignes en fin de code pour incrémenter les variables “suffixes” et enchainer les photos au prochain passage :
idxSup++ idxInf++
Hé oui ! Il y a problème : pas grand chose, mais problème quand même. Ça ne sert à rien d'incrémenter les variables en fin de code : quand la lecture reprendra idxSup et idxInf seront de nouveau valorisées à 0.
On va donc s'occuper de cette histoire d'incrémentation en début de code en isolant le cas particulier du premier passage, quand les variables n'ont pas encore été valorisées.
Une variable de type int pas valorisée vaut 0.
Il suffit donc de tester idxSup==0
(ou l'autre, faites bien comme vous voulez).
Au premier passage (idxSup==0
), on valorise les deux variables suffixes et une bonne fois pour toutes on applique un flou au masque.
Dès le deuxième passage et pour tous les autres (else
), on fait passer la valeur de idxSup dans idxInf (c'est bien ce qu'on veut : la photo du dessus “passe dessous” pour être recouverte par la suivante) et on pense à supprimer les anciennes instances.
var NumPhotoMax:int=3 // si premier passage if (idxSup==0) { var idxSup:int=1; var idxInf:int=0; //on applique le flou une bonne fois pour toutes masque.filters=[new BlurFilter(10,10,BitmapFilterQuality.HIGH)]; } else { // clip dessus "passe" dessous idxInf=idxSup; // incrément idxSup++; // supprimer removeChild(photoFond); removeChild(photoRecouvre); } // si l'index dépasse le nombre maximum de photo on le ramène à 1 idxSup=idxSup>NumPhotoMax?1:idxSup; // ajout des instances var clssRecouvre:Class=getDefinitionByName("Mv_Photo"+idxSup) as Class; var photoRecouvre:MovieClip=new clssRecouvre(); var clssFond:Class=getDefinitionByName("Mv_Photo"+(idxInf)) as Class; var photoFond:MovieClip=new clssFond(); photoRecouvre.mask=masque; addChildAt(photoRecouvre,0); addChildAt(photoFond,0); photoRecouvre.mask=masque; photoRecouvre.cacheAsBitmap=true;
La seule chose qui peut vous surprendre dans cette proposition de corrigé c'est :
idxSup=idxSup>NumPhotoMax?1:idxSup;
Peut-être pour faire en sorte que les index bouclent de 3 à 1 avez vous écrit :
if (idxSup>NumPhotoMax) { idxSup=1; } else { idxSup=NumPhotoMax; }
C'est pareil ! Question de goût on n'en discutera pas
Conclusion : vers la classe
Le principe est compris. Quitte à me répéter je n'affirme en rien que c'est LA technique pour faire du diaporama, c'est la mise en œuvre des techniques de masquage, avec et sans code.
De fait, si vous êtes tenté(e) de perfectionner la démo pour obtenir un marche/arrêt, ou défiler à l'envers ou reprendre à zéro, vous allez chaque fois plus sinon galérer, du moins surcharger le code.
Et puis imaginons que le principe vous séduise, que vous vous proposiez de le décliner souvent - puisque il s'agit seulement de concevoir des masques animés différents - rapidement vous allez vous lasser du copier coller. Il n'y a pas que Ronchon à ne pas être dactylo dans l'âme.
Et puis ça doit vous alerter : chaque fois que vous vous constatez en train de faire du copier-coller de blocs entiers de code, c'est qu'on peut faire mieux (comprendre moins lassant à l'usure). Tout ce qui est crétin et répétitif doit être confié au programme, nous les êtres humains on conçoit, les créateux c'est nous, le tâcheron c'est lui, le programme.
Ronchon se redresse, la mine gourmande : Ah ! Il l'avait bien dit…
Bon d'accord !
La réponse, ici, serait de concevoir une classe visionneuse.
Ce qui implique de se jeter dans le bain de la POO (Pfff ! Oooh ! Outch !).
A la demande générale de Ronchon c'est donc ce que nous allons faire, en nous appuyant d'abord sur l'exemple des trois lignes qui l'ont énervé, puis en appliquant à un modèle de visionneuse.
Néanmoins, puisque c'est un tout autre chapitre, je vous propose de nous retrouver là après un café, déjeuner, dîner, sommeil, week-end (rayer les mentions inutiles) bien mérité
