Cours ActionScript : Utilisation des classes en programmation AS3


Télécharger Cours ActionScript : Utilisation des classes en programmation AS3

★★★★★★★★★★3.5 étoiles sur 5 basé sur 1 votes.
Votez ce document:

Télécharger aussi :


Stéphane Popu

ActionScript 3

Niveau 2 – Utilisation des classes en programmation AS3

Préambule : Autour de la création d’une application ou sites flash AS3

L’as3 pourquoi ?

Première chose à faire : se documenter.

Se fabriquer sa boite à outils permanente

Création de Sites

Avantages de l’incorporation

Avantages de la séparation des pages 

Le point sur le référencement Flash 

Le poids de l’application

Intégration de l’application avec SWFObject

Utilisation.

Partie 1 : Autour du modèle évènementiel 

1 Les évènements dans flash. 

Asynchronisme et évènements 

Utilisation du système évènementiel

Construction d’une fonction évènementielle.

Stopper l’écoute d’un évènement. 

Savoir se servir de propriétés et méthodes du paramètre de la fonction évènementielle. 

Les évènements clavier.

Les évènements redondants : EnterFrame et Timer.  Créer un mouvement à l’aide du modèle évènementiel. 

Timer 

Remplacer le curseur de la souris par un clip

Charger une image ou un swf externe. 

Gérer le chargement une image ou un swf externe. 


A / Gestion des erreurs. 

B/ Création et gestion d’une animation de chargement. 

C/ optimisation du swf chargé (scaling )ou de l’image (lissage). 

2 Compréhension avancée des enjeux posés par le système évènementiel AS3.

Le diffuseur d’évènements

Les évènements et la liste d’affichage

Parcours d’un évènement écran dans la liste d’affichage : L’utilisation avancée des évènements écrans (useCapture, bubbling).

La phase « at target »

La phase de capture

La phase de bouillonnement

Stopper la progression événementielle.

Installer un diffuseur d’évènement sur une classe non graphique. 

Exemple : une classe de chargement.

Depuis la classe appelante 

Créer ses propres évènements et transmettre des informations personnalisées.  

Partie 2 : Animer des éléments : L’utilisation de TweenMax.

Introduction

Les classe statiques 

Installation de TweenMax 

Activation des classes dans le script.

Utilisation.

Utilisations des paramètres de TweenMax.

Utilisation des fonctions spéciales : l’exemple des courbes.

Les trajectoires

Création d’une animation complexe.

Delay 

Utilisation des fonctions de contrôle (onComplete, onStart, onUpdate).

Rappel de notation

Partie 3 : Séparer le contenant et le contenu : L’utilisation de la classe XML

Structure d’un XML

Les éléments 

Les attributs

Ecrire un xml dans l’espace actionscript

Charger un fichier XML externe

Partie 4 : Sur les classes

Comment organiser son code ?

1.   Création d’applications, en préparer les différents aspects

Le schéma Temporel.

Le schéma d’imbrication.

Les schémas Objets

2.   Organiser son travail, savoir circonscrire ses classes.

Architecture des classes

3.   Organiser son travail, les classes de l’application, les classes permanentes.

L’enveloppe et le cœur

4.   Les classes non graphiques dans une application Flash

Gestion de données xml dans une application.

Utiliser une classe de stockage de données.

Stockage dans un objet

Stockage et gestion depuis une classe de parcours du xml :

Partie 5 : Maîtriser La gestion des données entrantes

L’objet loaderInfo.

Récupération et utilisation de variables issues de la page html.

Exploiter les classes de la bibliothèque d’un swf externe.

Récupération et incorporation de polices

Partie 6 Utiliser le son en actionScript 3 (P.O.O).

Du contrôle basique à la création d’un lecteur audio.

Organisation de l’application 

1 la classe principale : LecteurSonIDE.

A – Chargement des données xml

2   – La classe BadgeSon

3   -  la classe « cœur » : PisteSon.

Documentation (dictionnaire de référence actionscript)

Cahier des charges et choix de la stratégie de programmation.

Chargement du son et mise en lecture

La fonction jouerSon 

Processus attachés à la fonction jouerSon  

Manipulation du son.

volume

Arrêt, Pause, Avancer reculer dans la piste.  

Pauser  /  lancer la lecture .

Arrêt Complet

Ajout et écoute du lecteur sur la classe principale.

Liaisons fla – classe principale – Classe PisteSon.

Traitement graphique de la navigation et du volume

Mise en place de la playlist 

1  les boutons. 

2  La liste.

3  Liaison au fonctionnement du lecteur musical.

4  L’habillage

5  Rafraîchissement

6  Classe playlist au complet :

7  Implantation dans la classe principale

Mise en place du bouton infos 

Conclusion

Partie 7 Autour de la syntaxe as3

Maitriser l’utilisation du mot clé « is » L’utilisation de la syntaxe crochet dans AS3 .

Partie 8 Diffuser de la vidéo à l’aide de flash

Préparer une vidéo pour l’incorporation.

Quels sont les codecs accepté par le format qui contiendra ma vidéo ?  

Mettre en place une vidéo dans l’environnement auteur.

Mettre en place et contrôler une vidéo externe dans flash à l’aide du script.

Utilisation de la Web Cam de l’utilisateur.

Création d’une classe vidéo

Organisation de la classe

Les modifications depuis Flash CS4.

Création de la classe

Contrôles sur la vidéo

La classe finale 

Annexes

Appendice 1 : Outils géométriques : Rectangle et Point.

L’objet Rectangle.

Récupérer les limites d’un DisplayObject

La méthode scrollRect

Naviguer dans une image plus grande que le cadre : 

L’objet Point.

Appendice 2 : Envoyer une requête http

navigateToURL(requête : URLRequest, fenetre :String)


Préambule

Autour de la création d’une application ou sites flash AS3

L’as3 pourquoi ?

Construire une application (site, jeu…), ne signifie pas se lancer tout de suite dans une création de code à tout va

Une publication papier, par exemple, est pensée en termes de grille, de charte graphique, de hiérarchisation du contenu. Avec flash la chose n’est pas différente : Il y a des étapes à ne pas négliger avant de se lancer dans des lignes de codes au risque de se perdre sous les traces() et les impasses !

En écartant la charte et la création graphique, il reste quatre étapes indispensables à la création d’une application flash. Ou plutôt : il va falloir envisager son application de trois manières différentes. La première concerne le temps de la visite. La seconde définit l’espace de l’application et l’agencement des éléments graphiques et interactifs des uns vers les autres. Enfin la dernière étape de pré-projet consiste en un listage des fonctionnalités et actions, et concerne donc tant l’ergonomie pour le visiteur que l’architecture des différentes classes qui composeront le programme.

Toutes ces étapes nécessitent plus le papier, la règle, et le stylo que les touches de votre clavier. C’est le moment de prendre un bol d’air, et de penser sous un arbre - comme le veut la tradition -, avant de se lancer dans les tréfonds d’actionScript ! 

Première chose à faire : se documenter.

La formation que vous suivez est avant tout destinée à vous permettre de voler de vos propres ailes. Les possibilités liées à actionScript sont gigantesques qu’il s’agisse d’effets visuels ou d’applications complexes. En somme, vous pouvez dire à vos futurs clients que tout est réalisable, qu’il s’agit seulement d’une question de budget ! 

En sortant de cette formation vous devriez être au fait sur l’organisation et la mise en place d’une application, et surtout vous devriez avoir pris l’habitude de savoir comment vous diriger pour manipuler une technique ou un objet que vous n’aurez pas eu l’occasion de voir durant le cours.

Adobe met à la disposition de l’utilisateur d’actionscript toute une gamme de documentation. Les deux plus importantes sont l’aide à la programmation as3 en français. Vous y trouverez des démarches étapes par étapes pour réaliser certaines animations ou applications. Vous y trouverez souvent les codes nécessaires à leur application. 

Autre source possible, le livre, Pratique de l’actionScript 3 de Tibault Imbert ( Entré cette année dans l’équipe de concepteurs du logiciel) trouvable en libre accès. Il traite des grands aspects de l’actionscript 3 avec de nombreux codes en exemple

L’autre outil indispensable est le dictionnaire de référence AS3, malheureusement encore non traduit en français. Il est également pourvu de nombreux exemples utilisables directement.

Se fabriquer sa boite à outils permanente

Au fil des projets, nous nous rendrons compte que certaines classes seront utilisées régulièrement alors que d’autres n’auront d’utilité que pour le projet en cours. Principalement les premières concerneront :

-    Les fonctions redondantes qui pourront être stockées en tant que fonctions statiques dans une classe thématique (comme une classe de placement automatique d’objets, outils de calcul divers…).

-    Les classes de chargement (texte et graphiques). -         Les classes de communication externes.

-    Certains évènements personnalisés…

Nous pouvons alors faire le choix de séparer ces classes dans un répertoire à part et d’en donner le chemin à flash et ou flashDevelop et qui deviendront disponibles de façon permanent au fil des projets. Nous pouvons alors capitaliser une lignée importante de codes qui nous resserviront à travers nos différents projet sans avoir de copie et donc de versions différentes de ces classes.

Nous configurons flash CS3 en ce sens :

Menu : Modifier / préférences / actionScript / paramètres d’actionScript 3 / « ajouter le répertoire cible » ;

Pour flash develop

Menu : tools / global Classpaths / “ajouter le répertoire cible”

Création de Sites :

Choisir une stratégie de site full flash : Incorporé sur une page ou sur plusieurs ? Voici une petite liste des avantage de l’un et de l’autre pour vous aidez à faire valoir vos choix auprès de vos clients. 

Avantages de l’incorporation

-    Maîtrise de processus de chargement. Et scénarisation de l’affichage -    Chargement unique des parties principales de l’application.

-    Référencement possible et «apparence de navigation » à l’aide d’un système de « deep linking ». 

Avantages de la séparation des pages 

-    Sectorisation de l’application. 

-    Comportement du site plus proche des habitudes d’un internaute (utilisation des outils d’historiques).

-    Plus de clics sur le site. 

-    Meilleur référencement naturel.

Le point sur le référencement Flash 

Le principal problème d’un site full flash est son référencement naturel.

Dans l’état actuel de choses, le moteur de recherche n’est capable de voir que les textes et les liens inscrit en dur dans l’environnement auteur. Aucun document lié comme un xml n’est interprété comme faisant partie de l’application.

La seule solution actuellement pour cela est d’utiliser une interface flash / javascript assurant le référencement et la navigation dans l’historique donc l’accès direct à une page ou l’utilisation des fonctions du navigateur. On citera parmi ces systèmes « swfaddress ».  Malheureusement l’adresse dispensée contient un « # » ce qui fait croire au moteurs que le lien concerné est une ancre de la même page. Pour contourner cela vous travaillez en conjonction avec un developpeur php qui préparera un fichier d’url rewriting.

Il en reste que la meilleure solution reste de proposer aux moteur un contenu alternatif dans la balise dédiée au swf dans la page html.

Le poids de l’application

La question du poids de fichier se pose dès lors où le fichier nécessitera plus de deux à trois secondes avant d’avoir le moindre affichage. A partir de 500 ko (dépendant de l’audience visée) pour une enveloppe de site full flash, le temps commence à paraître long sans le moindre petit élément graphique pour divertir l’internaute.

Il convient alors de placer une intro scénarisée dans le fichier de départ… Ce qui permettra de pouvoir télécharger les ressources principales externes sans donner l’impression de vide...

Intégration de l’application avec SWFObject

SWFObject est la manière la plus pertinente d’intégrer du flash sur une page html. Il est en cours de généralisation dans dreamweaver.

Il fonctionne à l’aide d’un fichier javascript externe qui met en place le swf quel que soit le navigateur employé. Seules subsistent sur la page html, les instructions modifiables ou utilisables par le créateur (passage de variables, taille fixe ou non…).

Pour mettre en place ce système deux solutions : 

-    Si vous employez flashDevelop vous pouvez récupérer le modele de la page en créant un nouveau projet AS3. Attention ne faites pas de celui-ci votre projet. Un projet flashDevelop AS3 est compilé via flex sdk et non le compilateur du logiciel. La classe principale n’etend donc pas un fichier fla mais est en réalité le point de départ de l’application.

-    Vous pouvez aussi récupèrer les sources et la documentation à l’adresse suivante :

vous aurez ainsi accès à une connaissance un peu plus intime du fonctionnement et des possibilité de cet objet javascript.

Utilisation.

Vous aurez besoin dans votre dossier, en plus du swf, d’un dossier « js » dans lequel se trouve le fichier swfObject. Et du lien de téléchargement du player « expressInstall ». Vous pouvez bien sur ranger ces objet de manière différente en modifiant les chemins d’accès dans votre page html.

La page suivante peut être utilisée en tant que modèle pour le placement d’un swf en « pleine page ».

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"

"">

<html>

<head>

      <title>Classes</title>

       <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />

      <meta name="language" content="en" />

      <meta name="description" content="" />

      <meta name="keywords" content="" />

      <!—importation de swf object -->

      <script src="" type="text/javascript"></script>

      <script type="text/javascript">

      <!-- vous pouvez passer des paramètres au travers de l’objet

flashvars  syntaxe > id1 : « valeur 1 », id2 : « valeur 2 », …  -->

            var flashvars = {

            };

      <!-- ici les paramètres de réglages du player (comportement dans la page, apparition du menu complet… ) cf. swfObjet doc  -->

            var params = {                menu: "false",              scale: "noScale",               allowFullscreen: "true",              allowScriptAccess: "always",

                  bgcolor: "#FFFFFF"

            };

      <!-- paramètre utilisé par mozilla pour localiser l’animation dans la page. Utiliser le nom du fichier sans l’extension  -->

var attributes = {

                  id:"Classes"

            };

<!-- instruction d’insertion (on y reconnaît une fonction) 1 : fichier / 2

: Div d’insertion / 3,4 : largeur, hauteur / 5 : version du player / fichier de lien vers l’installation du player requis / 6, 7, 8 : objets mis en place ci-dessus  -->

             swfobject.embedSWF("", "altContent", "100%", "100%",

"9.0.0", "", flashvars, params, attributes);

      </script>

<!--  les styles sont ici définis afin de faire apparaître le swf sur la fenêtre entière du navigateur. -->

      <style>

            object:focus { outline: none; }           html, body { height:100%; margin:0; }             body { margin-top:0px; background-color : #ffffff}      h1{font-family:Arial, Helvetica, sans-serif; color:#99CC33}             p{font-family:Arial, Helvetica, sans-serif; color:#fcfcfc}

      </style>

</head>

<body>

<!--  div d’insertion : Attention le div est REMPLACE par le swf. Si vous voulez le placer dans une page composée vous devez l’insérer dans un autre div et gérer le placement par celui-ci -->

      <div id="altContent">

            <h1>Classes</h1>

            <p>Alternative content</p>

            <p><a href=";><img 

 src=" " 

                  alt="Get Adobe Flash player" /></a></p>

      </div>

</body>

</html>


Partie 1 : Autour du modèle évènementiel  

1 Les évènements dans flash. 

Asynchronisme et évènements 

L’un des atouts de flash est en même temps l’une de ses difficultés. Contrairement au html

Actionscript est un langage asynchrone. C'est-à-dire qu’il n’attend pas que certains processus (comme les chargements externes) se terminent pour poursuivre l’exécution du code. Les actions souris pouvant déboucher sur bien plus que le renvoi vers une nouvelle url, elles doivent être entièrement construites également. Certains autres permettent de mettre en oeuvre des intervalles de temps pour créer du mouvement ou des décomptes. Les objets liés aux médias diffusés comme le son et la vidéo émettent eux aussi des signaux permettant de connaître leur état. Ce en effet sont des processus qui ont besoin d’être maîtrisés dans leur déroulement. Pour tout ces processus, l’actionscript propose un système unifié, le modèle évènementiel.

Il met en œuvre : 

-    un objet diffuseur

-    Un objet décrivant un type d’évènement -     un écouteur placé sur ce diffuseur.

-    Une fonction qui sera déclenchée par la réception.

        -      

Différents évènements sont produits par divers objets, s’y retrouver en représente la plus grande difficulté. 

Pour s’aider dans cette tâche, la documentation du langage renseigne les différents évènements produits par les différents objets présents dans le langage. Il y a aussi le regroupement logique : tous les éléments pouvant présenter une interactivité diffusent des évènements souris. Dans ces éléments on retrouve : TextField, MovieClip, Sprite, SimpleButton.

On sait donc que tous ces objets sont susceptibles d’accueillir un écouteur d’évènement souris. De même on s’imagine bien que les évènements de chargement émettent les éléments nécessaire à leur bon fonctionnement : départ, progression, fin du chargement, erreur d’url…

Le système évènementiel est la clé de voûte qui va permettre d’ajouter de l’interactivité à vos animations, de les alléger en vous servant de chargements externes, etc… Autant dire que ce chapitre est le plus important. Pour ainsi dire même, la maîtrise du langage tient à la maîtrise de ce système.

Utilisation du système évènementiel

Un évènement est un objet composite tout comme peut l’être le type MovieClip ou Sprite, cet objet est automatiquement instancié si l’action qu’il décrit est déclenchée. Puis, il est diffusé au travers d’un diffuseur d’évènement EventDispatcher qui est un autre objet composite au travers de sa méthode dispatchEvent (événement). Pour plus de commodité l’objet EventDispatcher est directement intégré aux objets interactifs que nous avons cités plus haut, nous en verrons la raison par la suite. 

Pour utiliser ce système il suffit de placer un écouteur d’évènement sur l’objet diffuseur au travers de la méthode : 

objet_a_ecouter.addEventListener( Evt_a_ecouter , fonction_a_declencher) 

Pour créer une interaction dans flash, il faut donc effectuer ces trois choses : 

-  Sélectionner un objet qui diffuse l’événement recherché. 

-  Abonner un écouteur sur cet objet. 

-  Créer une fonction qui traitera l’évènement s’il est déclenché. 

Tous les systèmes permettant les interactions ou diffusant des informations dans flash sont basés sur cette même formule. Il existe bien une ou deux exceptions basées sur les processus en vigueur dans les précédentes versions (diffusion  vidéo, moteurs d’interpolation tierces), que nous évoquerons en temps utiles. 

Ce qu’il faut retenir ici c’est simplement le trio : écouteur, diffuseur, fonction à déclencher.   

Construction d’une fonction évènementielle.

Nous avons sur la scène un MovieClip auquel nous avons donné « mc_lien » comme nom d’occurrence. On souhaite le rendre cliquable et lancer un lien vers une autre page du site. 

// Cette commande fait apparaître la main lorsque la souris passe sur le 

// clip

mc_lien.buttonMode = true ;

// écoute de l’évènement souris « clic » sur le clip mc_lien.addEventListener(MouseEvent.CLICK,envoiLien)

function envoiLien ( even : MouseEvent) : void 

{  

      // Mise en place d’une requète url ( chaque processus flash, faisant

// Appel à une ressources externe utilise cet objet

var requete : URLRequest = new URLRequest(“”);

// fonction générique pour l’appel d’une nouvelle page.

navigateToURL(requete, “_blank”); 

Exercice : Mettez en place un menu de trois boutons avec un trace dans chaque fonction.

Regardons maintenant notre fonction évènementielle un peu plus en détail, car elle répond elle aussi à un schéma très strict.

Une fonction liée à un évènement doit TOUJOURS avoir un paramètre en parenthèse : Ce paramètre est l’instance de l’objet évènementiel diffusé par l’élément écouté (ici l’objet est de type MouseEvent). Il contient des propriétés et méthodes qui permettent d’obtenir des informations concernant l’objet écouté et la nature de l’évènement ( comme par exemple connaître les coordonnées locales de la souris sur l’objet lors du clic ). 

Le paramètre évènementiel fait aussi toujours référence à l’objet qui l’a émis au travers de la propriété target. 

Une fonction évènementielle ne peut avoir qu’UN SEUL paramètre. En l’occurrence celui que nous venons de voir. Pour utiliser des variables non déclarées dans la fonction il faudra que celles-ci soit déclarée à l’extérieur du corps des fonctions (soit en début de script image, soit dans une classe pourvu de la déclaration « public » ou « private ». Sachez qu’il existe aussi la possibilité de créer ses propres évènements permettant de faire transiter des données lorsque la solution précédente n’est pas applicable. Nous aurons l’occasion de nous pencher sur les classes d’évènements personnalisés dans la deuxième partie du cours.

Une fonction évènementielle ne peut renvoyer aucune valeur. L’utilisation du mot clé « return » en fin de fonction provoquera une erreur. Cela est toutefois logique puisque l’appel de la fonction est effectué automatiquement et qu’il n’existe de ce fait aucune façon de récupérer une éventuelle valeur issue de l’exécution du code. 

Si vous vous posiez encore la question de savoir ou et comment placer variables et autres fragments de codes, les quelques règles que nous venons de voir sont déterminantes. Ce sont souvent celles-ci, en fait, qui imposeront de placer une variable localisée dans une fonction ou à la racine du code classe ou scénario. C’est le point d’entrée vers l’architecture de votre application.

Stopper l’écoute d’un évènement. 

Nous avons besoin de stopper l’écoute d’un évènement lorsque nous ne voulons plus que la fonction soit déclenchée ou lorsqu’il devient nécessaire de supprimer l’objet qui diffuse l’évènement. Pour ce dernier cas il est nécessaire de connaître un peu mieux le fonctionnement du lecteur flash : Pour ce programme, un élément ne peut être supprimé de la mémoire que si plus aucun évènement n’y est attaché. Sinon, la trace de l’objet reste en mémoire et si il est lourd graphiquement, il peut ralentir voire compromettre le bon fonctionnement de l’application.  Dans le principe tout écouteur placé doit pouvoir être retiré avant suppression de l’objet sur lequel il est placé. Nous utilisons pour cela la méthode présente sur l’objet EventDispatcher : diffuseur.removeEventListener(type_evenement, fonction_evenementielle). 

// nous créons le MovieClip cette fois ci par le code : var mc_lien :MovieClip=new MovieClip();

mc_lien.graphics.beginFill(1,1); mc_lien.graphics.drawRect(0,0,200,40);

mc_lien.addEventListener(MouseEvent.CLICK, envoiLien); 

mc_lien.buttonMode = true ;  addChild(mc_lien);

function envoiLien (even :MouseEvent):void 

var requete : URLRequest = new URLRequest("");  navigateToURL(requete, "_blank");

// retrait de l’écouteur.  mc_lien.removeEventListener( MouseEvent.CLICK , envoiLien); 

// retrait du comportement main du pointeur souris mc_lien.buttonMode = false ;  } 

L’exemple ici montre comment rendre un bouton cliquable une seule fois. Le clic déclenche la fonction qui renferme l’instruction de désabonnement et la désactivation du pointeur « main » sur le clip. En outre si nous supprimons ce clip il sera effectivement éligible à la suppression totale du cache de l’animation. 

Savoir se servir de propriétés et méthodes du paramètre de la fonction évènementielle. 

Il reste un élément du système évènementiel que nous n’avons pas abordé : Il s’agit de la variable « even » inscrite entre les parenthèses de notre fonction. 

Nous savons donc qu’il s’agit de l’objet instancié lorsque l’action à été déclenché. Cet objet contient des propriétés et méthodes communes à l’ensemble des objets  

évènementiels. 

En premier lieu faisont connaissance avec target.

target est une propriété partagée par tous les évènements. C’est un raccourci vers l’objet qui à diffusé l’évènement.

Donc lorsque nous utilisons la propriété target du paramètre de notre fonction nous accédons dans l’exemple suivant au clip sur lequel il faut cliquer.

function envoiLien (even :MouseEvent):void 

{  var requete : URLRequest = new URLRequest("");

navigateToURL(requete, “_blank”); 

even.target.removeEventListener( MouseEvent.CLICK , envoiLien);  even.target.buttonMode = false ;

D’autres propriétés en revanche sont propres au type d’évènement en cours. L’objet MouseEvent contient de nombreuses informations, telles que les coordonnées de la souris sur l’objet ou la scène au moment du clic. Pour permettre les combinaisons de touches, il est possible encore de savoir si la touche « ctrl »( ou « command »), « alt » ou « maj » sont enfoncées . 

Les évènements clavier.

Parmi les évènements utiles dans flash on trouvera aussi les évènements clavier. Moins utilisés. Ils peuvent trouver néanmoins de nombreuse application, de la présentation d’entreprise au jeu de plateforme en passant par les fonctions cachées . On se sert usuellement de deux des propriétés de l’objet transmis à la fonction.

Exemple : gestion clavier d’une progression dans le scénario  d’un clip.

On utilise ici la propriété keyCode (code de la touche) de l’évènement. On se sert également de la classe Keyboard qui répertorie les actions claviers sur les touches spéciales de l’ordinateur. Ici nous avons un clip dont le déroulement est ponctué de stop() au long de la timeline. Nous cherchons à la faire progresser si l’on appuie sur la fléche du bas : 

// on écoute l’objet stage pour les évènements clavier. stage.addEventListener(KeyboardEvent.KEY_DOWN,gestionGraphique);

function gestionGraphique(even:KeyboardEvent):void

{

      // on compare la propriété keyCode à la touché concernée stockée

// dans la classe Keyboard    if(even.keyCode == )

      { 

            ();

      }

}

Exemple reconnaissance d’une combinaison de touches :

Ici, c’est une autre propriété : charCode (code du caractère) qui va nous aider à déterminer  précisément quel caractère inscriptible du clavier donne lieu à l’évènement. 

Pour être traduit en une chaîne de caractère nous allons avoir recours à une fonction statique de l’objet string dédiés à cela : String.fromCharCode(even.charCode)

Nous nous servons également de deux autres propriétés de l’évènement permettant de savoir si la touche « ctrl » ou « maj » sont enfoncées simultanément au moment de l’événement.  Pour résultat la condition ne se déclenche que si la combinaison de touche « shift – ctrl – e » est détectée.

stage.addEventListener(KeyboardEvent.KEY_DOWN, ecouteClavier);

function ecouteClavier(even:KeyboardEvent):void

{

      var code:String=String.fromCharCode(even.charCode)

      if (even.shiftKey)

      {

            if (even.ctrlKey)

            {

                  if (code == "E")

                  {

                        trace("combinaison de touché détecte")

                  }

            }

      }

}

Exercice : créez à partir de ces  codes un objet avec un déplacement commandé par le clavier

Les évènements redondants : EnterFrame et Timer.  Créer un mouvement à l’aide du modèle évènementiel. 

Certains évènements, tels qu’un clic souris, le signal de fin de chargement d’une image externe sont envoyés une seule fois à chaque fois que l’utilisateur actionne le bouton gauche de sa souris. D’autres sont diffusés plusieurs fois et déclenchent à chaque fois la fonction qui lui est associée. Certains d’entre eux sont même diffusés de façon régulière. Ces évènements s’avèrent très utiles pour crées du mouvement, ou un déroulement dans le temps. Nous allons en découvrir un des plus courants : L’événement ENTER_FRAME : 

La diffusion de l’évènement ENTER_FRAME est assurée par les MovieClip et les objets héritant de MovieClip à savoir la scène de notre animation. Le comportement de cet évènement est d’envoyer un signal à chaque fois que la tête de lecture du clip entre dans une nouvelle image. Attention, ce comportement n’est pas dépendant de l’état de lecture du clip. Il envoie donc cet évènement même si le clip est stoppé. 

L’évènement ENTER_FRAME est dépendant de la cadence de l’animation. Une animation cadencée à 12 images par secondes déclenchera la fonction associée 12 fois en une seconde. 

Ce comportement nous permet donc de créer un défilement dans le temps à cadence rapide… Donc, du mouvement bien sur, mais aussi des vérifications redondantes pour des jeux ou d’autres applications à haute interactivité. Grâce à cette fonction on entre de plein pied dans ce qu’il n’est pas possible de réaliser en s’appuyant sur la ligne de scénario de flash. 

Timer 

Si l’on cherche à créer un mouvement qui soit indépendant de la cadence de flash on orientera son choix sur la classe Timer en effet l’objet timer fonctionne selon une horloge interne configurée pour envoyer un évènement de type TimerEvent.TIMER toute les x millisecondes. La cadence donnée au fichier n’a par conséquent aucune influence sur celui-ci.

L’objet Timer est nettement plus souple qu’un gestion par Event.ENTER_FRAME en effet, il dispose de fonction permettant de démarrer ou stopper le compte, il est aussi possible de modifier la frequence de l’horloge en cours de fonctionnement, permettant par exemple d’augmenter la vitesse de lecture d’un clip en asservissant sa lecture au fonctionnement de l’horloge.

Remplacer le curseur de la souris par un clip

Sur un nouveau fichier nous importons une image que nous insérons dans un clip. Nous le posons sur la scène avec « image » comme nom d’occurrence. 

Nous créons un second clip contenant une forme vectorielle créée dans l’environnement auteur un cercle rempli avec un dégradé radial. La couleur est sans importance puisque la forme nous servira de masque. En revanche, il faut que la couleur soit à alpha = 100 en son centre et 0 sur les bords. Nous déposons également ce clip sur la scène avec « lorgnette » comme nom d’occurrence. 

Nous créons un calque « actions » dans lequel nous écrivons ce code. 

// Ces deux lignes sont indispensables pour que le dégradé alpha soit pris 

//en compte. cacheAsBitmap sert à ce que le lecteur transforme la forme 

//vectorielle en tableau de pixels.  lorgnette.cacheAsBitmap = true; image.cacheAsBitmap = true;  // Masquage de l’image par la forme  =lorgnette;

// nous rendons le pointeur de la souris invisible 

(); 

// le scénario principal étant du type MovieClip on peut placer l’écouteur 

// sur celui-ci

this.addEventListener(Event.ENTER_FRAME,pointer) 

function pointer (even:Event) 

{ lorgnette.x=mouseX;  lorgnette.y=mouseY; 

// les deux lignes suivantes peuvent remplacer les deux précédentes.

// cela permet de créer un effet de retard sur le mouvement du masque :

//lorgnette.x= mouseX*0.4 + lorgnette.x*.6;

//lorgnette.y= mouseY*0.4 + lorgnette.y*.6;

}   

Voici le même code commandé par une instance de la classe Timer. Le code devient alors insensible à la cadence de l’animation :

lorgnette.cacheAsBitmap = true; image.cacheAsBitmap = true; =lorgnette;

//création d'une instance de la classe timer var horloge:Timer = new Timer(40);

// Ajout d'un ecouteur d'évènement sur cette classe horloge.addEventListener(TimerEvent.TIMER,pointer);

// départ du compte horloge.start(); ();

// la fonction pointer reçoit mainetant un évènement de type TimerEvent function pointer (even:TimerEvent)

{

      lorgnette.x= mouseX*0.4 + lorgnette.x*.6;  lorgnette.y= mouseY*0.4 + lorgnette.y*.6; }

L’ensemble des objets de type évènement auront toujours des propriétés liées à leur nature évènementielle. Les événements diffusés par une interpolation permettent d’en contrôler très finement le fonctionnement et même de les combiner en diffusant des signaux au début, fin, et à chaque changement de valeur de l’objet interpolé. 

Charger une image ou un swf externe. 

Le chargement d’une image ou d’un fichier swf externe est une opération très courante. Grâce à ce système vous pourrez mettre en place des sites entiers en alliant un système de chargement externes aux menus que vous devriez savoir mettre en place arrivé à ce point du cours.

Tout d’abord il est important de souligner que le chargement d’un media est susceptible de diffuser un nombre important d’évènements tels que le début, la progression, le signalement d’une erreur de chargement, la fin du chargement et quelques autres encore. Nous allons appréhender la base du chargement externe au travers du chargement simple d’une image et d’un swf. 

Ce premier code utilise uniquement la classe d’évènement Event. Sachez toutefois que le signalement d’erreur de chargement est géré par le type d’objet IOErrorEvent, et que les signaux de progression qui nous permettront de mettre en place une barre de chargement est assuré par la classe ProgressEvent. 

Nous faisons la connaissance de l’objet Loader: C’est l’objet qui nous sert à charger le media.  Il fait partie des objets affichables dans flash. Cela à plusieurs implications on peut directement l’utiliser pour l’affichage du graphique chargé. Il peut être réutilisé pour charger d’autres images. Il contient en outre une propriété content qui permet d’extraire le contenu chargé.

celui-ci est équipé d’un diffuseur d’évènement (loader.contentLoaderInfo) qui va servir à émettre tout les signaux du téléchargement. Donc ici le diffuseur et l’objet ne sont pas une seule et même chose.

En nous intéressant à la documentation nous nous apercevons que deux évènements peuvent correspondre à notre attente : 

Event.complete

Dispatched when data has loaded successfully. (diffusé une fois le chargement terminé) 

Dispatched when the properties and methods of a loaded SWF file are accessible. (diffusé une fois que le swf chargé est disponible).

La différence entre les deux réside dans le fait que l’évènement init assure qu’un swf ou une image permettront toutes les propriétés et méthodes dont dispose un movieClip ou un bitmap. Nous choisirons donc d’écouter cet évènement. 

Nous allons abonner un écouteur qui réagira lorsque l’évènement INIT sera émis par l’objet contentLoaderInfo. Il sera alors possible de le redimensionner, si c’est un bitmap de le lisser en cas de redimensionnement, de le centrer dans un container puis in fine de l’afficher sur la scène. 

var container_principal:MovieClip=new container();  addChild(container_principal);

var requete:URLRequest=new URLRequest("VOTRE_IMAGE_OU_SWF"); var chargeur:Loader=new Loader(); 

chargeur.contentLoaderInfo.addEventListener(,chargementTermine); function chargementTermine(even:Event) 

container_principal.addChild(chargeur);

//centrage du media 

chargeur.x= container_principal.width / 2 – chargeur.width / 2; chargeur.y= container_principal.height / 2 – chargeur.height / 2;  chargeur.contentLoaderInfo.removeEventListener(,chargementTermine

); 

Gérer le chargement une image ou un swf externe. 

Comme je l’ai évoqué, le chargement d’une image émet plus d’évènements que ceux que nous avons vus. Ces évènement peuvent nous permettre en particulier d’éviter des erreurs qui bloqueraient notre application si le chargement ne peut s’effectuer ou d’afficher une animation de chargement. Tous les évènements sont diffusés au travers de l’objet contentLoaderInfo contenu par l’instance de notre loader. C’est donc lui qu’il faut cibler avec des écouteur approprié)

A / Gestion des erreurs. 

Un problème de chargement (connexion coupée, mauvais lien) peut mettre en péril la stabilité et le bon déroulement d’une image. Aussi prévoir ces types de comportement peut nous permettre d’afficher quand même un symbole par défaut, de la même manière qu’en html. Pour ce faire il est utile de prévoir dans son animation un clip que nous pourrons mettre en place par le script en l’exportant au travers de son panneau de liaison. Nous le ferons passer pour l’image chargée au cas où une erreur est émise. Dans ce cas deux objet peuvent apparemment nous servir. Le premier « flash.events.HTTPStatusEvent » informe le lecteur flash sur la présence ou l’absence de connexion internet. Il est émis dans l’un ou l’autre cas. Il contient la propriété status qui envoie le code de connexion http correspondant. Nous n’avons pas un réel besoin d’écouter cette évènement dans le cadre de notre application.  

Le lecteur en cas de lien brise ou d’image corrompue envoie également un évènement qui spécifie l’erreur : flash.events.IOErrorEvent.IO_ERROR

En écoutant cet évènement il nous est possible d’ajouter une gestion des erreurs : 

Nous créons un clip dans la bibliothèque avec « container » comme nom de liaison. Il contient éventuellement un décor. 

var container_principal:MovieClip=new container();  addChild(container_principal); 

var requete:URLRequest=new URLRequest(""); var chargeur:Loader=new Loader(); 

chargeur.contentLoaderInfo.addEventListener(,chargementTermine); chargeur.contentLoaderInfo.addEventListener(IOErrorEvent.IO_ERROR, erreurChargement); 

function erreurChargement (even: IOErrorEvent):void 

// retrait des ecouteurs 

chargeur.contentLoaderInfo.removeEventListener(,chargementTermine

); 

chargeur.contentLoaderInfo.removeEventListener(IOErrorEvent.IO_ERROR,erreur Chargement); 

// instanciation d’un clip destiné à remplacer l’image manquante  var img_defaut : MovieClip = new ImageDefaut();  container_principal.addChild(img_defaut); // j’envoie un signal de fin de chargement 

chargeur.contentLoaderInfo.dispatchEvent(new Event()); 

function chargementTermine(even:Event):void 

{

container_principal.addChild(chargeur);

//centrage du media 

chargeur.x= container_principal.width / 2 – chargeur.width / 2; chargeur.y= container_principal.height / 2 – chargeur.height / 2;  chargeur.contentLoaderInfo.removeEventListener(,chargementTermine

); 

chargeur.contentLoaderInfo.removeEventListener(IOErrorEvent.IO_ERROR,erreur

); 

Note : Dans la fonction « erreurChargement » nous diffusons un évènement INIT destiné à enclencher un nouveau chargement dans le cas d’un chargement par lot, comme celle d’une galerie. Dans ce cas nous pourrions écouter l’évènement init afin de déclencher le chargement suivant. 

B / Création et gestion d’une animation de chargement. 

Le chargement d’une image lourde (le lecteur flash permet la manipulation d’un bitmap de 2880 pixels de cotés) ou d’un swf contenant des élément bitmap peut prendre un certains temps. Il peut être bienvenu dans ce cas d’afficher une animation de chargement ou d’attente afin de ménager la patience du visiteur. 

Pour ce faire nous aurons besoin d’écouter deux évènements supplémentaires, et d’ajouter une instruction (disparition de l’animation) à notre fonction de fin. 

nous informe du debut du chargement de l’image. Nous insererons donc un ecouteur qui se chargera dans la fonction d’afficher et de centrer notre animation.  

ProgressEvent.PROGRESS quand à lui est une classe d’évènement qui permet en consultant ses propriétés de connaître le nombre de bytes chargés. Nous connaissons également à travers lui le nombre de bytes à charger. En réalisant une simple opération nous pouvons obtenir un decimal entre 0 et 1 que nous pouvons appliquer à l’échelle horizontale (scaleX) d’un clip contenant un graphique en forme de barre. L’évènement est déclenché à chaque chargement d’un nouveau paquet de donnée. Il peut donc créer l’illusion d’une barre animée.  // un clip contenant un rectangle montrant la barre de chargement dans son état final est créé dans la bibliothèque (contient un champ texte : « champ_texte » et le clip du graphique : « barre

». 

On lui donne « BarreChargement » comme identifiant de liaison. 

var barre_load : MovieClip= new BarreChargement();  var container_principal:MovieClip=new container();  addChild(container_principal);

var requete:URLRequest=new URLRequest("");  var chargeur:Loader=new Loader(); 

chargeur.contentLoaderInfo.addEventListener(,chargementTermine); chargeur.contentLoaderInfo.addEventListener(IOErrorEvent.IO_ERROR,erreurCha rgement); 

// ajout d’une ecoute sur le commencement et la progression du chargement chargeur.contentLoaderInfo.addEventListener(,departChargement); chargeur.contentLoaderInfo.addEventListener(ProgressEvent.PROGRESS,chargeme ntEnCours); 

function departChargement(even:Event):void  { 

//centrage de la barre de chargement par rapport à son état final  barre_load.x= container_principal.width / 2 – barre_load.width / 2;  barre_load.y= container_principal.height / 2 – barre_load.height / 2; 

// reduction de l’echelle de la barre à son état minimal barre_load.barre.scaleX=0;  //ajout à la scène 

container_principal.addChild(barre_load);

function chargementEnCours(even:ProgressEvent):void 

{  var ratio_chargement:Number= even.bytesLoaded / even.bytesTotal;  var pourcent:Number= Math.round(ratio_chargement * 100);  var texte:String = pourcent +" %";  = texte; barre_load.barre.scaleX = ratio_chargement; 

function erreurChargement (even: IOErrorEvent):void 

// retrait des ecouteurs 

chargeur.contentLoaderInfo.removeEventListener(,chargementT ermine); 

chargeur.contentLoaderInfo.removeEventListener(IOErrorEvent.IO_ERROR, erreurChargement); 

chargeur.contentLoaderInfo.removeEventListener

(,departChargement);

chargeur.contentLoaderInfo.removeEventListener (ProgressEvent.PROGRESS,chargementEnCours); 

// instanciation d’un clip destiné à remplacer l’image manquante 

var img_defaut : MovieClip = new ImageDefaut(); 

container_principal.addChild(img_defaut);

// j’envoie un signal de fin de chargement 

chargeur.contentLoaderInfo.dispatchEvent(new Event()); 

}  

function chargementTermine(even:Event):void 

// retrait de la barre de chargement : 

container_principal.removeChild(barre_load);

//centrage du media 

chargeur.x= container_principal.width / 2 – chargeur.width / 2;  chargeur.y= container_principal.height / 2 – chargeur.height / 2; 

container_principal.addChild(chargeur);

chargeur.contentLoaderInfo.removeEventListener(,chargementT ermine); 

chargeur.contentLoaderInfo.removeEventListener(IOErrorEvent.IO_ERROR, erreur); 

chargeur.contentLoaderInfo.removeEventListener

(,departChargement);

chargeur.contentLoaderInfo.removeEventListener (ProgressEvent.PROGRESS,chargementEnCours); 

C/ optimisation du swf chargé (scaling )ou de l’image (lissage). 

Nous allons compléter notre code par un processus qui va redimensionner notre média et lui appliquer un lissage si nous détectons qu’il s’agit d’un bitmap. 

Le redimensionnement doit tenir compte du format vertical ou horizontal du media chargé.

Nous devrons donc tenir compte de la hauteur et largeur. 

Le redimensionnement ne s’applique que si le medias est PLUS grand que le conteneur. Aussi nous aurons besoin de savoir si la hauteur de l’image depasse la hauteur du cadre et ainsi de suite.

Nous nous servons pour cela de la classe math qui contient plusieurs méthodes de comparaison de valeur numérique. L’une (valeur_1, valeur_2) renvoie la valeur la plus grande. (valeur_1, valeur_2) renvoie la valeur la plus petite. C’est celle-ci dont nous avons besoin pour notre comportement de réduction. Nous emploierons des ratios de manière à modifier l’échelle plus simplement. 

var ratio_largeur:Number= (container_principal.width-30 )/ chargeur.width;  var ratio_hauteur:Number= (container_principal.height-30) / chargeur.height; 

var ratio_final:Number= (ratio_largeur,ratio_hauteur);  if(ratio_final<1) 

chargeur.scaleX=chargeur.scaleY=ratio_final;

Le lissage est uniquement utilisable sur les objets de type Bitmap. Aussi, si nous voulons garder la capacité de charger aussi des swf, nous aurons besoin d’utiliser une condition en nous servant du mot clé du mot clé « is » permettant de déterminer si l’instance est de tel ou tel type.  Pour effectuer ce test nous aurons besoin de la propriété content de notre instance loader qui contient effectivement l’objet chargé pour la comparer au type bitmap. 

if (chargeur.content is Bitmap) 

Si tel est le cas nous aurons besoin de transformer le contenu en un objet bitmap à par entière. 

if (chargeur.content is Bitmap) 

{  var bmp:Bitmap = Bitmap(chargeur.content);  bmp.smoothing = true; 

Ce procédé est nécessaire lorsque nous manipulons un objet que nous savons être d’un type, mais que flash traite pour raison pratique sous une forme plus générique dont dépend l’objet en question. Par exemple tout objet imbriqué dans un clip peut être récupéré avec la fonction getChildByName. L’objet renvoyé l’est sous la forme DisplayObject dont dépendent l’ensemble des objets affichable. Pour accéder à la propriété text d’un champ texte par ce biais nous aurions également besoin de le transtyper. 

Voici le code intégré à notre fonction de fin de chargement : 

function chargementTermine(even:Event):void 

// Retrait de la barre de chargement : 

container_principal.removeChild(barre_load);

// extraction du contenu :  var contenu : DisplayObject = chargeur.content; 

// Redimensionnement 

var ratio_largeur:Number= (container_principal.width-30 )/ contenu.width;  var ratio_hauteur:Number= (container_principal.height-30) / contenu

.height; 

var ratio_final:Number= (ratio_largeur,ratio_hauteur);  if(ratio_final<1) 

contenu.scaleX = contenu.scaleY = ratio_final; 

// Lissage si bitmap  if (contenu is Bitmap) 

var bmp:Bitmap = Bitmap(contenu);  bmp.smoothing = true; 

//centrage du media 

contenu.x= container_principal.width / 2 – contenu.width / 2;  contenu.y= container_principal.height / 2 – contenu.height / 2;  container_principal.addChild(contenu); 

chargeur.contentLoaderInfo.removeEventListener(,chargementTermine

);

chargeur.contentLoaderInfo.removeEventListener(IOErrorEvent.IO_ERROR,erreur

); 

chargeur.contentLoaderInfo.removeEventListener

(,departChargement);

chargeur.contentLoaderInfo.removeEventListener (ProgressEvent.PROGRESS,chargementEnCours); 

}

2 Compréhension avancée des enjeux posés par le système évènementiel AS3.

Nous avons fait connaissance avec les évènements en programmant nos premiers click souris. Puis nous avons vu que d’autres types d’évènements pouvaient être émis par des processus non graphiques comme les chargements d’image, de fichiers texte.

Le système sur lequel repose l’architecture d’action script 3 est intimement lié avec les évènements. Pour quasiment chaque action que vous souhaiteriez détecter il existera un type d’évènement à écouter. Et si elle n’existe pas il est parfaitement possible de le créer par soi même à l’aide d’une classe d’évènement personnalisée.

Nous allons donc nous intéresser aux écouteurs un peu plus en détail.

Un évènement c’est quoi ?

C’est une classe spécialisée tout comme peuvent l’être une des classes natives de flash comme le MovieClip et SimpleButton. Comme ces classes, un évènement doit être instancié : 

new Event( type_evenement ) ;

Les DisplayObject de flash font cela automatiquement. Un click souris instancie automatiquement la classe d’évènement MouseEvent.

var clicksouris : MouseEvent = new MouseEvent (MouseEvent.Click) ;

Le diffuseur d’évènements

Continuons avec l’exemple du clic de souris sur notre clip. Nous avons vu que flash instanciait automatiquement cet évènement. Ce n’est pas tout d’instancier il faut encore l’émettre pour qu’il puisse être entendu.  Il lui faut enfin diffuser cette instance au travers d’une autre classe spécialisée : EventDispatcher.

Grace à cette classe il est possible de s’abonner pour l’écoute de tous les types d’évènements avec la méthode addEventListener (type_a_ecouter,fonction_a_ecouter) proposée par cette classe.

Grace à cette classe, il est possible de diffuser tout les types d’évènements sans exception. Une méthode dispatchEvent(instance_evenement_a_diffuser).

Une instance donnée d’EventDispatcher est donc le point de jonction entre un évènement et l’éventuel écouteur qui peut s’être abonné à cette même instance.

Il se trouve que les objets interactifs de flash « ont » tous dans leurs classes respectives l’ensemble des fonctions qui forment la classe EventDispatcher. L’objet et le diffuseur sont donc imbriqués l’un dans l’autre. 

C’est donc l’objet lui-même, le sprite, le MovieClip, (…), qui est en mesure d’émettre un évènement plutôt que par le biais d’une instance d’EventDispatcher. C’est ce qui nous permet également de nous abonner directement à l’objet à l’aide de sa fonction EventDispatcher. Et de le cibler avec la propriété target de l’événement émis

Les évènements et la liste d’affichage

Destruction des objets et écouteurs d’évènement.

Lorsque un objet de la liste d’affichage est retiré de celle-ci, il n’est pas pour autant détruit. Il faut pour cela passer sa référence à null :

objet = null ;

Les objets entreposés dans le cache peuvent à partir d’un certain poids et nombre encombrer l’application et ralentir son déroulement. Pour éviter ce cas de figure flash détruit du cache le objet éligibles. 

Pour être éligible à la destruction (c.f. classe GarbageCollector) un objet doit remplir deux critères :

-    Il ne doit pas se trouver dans la liste d’affichage :  Le fait de passer la variable à null ne détruit pas véritablement l’objet, il ne fait que découpler la variable et l’objet auquel elle fait référence.

-    Aucun écouteur ne doit faire référence à cet objet : Si un clip, par exemple, est écouté pour un évènement de type MouseEvent.CLICK, qu’il est retiré de la liste, et que sa valeur est passée à null. Il résidera malgré tout dans la mémoire, tout en étant plus accessible, l’empéchant d’être éliminé si l’application a besoin de faire un peu de place. Il est particulièrement recommandé de se préoccuper de cette question lors d’affichages multiples d’un même objet en grand nombre comme une galerie.

Les écouteurs d’évènement sont tous stockés dans un tableau général. Ce tableau n’est pas accessible depuis le code. On peut exploiter deux façons de traiter cette question :

1 ) On passe la variable useWeakReference à « true », dans notre déclaration d’écoute :

Objet.addEventListener(typeEcouteur,fonctionQuelqonque,useCapture,Priorité, useWeakReference) ;

Objet.addEventListener( MouseEvent.CLICK, fonctionQuelqonque, false, 0, true) ;

Ce paramètre à pour effet de placer l’écouteur en question dans un tableau secondaire qui annule l’obligation de désactiver l’écouteur pour rendre l’objet écouté éligible à la destruction. Il ne règle pour autant pas le problème dans tout les cas. Par exemple nous pouvons avoir besoin de retirer un objet de la liste d’affichage et d’avoir la certitude qu’il ne sera pas détruit parce que nous en aurons l’utilité plus tard.

Cela, de surcroit, n’est pas une solution propre puisque subsisteront dans la mémoire de l’application les écouteurs. 

2) On intègre à nos classes graphiques une gestion des écouteurs à l’aide des évènements émis lors de l’ajout et du retrait de la liste d’affichage. C’est bien sur le moyen le plus propre pour gérer les écouteurs.

Le modèle de classe suivant peut être utilisé :

package  {

      import flash.display.Sprite;        import flash.events.Event;     import flash.events.MouseEvent;

      public class Container extends Sprite

      {

            Private var layer_decor :Sprite = new Sprite () ;

Private var layer_controles :Sprite = new Sprite () ;

            public function Container () 

            {

// la fonction ajoutScene se declenchera lorsqu’une instance de

// cet objet sera ajouté à la liste d’affichage

                  this.addEventListener(Event.ADDED_TO_STAGE, ajoutScene);

                  // Je dessine une forme :

                  layer_decor.graphics.beginFill(0x000000, .5);          layer_decor.graphics.drawRect(0, 0, 200, 200);

                  //je crée un comportement de bouton pour l’instance de cet objet

                  this.buttonMode = true;

                  this.useHandCursor= true;

   //je désactive la sensibilité souris des objets se trouvant au

// dessus de la racine de mon clip. 

                  layer_decor.mouseEnabled = false;

                  layer_controles.mouseEnabled = false;

                  this.addchild(layer_decor);

                  this.addchild(layer_controles);

                  // j’installe mon ecouteur principal              this.addEventListener(MouseEvent.CLICK, clique);

            }

            private function ajoutScene (even:Event):void

            {

   // Une fois sur le scene, je pose un ecouteur sur l’évènement

                  // retrait de la scène.

this.addEventListener(Event.REMOVED_FROM_STAGE, retraitScene);

   // Si je veux automatiquement supprimer l’écouteur : this.removeEventListener(Event. ADDED_TO_STAGE, ajoutScene);

            }

            public function retraitScene (even:Event = null):void

            {

this.removeEventListener(Event.REMOVED_FROM_STAGE, retraitScene);

            }

            public function clique (even:MouseEvent = null):void

            {

                  // l’objet se retire de la liste d’affichage

this.parent.removeChild(this);

            }

      }

}

On trouve également les évènements Event.ADDED et Event.REMOVED. Respectivement , ils sont émis lors de l’ajout ou du retrait d’un objet parent même si celui-ci n’appartient pas à la liste d’affichage.

L’autre utilité de ce fonctionnement est de protéger l’application contre une tentative d’accès d’un objet d’affichage à des propriétés qui sont instruites seulement à partir du moment où l’objet se trouve dans la liste d’affichage.

Ces propriétés sont les suivantes : 

Objet_graphique.stage : Permet d’accéder à la racine absolue de notre application.

: Permet d’accéder à la racine relative de notre application. Objet_graphique.parent : Permet d’accéder au parent de l’objet.

Si l’objet n’est pas dans la liste d’affichage ces propriétés renvoient « null ». Et l’application génère une erreur.              

Parcours d’un évènement écran dans la liste d’affichage : L’utilisation avancée des évènements écrans (useCapture, bubbling).

Notre visite de l’actionscript nous pousse à nous intéresser de plus près à un aspect particulier du modèle évènementiel. Une nouvelle notion à ingérer mais qui va nous aider à simplifier notre code : 

Nous l’avons vu précédemment. L’utilisation des écouteurs dans ActionScript 3 nécessite une gestion rigoureuse si l’application est un tant soit peu complexe. Il n’est donc pas question de placer des écouteurs dans tous les bouts de code sans savoir ce qu’il adviendra de ceux-ci. Une des fonctionnalités du nouveau modèle évènementiel permet une économie de moyen, qui va s’avérer fort utile dans l’optimisation des nos applications.

Chaque émission d’évènement par une instance graphique de la liste d’affichage, parcourt l’ensemble de la liste d’affichage depuis l’objet Stage jusqu’à l’objet émetteur puis de cet objet à l’objet Stage. Il y a donc trois phase de propagation. Lorsque l’évènement va du Stage à l’objet ciblé, lorsqu’il a atteint cet objet, puis lorsque qu’il repart de l’objet à la racine de l’animation.

Il faut encore répéter que ce modèle n’est pas valable sur des objets non affichables où l’évènement ne se propage pas à travers l’animation.

La phase « at target »

Lorsque l’on écoute un évènement sans nous occuper des paramètres supplementaires de la méthode addEventListener nous écoutons en fait la phase at_target décrite par le code (2) 

Nous gardons l’architecture décrite dans le shema ci-dessus et faisons appel à la propriété eventPhase de l’instance d’évènement transmis à la fonction :

Objet_4.addEventListener(MouseEvent.CLICK,ecouteClic);

function ecouteClic(even:MouseEvent):void

{ trace("phase en cours : "+ (even.eventPhase); // Renvoie 2

(at_target);

trace("cible (target) : "+ (); // Renvoie le nom

d’occurrence du clip cible : « objet_4 »;

trace("cible (currentTarget) : "+ (); // Renvoie le nom d’occurrence du clip cible : « objet_4 »;

}

Cette méthode d’écoute est celle que nous connaissions dans actionscript 2. C’est la méthode la plus basique. Mais loin d’être la plus souple.

La phase de capture

Cette méthode est particulièrement intéressante pour l’écoute d’objets multiples de même type. Nous savons donc que pendant la phase de capture, l’évènement parcourt chaque objet de la hiérarchie de l’objet visé par l’évènement.

Il traversera donc chacun  des parents. L’utilisation du paramètre useCapture de la méthode objet_parent.addEventListener (type,ecouteur,useCapture)  va nous permettre d’intercepter l’évènement pendant ce parcours.

Nous choisissons donc de placer un évènement sur l’objet « container » de notre schéma 

container.addEventListener(MouseEvent.CLICK,ecouteClic,true);

function ecouteClic(even:MouseEvent):void

{

trace("phase en cours : "+ (even.eventPhase); // Renvoie 1 (capture);   trace("cible (target) : "+ (); // Renvoie le nom d’occurrence du clip cible : « objet_4 »;

      trace("cible (currentTarget) : "+ (); // Renvoie le nom d’occurrence du clip sur lequel est placé l’écouteur : « container »;

}

Nous voyons dans l’exemple que even.currentTarget définit l’objet ecouté par la méthode. Et que l’objet que nous avons cliqué lui est designé par la propriété even.target. 

Nous n’avons nulle part indiqué que c’est l’objet « objet_4 » qui doit être écouté, et pour cause ! C’est en fait toute la hierarchie descendante de l’objet écouté qui déclenchera la fonction. Dans notre exemple, indistinctement, les 4 objets enfants déclencheront la fonction dès lors où ils seront cliqués.

Nous voyons dès lors l’intérêt de placer un seul écouteur en amont, par exemple, sur un container de vignettes dans une galerie photo ! Au contraire, l’utilisation peut être très fastidieuse si la hiérarchie descendante contient beaucoup d’autres types d’objets et si l’on a pas pris soin de désactiver la sensibilité souris de ces derniers à l’aide de la méthode mouseEnabled des DisplayObject

La phase de bouillonnement

La dernière phase dite Bubbling ou bouillonnement ressemble fort à la précédente dans ses caractéristiques : l’écoute concerne alors  la hiérachie descendante de l’objet sur lequel est placé l’écouteur.

La fonction est alors déclenchée dans un troisième temps. Il suffit pour mettre l’écoute en place de viser l’objet parent comme dans la phase de capture, mais cette fois ci de laisser le paramètre useCapture sur sa position par défaut. Ici en l’occurrence nous n’y faisons pas référence dans la méthode.

container.addEventListener(MouseEvent.CLICK,ecouteClic);

function ecouteClic(even:MouseEvent):void

{

trace("phase en cours : "+ (even.eventPhase); // Renvoie 3 (bubbling);      

            trace("cible (target) : "+ (); // Renvoie le nom d’occurrence d’un des clip de la hierarchie déscendante : « objet_4 » par exemple;

            trace("cible (currentTarget) : "+ (); // Renvoie le nom d’occurrence du clip sur lequel est placé l’écouteur : « container »;

}

Les occasions d’utiliser ce dernier comportement restent toutefois assez rares, au contraire de la phase de capture très utilisée.

Utilisation des fonctions et propriétés proposées par les évènements

Toutes les occurrences d’évènements possèdent au moins ces propriétés en tant qu’elles héritent toutes de la classe mère Event : 

target : définit l’objet éméteur. 

Dans le cas de l’écoute d’un objet graphique nous pourrons accéder à chacune de ses propriétés et méthodes au travers de cette propriété.

currentTarget : définit l’objet écouté.

eventPhase : Indique si l’événement est en phase de capture (1) de at_target (2) ou de remontée(3).

type : envoie le type de l’événement utilisé.

Afin d’assouplir encore le code de nos applications, au lieu de retirer un écouteur d’évènement d’un objet défini, d’un évènement d’un type défini, nous pouvons utiliser à la place la syntaxe suivante en se servant des propriétés accessibles dans la fonction :

container.addEventListener(MouseEvent.CLICK,ecouteClic);

function ecouteClic(even:MouseEvent):void

even.target.removeEventListener(,ecouteClic); //revient exactement au meme que : 

      // container.removeEventListener(MouseEvent.CLICK,ecouteClic); }

L’intérêt de ce comportement réside dans le fait que l’objet écouté n’a plus à être défini dans la classe où s’effectue la fonction.  Il est possible donc de déposer cette ligne partout ou un ecouteur à besoin d’être désactiver en modifiant uniquement la référence de fonction.

Stopper la progression événementielle.

D’autres méthodes utiles se trouvent dans l’instance d’un évènement. Nous citerons en particulier les méthodes d’arrêt d’un évènement stopPropagation(), qui empêche l’évènement de se transmettre au delà de l’enfant direct d’un objet écouté en phase de capture.

La méthode stopImmediatePropagation() quand à elle, stoppe la propagation événementielle à l’objet écouté lui-même. Utile en cas de système de gestion d’un menu.

Installer un diffuseur d’évènement sur une classe non graphique. 

Deux possibilités permettent à une instance de classe d’émettre des évènements sans être pour autant une classe héritant de DisplayObject. Nous n’en développerons ici qu’une. La première possibilité sur laquelle nous ne nous étendrons pas est d’utiliser un mécanisme d’implémentation de la classe Event sur notre classe. 

Sommairement, il s’agirait de déclarer que nous sommes fidèles au modèle de la classe EventDispatcher, en en reprenant toute les fonctions (interface) et de placer dans notre classe l’ensemble des fonctions qui composent la classe de diffusion. L’avantage de cette méthode est que l’instance de la classe et le diffuseur ne font qu’un, simplifiant ainsi l’utilisation de target.

Il nous sera facile alors de récupérer des informations dans l’objet sur lequel est placé l’écouteur.

L’autre solution nettement plus « simple » à l’usage est de créer dans notre classe une instance d’EventDispatcher La méthode implique que la propriété target de l’instance d’évènement ne renvoie que vers le diffuseur lui même et ne permet donc pas d’accéder aux propriétés et méthodes de la classe qui le supporte. 

Si c’est possible de directement faire hériter la classe de EventDispatcher. De cette façon le diffuseur et l’instance de classe sont les mêmes, et la propriété target nous permet directement d’accéder au données de notre objet.

Exemple : une classe de chargement.

Nous l’avons vu précédemment un chargement d’image ou de swf est une opération coûteuse en lignes de code. Pourquoi ne pas placer ces lignes dans une classe que nous pourrions utiliser à loisir en une vingtaine de lignes comprenant la barre de chargement :

package utilitaires 

{

import flash.display.Bitmap;

import flash.display.Loader;

import flash.events.Event; 

import flash.events.EventDispatcher;

import flash.events.ProgressEvent; 

import flash.events.IOErrorEvent;

import .URLLoader;

import .URLRequest;

import flash.display.LoaderInfo;

import flash.display.DisplayObject;

public class ChargeurGenerique extends EventDispatcher

{

// le loader est accéssible à toutes les fonctions

private var chargeur:Loader;

// La variable receuille le produit du chargement

private var contenuCharge:DisplayObject;

// raccourci vers le diffuseur de l'objet loader

public var infoLoad:LoaderInfo;

// image remplaçante en cas d'erreur de chargement

private static var image_defaut:DisplayObject;

// pourcentage du chargement.

public var ratio_chargement:Number = 0;

public function ChargeurGenerique() 

{

      // instanciation du chargeur

      chargeur = new Loader();

      // mise en place des écouteurs

      infoLoad = chargeur.contentLoaderInfo; 

);

       infoLoad.addEventListener ( , chargementTermine

);

       infoLoad.addEventListener ( , redirigeEvenement

infoLoad.addEventListener ( ProgressEvent.PROGRESS, calculRatio );

      infoLoad.addEventListener ( IOErrorEvent.IO_ERROR, echecChargement );  addEventListener ( Event.REMOVED_FROM_STAGE, annulationChargement );

            } 

// on donne la possibilité de signifier une image par défaut via une fonction statique :

// - l'image sera disponible pour toutes les instances de

ChargeurGenerique.

// - Elle sera appelée de cette manière ChargeurGenerique.imageDefaut = un_Display_Object;

public static function set imageDefaut(image:DisplayObject):void

{image_defaut = image; }

public static function get imageDefaut():DisplayObject { return image_defaut; }

public function get contenu():DisplayObject { return contenuCharge; }

            public function annulationChargement():void

            {

                  chargeur.unload();             chargeur.close();

                  desactivation();

            }

// cette fonction regroupe tout les écouteur à annuler. De cette manière je n'ai pas à dupliquer ces lignes de code.

private function desactivation ():void

{

      infoLoad.removeEventListener ( , chargementTermine);  infoLoad.removeEventListener ( , redirigeEvenement );  infoLoad.removeEventListener ( ProgressEvent.PROGRESS, calculRatio ); infoLoad.removeEventListener  ( IOErrorEvent.IO_ERROR, annulationChargement

);

      removeEventListener ( Event.REMOVED_FROM_STAGE, desactivation );

            }

// les évènements de progression sont redirigés et viennent alimenter une variable publique indiquant le ratio de chargement. 

            private function calculRatio ( even:ProgressEvent ):void 

            { 

                  ratio_chargement =   even.bytesLoaded/even.bytesTotal;              this.dispatchEvent(even);

            }

            // les évènements diffusés par LoaderInfo sont redirigés sur cet objet pour permettre l'écoute.

            private function redirigeEvenement ( even:Event ):void 

            { 

                  this.dispatchEvent(even);

            }

            public function charger ( pURL:String ):void 

            { 

                  //création de la requète                  var requete:URLRequest = new URLRequest()

                  = pURL;             // debut du chargement

                  ( requete );

            }

            // Le chargement à bien executé

            private function chargementTermine ( even:Event ):void 

            {

                  // retrait des écouteurs

                  desactivation();

                  // mise à disposition du contenu par l'objet            contenuCharge = even.target.content;                  // Si l'objet chargé est un bitmap, on le lisse                if(contenuCharge is Bitmap)

                  {

                        // transtypage pour acceder à la fonction                     var bmp:Bitmap = Bitmap(contenuCharge);                   // fonction de lissage.                      bmp.smoothing = true;

                  }

                  // Envoi de l'évènement complete

                  this.dispatchEvent(new Event(Event.COMPLETE));

            } 

            private function echecChargement ( even:Event ):void 

            { 

                  trace ("erreur de chargement");             // si l'image par défaut existe

                  if (image_defaut != null)

                  {

                        desactivation();

                        contenuCharge = image_defaut;

                        this.dispatchEvent(new Event(Event.COMPLETE));

                  }

                  else

                  dispatchEvent(even.clone())

            }

      }

}

Depuis la classe appelante :

import utilitaires.*

ChargeurGenerique.imageDefaut = new Erreur(); var chargeur:ChargeurGenerique = new ChargeurGenerique(); chargeur.addEventListener(Event.COMPLETE,finChargement); chargeur.addEventListener(ProgressEvent.PROGRESS,progression);

chargeur.charger("");

private function progression(even:ProgressEvent):void

{

      trace(chargeur.ratio_chargement)

      mc_barre.scaleX = chargeur.ratio_chargement;

private function finChargement(even:Event):void

{

      removeChild(mc_barre);

      chargeur.removeEventListener(Event.COMPLETE,finChargement)       var ratio:Number =

(stage.stageWidth/chargeur.contenu.width,stage.stageHeight/chargeur

.contenu.height);

      chargeur.contenu.scaleX = chargeur.contenu.scaleY = ratio;  addChild(chargeur.contenu); }

Créer ses propres évènements et transmettre des informations personnalisées.

Parfois nous pouvons être amenés à avoir besoin de transmettre un message spécifique, cela peut aussi faire partie d’un certain confort. La gamme des évènements dans flash est très importante, mais un évènement spécifique destiné à acheminer un message, ou un objet quel qu’il soit pourrait simplifier les choses dans nombre des cas. 

Par exemple, classiquement, lors de la création d’un diaporama, nous aurions besoin de précharger les trois premières images avant de lancer le diaporama, de façon à optimiser le fonctionnement. Un évènement personnalisé nous faciliterait la tache et pourrait transmettre les information de l’application sous un format unique ce qui nous éviterai la multiplication des fonctions.

Je vous propose donc une classe d’évènement très générique : EvenementPerso. Elle permet de faire passer n’importe qu’elle chaine de caractère, et un objet générique secondaire. En somme cette classe très pratique peut acheminer n’importe quel message et n’importe quel objet actionscript du plus simple au plus complexe à travers n’importe que EventDispatcher.

package utilitaires 

{

      import flash.events.Event;

      public class EvenementPerso extends Event 

      {

            public static const TRANSIT_MESSAGE:String = "use_message";

            private var _message:String = "";

            private var _objet:Object;

            public function EvenementPerso(type:String,

bubbles:Boolean=false, cancelable:Boolean=false,pmessage:String="") 

            { 

                  _message = pmessage;            super(type, bubbles, cancelable);

            } 

            public function get objetSecondaire():Object { return _objet; }    public function set objetSecondaire(obj:Object ):void{ _objet = obj; }

             public function get message():String { return _message; }

            public override function clone():Event 

            {

   var even:SimpleEvent = new SimpleEvent(type, bubbles, cancelable, _message);

even.objetSecondaire = _objet;    return  even;

            } 

            public override function toString():String 

            { 

                  return formatToString("SimpleEvent", "type", "bubbles",

"cancelable", "eventPhase"); 

            }

      }

Utilisation comme évoqué plus haut mon diaporama à chargé ses trois images. Je souhaite le faire savoir à la scène principale pour qu’il arréte l’animation de prechargement. J’ai aussi un événement à envoyer lorsque le diaporama est fini. Je peux également faire savoir quelle photo est en cours de diffusion pour afficher le titre par exemple. Trois types d’évènement distincts mais qui pourraient tous transiter par la même classe d’évènement. Voire par le même type d’évènement.

Sur celle-ci j’ai placé un écouteur sur le diaporama :

diaporama.addEventListener(EvenementPerso.TRANSIT_MESSAGE,evtDiapo);

function evtDiapo(even:EvenementPerso):void

{

      if(even.message==”tampon_charge”)

      {

            // retrait de l’animation de préchargement et affichage du titre ;

            titre = String(even.objetSecondaire) ;

      }

      else if ( even.message == “ fin_diapo“)

      {

            diaporama.pause();

gotoAndPlay(“fin”)

      }

}

Depuis le diaporama maintenant, depuis les fonctions appropriées je pourrais envoyer l’évènement : 

var evenement :EvenementPerso = new

EvenementPerso(EvenementPerso.TRANSIT_MESSAGE,false,false,”tampon_charge”); evenement.objetSecondaire = paramXML.image[index_en_cours].titre; this.dispatchEvent(evenement);

Plus loin le même évènement sera produit pour la fin du diaporama ou à chaque nouvel affichage de photo.

La fonction evtDiapo sera déclenchée par l’émission de l’évènement. La propriété message nous servira a discriminer l’étape et répondre par le comportement approprié.

En conclusion nous avons pu observer quelques uns des principaux aspects des évènements dans flash. Une fois aguerris à ce système évènementiel vous n’aurez plus aucune difficultés pour synchroniser vos applications et proposer de vraies scénarisations de vos applications. Notamment vous serez aussi à l’aise si vous vous intéressez à javascript et jquery car le système évènementiel en vigueur est le même. Vous serez même surpris d’y trouver des fonctions telles que addEventListener !


Partie 2

Animer des éléments : L’utilisation de TweenMax.

Introduction

L’un des premiers intérêts de flash est la possibilité de créer des animations utilisant des effets réalistes ou étonnants, la limite étant celle de l’imagination du créateur et des capacités des machines contemporaines. Mais, autant il est simple de créer une animation via le scénario, autant l’utilisation des classes d’animations (Animator et Tween) peuvent s’avérer fastidieuse et surtout peuvent présenter des bugs important, voire dramatique pour le bon fonctionnement de l’animation : Il peut en effet arriver que les animations via la classe Tween peuvent s’arrêter avant leur terme… Par ailleurs la classe Tween ne peut être utilisée que dans un environnement gérée par l’interface auteur ( par opposition avec la création via le kit de développement flex ). 

Pour cette raison nous allons donc utiliser une applications d’animation externe et open source extrêmement pratique et simple d’utilisation : TweenMax de la société Greensocks. TweenMax est une classe d’animation permettant d’agir sur les propriétés principales d’un objet d’affichage, mais qui possède aussi des propriétés avancées permettant d’animer filtres, volume sonore,  proposant aussi une alternative simple à des animations complexes

(interpolations en courbes, interpolations de couleurs, modifications du texte). Autre intérêt de cette classe : il en existe des versions pour as2 et as3.

D’autres classes d’interpolations sont aussi disponibles en open source, telle Tweener de caurina. A noter d’ailleurs que cette dernière possède des distribution dans la langage javascript permettant ainsi des effets d’interpolation directement dans le html… 

Je fais le choix de vous faire découvrir TweenMax pour différentes raison : D’une par la classe est développée fréquemment et la dernière version est très récente. Et en particulier : La classe contient quelques propriétés et méthodes spéciales n’existant pas dans Tweener.  Enfin TweenMax semble être plus performante en terme de rendu (plus fluide). Notamment dans l’utilisation de nombreuses interpolations. 

Enfin une fois habitué à TweenMax, vous n’aurez aucune difficulté à vous adapter à Tweener si besoin en est. Les deux systèmes sont particulièrement similaires dans leur utilisation. Mais certaines fonctionnalités avancées comme les actions sur un champ texte seront payantes chez greensock et on trouvera une fonctionnalité (simple) sur le texte chez caurina qui elle sera incluse dans le paquetage. Il vous faut en vertu de cela pratiques une veille technique afin de pouvoir utiliser le moteur approprié selon vos besoins.

Nous allons maintenant en découvrir les différents aspects du fonctionnement. Avant cela nous allons nous intéresser à un type d’objet particulier : les « classes » statiques. 

Les classe statiques

Le terme statique est un attribut qui peut être placé devant une fonction ou une propriété. Il correspond au mot clé « static ».

Cet attribut signifie, s’il s’agit d’une propriété,  que la valeur qu’elle contient sera partagée par l’ensemble des processus qui l’utiliseront. Pour une propriété normale, une propriété donnée ne sera consultable qu’une fois l’objet instancié. Et il ne sera disponible que dans l’instance en question. Si la propriété est statique elle sera disponible pour chaque instance de la classe quel que soit son ordre d’apparition.

A quoi ça sert ? 

C'est la seule possibilité de conserver une ou des valeurs quelles que soit l’endroit du code où on les utilise sans avoir à les transmettre d’objet en objet ou via le système évènementiel. Cela se révèle pratique, par exemple pour conserver les informations d’un xml externe pour une galerie photo. D’une manière générale, dès qu’il est question de stocker une données ou un ensemble de données et de la ou les rendre accessibles, de rendre les modifications quel que soit l’objet qui l’emploie.

La classe TweenMax offre deux possibilités pour la création d’interpolation : Il est possible soit de créer une instance soit d’employer les méthodes statiques. En l’occurrence, pour appeler l’une de ses fonctions ou paramètre on utilisera le nom de la classe suivi de la méthode demandée : 

(objet,3,{x : 0});

Soit nous pouvons récupérer l’instance de l’interpolation et l’utiliser plusieurs fois au cours de l’animation. Ainsi il est possible d’utiliser ces trois notations selon son besoin de contrôle sur l’interpolation et ses habitudes de programmation :

var myTween:TweenMax = new TweenMax(mc, 1, {x:100});  (mc, 1, {x:100}); 

var myTween:TweenMax = (mc, 1, {x:100});

Nous avons donc la possibilité d’adapter la complexité du code en fonction des besoins réels, ce qui peut représenter un gain de temps non négligeable en production.

On utilisera des instances par exemple dans le cas ou notre animation aurait besoin d’être modifiée en cours d’interpolation par un choix laissé à l’utilisateur par exemple. 

myTween.updateTo({x :mouseX,y :mouseY},true);

Avant d’utiliser TweenMax nous devons passer par son installation au sein de notre environnement de programmation.

Installation : 

Première chose à faire, récupérer la dernière version 

Le dossier contient tout le nécessaire pour l’utilisation et la documentation ainsi qu’un générateur de code, très utile pour commencer à se servir de la classe (dossier « demos »). Vous trouverez aussi les principales en ligne sur la page ci-dessus.

Activation des classes dans le script : 

Deux possibilités : 

Une fois l’archive décompressée, nous récupérons le dossier greensock-as3 puis : 

-      Nous le plaçons à la racine de l’animation que nous allons créer pour une utilisation ponctuelle. Si vous utilisez flash CS3, il est nécessaire de préciser l’emplacement dans les paramètres d’as3 : 

Fichier / paramètres de publication / onglet flash / paramètres d’AS3 puis cibler le dossier greensock-as3.

-      Soit nous pouvons l’installer dans le logiciel de manière permanente ce qui permettra ensuite de la réutiliser au gré de nos applications.

Nous le plaçons dans un dossier à part, dans un emplacement stable puis nous allons à l’emplacement : 

Modifier / Préférences / ActionScript / Paramètres d’acionScript 3 / puis cibler le dossier greensock-as3.

Utilisation.

Nous voici maintenant avec la possibilité d’utiliser la classe dans notre application.

Première chose a faire est d’importer les paquetages nécessaires  pour des interpolations de bases : 

import com.greensock.*; 

import com.greensock.easing.*;

Utilisations des paramètres de TweenMax.

Nous allons effectuer une première interpolation à l’aide de la méthode statique dévolue (objet, temps,{parametres}).

La méthode prend trois arguments minimum :  -       l’objet qui va subir une interpolation

-    la durée de l’interpolation en secondes 

-    un objet - d’où la notation entre crochets et « : » remplacant l’opérateur « = »  -  contenant, entre autre, les propriétés de l’objet qui doivent être interpolées et leur valeur finale. Entre autre, car cet objet peut contenir nombres d’autres paramètres comme une fonction de rappel en début, fin ou à l’exécution de l’interpolation. Il n’est pas nécessaire de placer les propriétés dans un ordre particulier.

( objet_a_interpoler, duree_secondes, {prop :valeur, prop2 :valeur} ) ;

La syntaxe de cette classe est la même qu’il s’agisse d’as2 ou as3, seules les nom des propriétés doivent être inscrites selon la version d’actionscript.

Nous créons un clip sur la scène auquel nous donnons un nom d’occurrence.

Interpolation d’une propriété, de deux… Comme on le voit, une seule interpolation permet la mise en place de multiples changements. 

Utilisation des fonctions spéciales : l’exemple des courbes.

L’un des aspects le plus intéressants de TweenMax est de pouvoir modifier des paramètres qui dans le strict cadre du langage d’adobe demanderaient des lignes et des lignes de code pour leur mise en oeuvre. 

L’une d’elle en particulier va nous permettre de faire suivre à l’objet un trajet non linéaire vers son point d’arrivée, à l’aide des courbes de Bézier. Ce qui peut s’identifier à l’utilisation d’un guide dans le logiciel  adobe Flash ®.   

Pour avoir accès à ces fonctions spéciales il faudra parfois les activer avant de s’en servir. Cela se fait au sein d’une fonction unique prenant un tableau en paramètre.  

Pour commencer dans le moteur d’interpolation. On importe donc le répertoire des plugins : 

import com.greensock.plugins.*;

et à la tête du code on active le plugin concerné (ici les trajectoires via bezier et l’ombre portée).

TweenPlugin.activate([BezierPlugin , DropShadowFilterPlugin]);

Il est possible maintenant d’utiliser ces fonctions, sans quoi un message d’erreur sera émis lors de la compilation.

Les trajectoires

TweenMax dispose de plusieurs possibilités pour faire emprunter une trajectoire particulière.  Les deux emploient les courbes de bezier. La première classiquement courbera l’axe menant de la propriété de coordonnée de départ à celle d’arrivée. Vous pouvez vous rendre à la page de TweenMax. L’exemple vous fournira un code utilisable dans votre application.

 « Tweener » propose une petite animation très bien faite, dont vous pourrez vous servir pour créer vos premières trajectoires via ce moteur. 

La seconde possibilité propre à TweenMax : bezierThrough (littéralement « bezier à travers »)  est de faire passer l’objet précisément par les coordonnées fournies.

Les courbes sont définies à travers les coordonnées des points de contrôles assemblées dans un objet, le ou les objets sont insérés dans un tableau. 

( objet_a_interpoler, duree_secondes, {x :100,y ,200, bezier :

[{ x : 50, y : 23 }, { x : 510, y : 213 }, { x : 40, y : 13 }] } ) ;

Complémentairement il est possible aussi d’orienter automatiquement l’objet dans l’axe de la trajectoire grâce à orientToBezier:true . Il suffit de rajouter la commande dans les propriétés de l’animation :

( objet_a_interpoler, duree_secondes, {x :100,y ,200, bezier :

[{ x : 50, y : 23 }, { x : 510, y : 213 }, { x : 40, y : 13 }], orientToBezier:true } ) ;

Enfin signalons la possibilité de faire suivre à l’objet une succession de point de coordonnées lors de l’interpolation avec l’objet LinePath2D. L’interpolation suivra alors une trajectoire rectiligne entre les coordonnées fournis.

Création d’une animation complexe.

Nous savons comment créer une interpolation. Il arrivera fréquemment qu’une seule animation ne soit pas suffisante, nous aurons besoin d’une seconde qui devra s’enchaîner à une première. Il existe pour cela deux possibilités. La première est à réserver à un enchaînement d’interpolations simple. La seconde permet un contrôle avancé des interpolations et de leurs déroulements. Il est possible aussi d’utiliser les deux simultanément a différentes fins.

Delay 

L’ajout de du paramètre delay dans les propriétés de notre interpolation est la méthode la plus simple pour synchroniser  plusieurs instances. « delay » accepte une valeur en secondes. L’interpolation sera mise en attente jusqu'à ce que le delai indiqué en secondes soit atteint. On place simplement la propriété au sein de l’objet du second paramètre. Dans l’exemple suivant, la deuxième interpolation commence 2 secondes après que la première se soit achevée :

( objet_a_interpoler, 3, {x :100,y ,200, bezier : [{ x :

50, y : 23 }, { x : 40, y : 13 }], orientToBezier:true } ) ; ( objet_a_interpoler, 1, {x :500,y ,400, bezier : [{ x : 50, y : 23 }], orientToBezier:true, delay :5 } ) ;

Utilisation des fonctions de contrôle (onComplete, onStart, onUpdate).

Les fonctions de contrôle permettent d’exécuter un bloc de code en début (onStart), ou fin (onComplete) d’animation ainsi qu’a chacun de ses rafraîchissements (onUpdate).

Le système d’évènements lié à TweenMax est plutôt hérité de l’ancienne version d’actionScript. Aussi on pourra trouver diverses écritures correspondant à un passage de fonction en paramètre.

( objet_a_interpoler, 1, {x :500,y ,400, bezier : [{ x : 50, y : 23 }], orientToBezier:true,

onComplete :function() :void{trace(« interpolation terminée »)} } ) ;

Conseil : nous arrivons a des paramétrages sui deviennent de plus en plus long à écrire. Pour clarifier votre code, utilisez les retour à la ligne et l’indentation, ainsi l’interpolation précédente sera bien plus digeste présentée de cette manière, nous voyons au passage que le passage à la ligne n’est pas pris en compte par le code comme une fin d’instruction puisqu’il est ici précédé d’une virgule :

TweenMax.to ( objet_a_interpoler,

 1,

 { x :500, y , 400, 

bezier : [{ x : 50, y : 23 }], orientToBezier:true,  onComplete :function() :void

{ trace(« interpolation terminée ») }

) ;

Il est aussi possible bien sur d’ecrire sa fonction en dehors des paramètres. C’est même à conseiller pour la lisibilité des paramètres de l’interpolation : 

( objet_a_interpoler, 1, {x :500,y ,400, bezier : [{ x : 50, y : 23 }], orientToBezier:true, onComplete : finInterpolation } ) ;

 function finInterpolation() :void{trace(« interpolation terminée »)} ;

Dans le cadre d’une classe on peut bien sur mettre cette fonction dans le corps de celle-ci :

private function finInterpolation() :void{trace(« interpolation terminée »)} ;

On se trouve dans ce cas dans l’impossibilité d’accéder à l’interpolation à moins qu’une variable declarée dans le corps de la classe lui soit attribuée. 

Mais il existe aussi deux autre possibilités : Soit celle de pouvoir passer des paramètres rassemblés dans un tableau au travers des propriétés : onStartParams, onUpdateParams, onCompleteParams.

var interpolation : TweenMax  = ( objet_a_interpoler, 1, {x :500,y ,400, bezier : [{ x : 50, y : 23 }], orientToBezier:true, onComplete : finInterpolation, onCompleteParams:[5, mc]});

function onFinishTween(param1:Number, param2:MovieClip):void { trace("The tween has finished! param1 = " + param1 + ", and param2 = " + param2);}

Soit si l’on veut rester à la même notation que celle du système évènementiel AS3 :

On utilise dans ce cas la propriété onCompleteListener ou onStartListener etc… En donnant le nom de la fonction. Dans ce cas seulement, on retrouve la notation propre d’as3 en terme d’évènement ;

Un evenement  de type TweenEvent sera émis par l’interpolation ( penser à importer le paquetage com.greensock.events).

import com.greensock.*; import com.greensock.easing.*; import com.greensock.events.*

private function declencher():void 

{ var interpolation : TweenMax  = ( objet_a_interpoler, 1, {x :500,y ,400, bezier : [{ x : 50, y : 23 }], orientToBezier:true, onCompleteListener : finInterpolation});

}  

private function finInterpolation (even:TweenEvent):void 

{

      var interpolation:TweenMax = even.target as TweenMax;       // inversion du mouvement :      interpolation.reverse(); }

Nous voyons donc que le moteur de greensock s’adapte très facilement aux habitudes de programmation que nous contracterons avec la pratique, et aux besoins qui peuvent s’avérer différents en terme de complexité. 

Il existe bien d’autres méthodes liées à cette classe. Le présent document ne fait que guider vos premiers pas avec ces moteurs d’interpolation, il ne dispense en aucun cas d’étudier le moteur de votre choix pour en tirer le meilleur parti. En cherchant dans cette documentation vous vous apercevrez par exemple qu’il existe une méthode permettant d’arrêter toutes les interpolations, une autre permettant de les reprendre. La plupart des besoins courants seront bien souvent alimentés via une méthode ou un plugin à activer. 

Rappel de notation

Pour nous raccourcir des lignes de code, nous allons, au long de ce chapitre, utiliser la syntaxe abrégée pour la création des tableaux et objet nécessaires.

Pour les tableaux :

var tab :Array = new Array() ; tab[0] = (« une_valeur ») ;

tab[1] = (« une_autre_valeur ») ;

Revient à écrire :

var tab :Array = [ « une_valeur », « une_autre_valeur » ] ;

On observe que les crochets suffisent à définir un tableau. Les mêmes crochets servent aussi à définir l’index du tableau ou à en récupérer la valeur.

De même pour les objets : 

var obj :Object = new Object() ;

obj[« x »] = 50 ; obj[« y »] = 23 ;

Revient à écrire :

var obj :Object = { x : 50, y : 23 } ;

Ici on voit que les accolades suffisent à définir un objet. 

Contrairement à la notation régulière, la propriété est définie sans l’aide des guillemets. On lui attribue une valeur à l’aide de l’opérateur « : ».

Les couples propriétés / valeurs sont séparés par des virgules.

Nous aurons durant le chapitres l’occasion de voir des notations telles que :

[{ x : 50, y : 23 },{ x : 510, y : 213 },{ x : 250, y : 123 },{ x : 40, y : 13 }]

Il s’agit en fait d’un tableau composé de quatre objets informant des propriétés x et y. C’est par exemple la notation que vous trouverez pour définir une courbe dans TweenMax.

Partie 3

Séparer le contenant et le contenu : L’utilisation de la classe XML

Le nouveau type natif XML dans actionscript 3 recoupe exactement la norme en usage pour un xml externe.

Il est donc possible de charger un XML et l’utiliser comme fichier de contenu et ou de paramétrage en conservant son aspect natif.

Il est possible aussi d’en créer un de toute pièce et de le transmettre à un script php qui se chargera de le transformer en un fichier sur le serveur exploitable par la suite. 

L’avantage d’un xml pour une application flash est aussi de libérer les ressuources du serveur et de la base où son utilisation est fortement sollicitée (chat, jeu en reseau…). Pour une galerie d’images par exemple quel besoin de faire une requête sur le serveur si la base n’est pas mise à jour par des contributions d’utilisateurs ? Toutes les opérations se passeront du coté client, optimisant ainsi la bande passante de manière considérable.

Enfin le parcours d’un arbre xml à été entièrement revu en actionscript 3, et est particulièrement pratique comparé à l’ancienne version ou même à javascript.

Structure d’un XML

<?xml version="1.0" encoding="utf-8" ?>

<nœudprincipal>

                  <enfant propriete= « chaine »>

                                    <sous_enfants><a class= »monstyle »>cliquez pour voir !</a></sous_enfants>

                                 <sous_enfants></sous_enfants>

< /enfant>

</nœudprincipal>

Un xml est une liste ordonnée par des balises imbriquées de la même façon que des balise html. A sa différence il n’existe pas de balises prédéfinies, leurs noms et l’arborescence ( pas de chevauchement ) sont laissés au choix du programmateur ( pas de caractères spéciaux ,ni accents, ni espaces ). Il existe toutefois des syntaxe xml standardisées permettant l’échange d’informations normées (gestion informatisée des bibliothèques, flex, flux rss…). Un xml est principalement utilisé pour le transport de contenu écrit, et référentiel.



Les éléments 

Un xml se compose nécessairement d’un nœud racine dans lequel est déployée le reste de l’arborescence.

Le nœud racine contient le reste de l’arborescence. Il ne peut pas contenir de texte non entouré de balises.

Un noeud ou élément sans enfant peut être noté selon la syntaxe de balise abrégée : <balise identifiant= « .3 » />

En syntaxe AS3 on parcourt les éléments selon la syntaxe pointée et l’utilisation des crochets pour parcourir des enfants multiples. Par exemple :


Nœudprincipal.enfant.sous_enfants[0] : cible le contenu du premier nœud sous_enfants.

Les éléments peuvent contenir des chaines de caractères au format HMTL. Il est donc possible de placer dans ces balises du contenu texte formaté à l’aide des balises et des styles css

Les attributs

Tout comme la syntaxe xhtml , il est possible de définir des attributs à  l’intérieur des balises ouvrantes des éléments. Les attributs sont une paire d’identifiants et de valeurs.

L’identifiant : doit être nommé selon une syntaxe sans caractères spéciaux (sauf l’underscore), ni espaces, ni accents. 

La valeur doit se trouver entre guillemets ou apostrophes. Elle ne peut pas contenir de syntaxe à balise (donc pas de texte formaté en html) mais l’utilisation des slashes est autorisée. La valeur est transmise à flash sous forme de chaine, il est donc nécessaire de faire attention lors par exemple de l’envoi d’un booleen pour que les valeurs soient justes.

On récupère un attribut dans flash à l’aide du signe @ (arobase).

Dans notre exemple du début il suffit de cibler :

Nœudprincipal.enfant.@propriete

Note Importante : un fichier XML destiné à être utilisé par flash doit être impérativement codé en UTF-8

On trouvera l’option adéquate dans l’éditeur de script (via flashDevelop : file / encoding / UTF-8 )  L’entête doit préciser l’encodage utilisé.

Ecrire un xml dans l’espace actionscript

XML est un type primitif. A ce titre il s’instancie de la manière suivante :

var parametres : XML = 

<params>

                    <couleurs fond=”0xCCFF33” bords=”0xFFFFFFF”></couleurs>

<alphas fond=”.9” bords=”.7 »></alpha>

<titre><fr><u>Notre nouvelle adresse</u></fr><en><u>Our new adress</u></en></titre>

<params>

Hormis sa mise en variable, et l’amputation de l’entête, on ne distingue aucune différence entre l’écriture d’un XML externe ou interne.

Parcourir un fichier XML dans actionscript 3 

Le parcours d’un fichier XML à été entièrement revu en AS3 par rapport à la précédente version.

Un fichier XML partage certaines caractéristiques communes avec un tableau qui rendent son parcours un peu similaire.

Le nœud principal dans le XML est remplacé par le nom de la variable :

var donnees :XML=

<main>

<parametres ><couleurs fond="0xCCFF22"></couleurs></parametres > <medias>

<image chemin=" "><titre type="html" >Nouveau batteur</titre></image >

<image chemin=" " ><titre type="html" >Racleur à tomate</titre></image >

</medias>

</main> ;

//Cibler un nœud (ici la balise paramètres) (nous laissons ici le type indéterminé): var parametres : * = donnees.parametres;

trace ("parametres : "+parametres) // renvoie la balise complète

//Cibler un identifiant de balise (ici la couleur du fond) :  var couleur_fond :Number = donnees.parametres.couleurs.@fond ; trace("couleur : "+couleur_fond) // renvoie l'équivalent décimal de la valeur hexa rentrée.

//. Note sur le transtypage : En imposant une chaine de caractères (String) 

// à une variable de type Number, l’application va automatiquement interpréter la chaine en tant que   nombre .

// Cela ne génère donc pas d’erreurs.

//Cibler une balise dans un nœud multiple(nous récupérons le l’url de la première référence d’image du xml) : var ref_image_1 :String=donnees.medias.image[0].@chemin ; trace("chemin première image : "+ref_image_1)// renvoie :

 

//cette notation nous permet d’utiliser une boucle sur un nœud multiple pour parcourir celui-ci.

//Si nous voulons effectuer un parsage préalable de tous les enfants d’un nœud nous pourrons soit utiliser une boucle " for " soit comme  ci-dessous une boucle " for each "

for each (var image :XML in donnees.medias.*)

{

      //Nous verifions si ce noeud possede encore des sous-enfants (noter l'utilisation de parenthèse après "length))     If ( image.length()>0 )

      {

            for each(var infos :XML in image)

            {

//Si c’est le cas nous verifions quelles infos sont présentes en nous servant du despcripteur de balise

                  var chaine_info : String = infos.*.name();

//Notons que le paramètre “name” est ici une methode et prend donc des

//parenthèse à la fin de l’expression ne pas confondre avec la propriété //name d’un membre du package display.

                  if(chaine_info == "titre")

                  {

                        trace("titre ="+infos.*)

                  }

            }

      }

}

Charger un fichier XML externe

Nous utilisons pour cela la classe prédéfinie de chargement de fichiers texte URLLoader() (Nous nous en servons également pour un fichier de type css ou texte simple).

//XML dans lequel va être stocké le XML chargé

var dataXML : XML;

//Loader qui va charger le XML var loader:URLLoader;

var diffuseur : EventDispatcher = new EventDispatcher() ;

/*-- Fonctions de chargement des données --*/

//Fonction de chargement du XML de base function load(url:String):void  

{

     //création du loader et chargement des données   loader = new URLLoader(new URLRequest(url));

     // ajout d’un ecouteur d’évenement déclenché à la fin du chargement loader.addEventListener(Event.COMPLETE, finChargement);

}

//Fonction déclenchée à la fin du chargement du XML function finChargement(evt:Event):void

 {

//Stocke le XML chargé dans le XML prévu à cet effet dataXML = new XML(); 

}

Note : le code ci-dessus fournit l’architecture minimum pour gérer un téléchargement. Une mauvaise adresse peut générer une erreur qui fera planter l’application. Il est préférable de prévoir une gestion des erreurs.


Partie 4 : Sur les classes

Comment organiser son code ?

La souplesse de flash, permet l’insertion de codes à de multiples niveaux : Scénario, clips, classes reliées à des clips ou au scénario principal. Il est même possible de se passer entièrement du logiciel est de se servir uniquement du code pour fonctionner en reliant FlashDevelop au « flex sdk » et à la dernière version du player flash en version debugger.

Dès lors il peut apparaître un peu déconcertant de savoir ou l’on devra écrire telle ou telle fonctionnalité de notre application. 

Le principe prévaut dans cette nouvelle mouture d’actionScript d’utiliser la programmation orientée objet, c'est-à-dire d’employer des classes pour regrouper chaque fonctionnalité particulière. 

C’est un principe qui trouve toute sa vérité dans des applications complexes dotées de nombreuses interactions, mais qui convenons-le au départ peut sembler une gageure, sans avoir pratiqué et en mesuré les différentes implications.

Lorsqu’il s’agit de créer une petite application ou une animation dotée de quelques interactions, l’intérêt des classes peut s’avérer un peu restreinte. L’intérêt d’une classe est sa portabilité, sa capacité d’être réemployée dans un autre contexte sans devenir une usine à gaz. 

Pour une animation il est possible aussi avec un peu d’astuce de mélanger éléments créés et interpolés sur la scène et des éléments programmés. Voire de programmer une animation dans flash sans avoir à se servir du scénario principal du logiciel, l’interêt de créer une classe pour cela est que l’animation en question presente un aspect systématique. C'est-à-dire que son comportement soit entièrement lié à une action répétée et ou faisant appel au hasard… sinon le calque actions-image reste le moyen de travailler rapidement.

La seule contrainte reste dans flash la synchronisation des éléments. L’actionscript est exécuté de manière asynchrone, c'est-à-dire qu’il n’attend pas la fin d’une opération pour déclencher la suivante. Une bonne partie du code consiste souvent à indiquer au lecteur flash d’attendre que l’image ou le document soit chargé et quoi faire ou montrer en attendant.

Même pour des petits objets utilisés d’animation en animations il peut s’avérer judicieux de coder les quelques lignes sur la ligne de scénario du clip. Avez-vous remarqué lors d’une exportation pour actionscript que le clip est transformé en une classe qui contiendra toutes les définitions de fonctions  ? 

Voici par exemple la classe d’un clip contenant une zone sensible programmée sur un calque

« actions » vers un lien html créé dans flash :

package intro_croisieres_V2_fla

{

      import flash.display.MovieClip;     import flash.events.MouseEvent;

      public class skip_me_AS3_4 extends MovieClip

      {

            function frame1 () : *;

            public function goHome (param0:MouseEvent) : *;

            public function skip_me_AS3_4 ();

      }

}

En somme, si c’est possible, profitez de cette fonctionnalité offerte par le logiciel… L’important est de bien garder en vue la complexité de votre application : A partir d’un certain point - 150 à 200 lignes - il devient nettement plus pertinent d’organiser son code à l’aide de classes.  Les éléments redondants peuvent être aussi envisagés. Ne serait-ce que par exemple un chargement d’image dont vous aurez l’utilité à maintes reprises : évidemment une classe. 

1. Création d’applications, en préparer les différents aspects

Le schéma Temporel.

Les applications flash ont un déroulement dans le temps et c’est un paramètre qu’il faut d’ores et déjà intégrer lors de la conception d’une application.

Cela commence souvent par : « mon application comporte pas mal de graphiques internes, il faudra donc prévoir une barre ou une animation de chargement… ». 

On entre dès lors dans la scénarisation de l’application qu’il va falloir s’efforcer d’optimiser en fonction du temps imparti et du budget de l’application. Cette scénarisation s’opère en visitant mentalement l’architecture du site ou de l’application, et en évaluant les temps d’attente en fonction du poids des éléments à charger… etc.

Le poids du code étant négligeable, ce sont bien les éléments graphiques qu’il faudra envisager rigoureusement.

L’intégration de polices de caractères, par exemple, pouvant être très coûteuses en poids surtout si l’on embarque chacune en gras et italique. La question se posera alors de savoir si on la charge à la racine de notre document maître ou par le biais d’une bibliothèque d’un swf externe… Ce qui allègera l’application mais rendra la programmation plus délicate.

Le schéma temporel est donc un véritable story-board, qui commence par la couverture des temps d’attente. Mais, soigné autant qu’il le mérite, il deviendra l’outil qui plongera l’internaute dans un véritable univers… en résolvant des questions si « simples » soit elles que « comment vais-je passer d’une rubrique à l’autre ? ».

Cette charte est aussi indispensable pour la scénarisation éventuelle, que le sont les plans de coupe au cinéma pour dynamiser le montage et immerger un peu plus le spectateur dans le récit. Ne confondez pas cela avec une simple transition par exemple entre deux rubriques d’un site classique qui peuvent être programmées à la fin à partir du moment où ils auront été prévus : Le moteur d’un site  tient souvent en deux ou trois fonctions qui pourront être étoffées par la suite.

Le schéma d’imbrication.

Le schéma d’imbrication consiste à prévisualiser sur le papier comment les éléments de l’application seront placés dans la liste d’affichage afin de garantir une ergonomie maximum pour l’utilisateur  du site et le développeur de l’application. 

Les schémas Objets

Enfin, la question  à se poser lors de la création d’une application c’est qu’est ce que cette application doit pouvoir faire. En faisant la liste des actions possibles, nous pouvons d’ores et déjà nous faire une idée des différentes classes à créer pour lui faire voir le jour.

Pour vous aider à vous faire une idée plus concrète : voici un schéma pour une application générique. Et le découpage des classes tels qu’on pourrait la réaliser en s’aidant de la bibliothèque du fla.

2. Organiser son travail, savoir circonscrire ses classes.

De nombreuses questions concernant la création de classes se posent lorsque l’on commence à s’aventurer dans la programmation orientée objet. 

La première d’entre elle est celle de savoir si l’on doit faire une classe pour l’objet ou si il est juste nécessaire d’inclure l’objet dans la classe qui est censé gérer ce processus entre autres. Plusieurs critères peuvent justifier l’utilisation d’une classe : 

La principale règle est de lisser au maximum la longueur de chacune en isolant dans une classe chaque comportement complexe d’une animation. Songez que plus vous isolerez les mécanismes concernant un même processus plus vous aurez de facilité à vous relire. De plus dans FlashDevelop, votre travail sera rendu bien plus efficace grâce à son excellente aide à la programmation.

L’autre idée directrice en direction de l’architecture de classe entre elles est qu’une modification d’un paramètre dans un xml par exemple ne donne pas lieu à des changements dans toutes les classes concernées. Dans ce cadre j’aurai intérêt à standardiser mes informations au travers d’une classe de distribution des infos ou d’une classe permettant d’instancier un objet disposant des infos.

Architecture des classes

Ensuite il est utile de savoir ou s’arrête la classe principale et où commencent les classes spécialisées de l’application. La règle ici est relativement simple : Eviter le plus possible la propagation des paramètres optionnels de l’application dans le fonctionnement des classes spécialisées. De cette manière il vous sera beaucoup plus aisé d’utiliser ces classes dans les configurations les plus diverses. Dans le cas d’un lecteur son par exemple. On peut le trouver sous de multiples formes et présentation, malgré cela, ils ont tous en commun la même base. (Jouer, arrêter, modifier le volume…). Il est alors intéressant de prévoir cela afin de ne pas avoir à réecrire une classe à chaque fois.

Par exemple, je veux créer un petit lecteur musical géré par xml externe. Voici à quoi pourrait ressembler le schéma d’application. La classe de gestion du son est bien isolée et pourra être réutilisée en de multiples occasions :

3. Organiser son travail, les classes de l’application, les classes permanentes.

On distinguera deux différentes catégories d’utilisation. On pourrait les appeler classes locales et  classes globales. 

L’enveloppe et le cœur

Les premières ne nous seront utiles que dans le cadre de l’application. Il s’agit souvent des classes graphiques qui nous servent à habiller l’animation. Les autres sont des classes usuelles qui pourraient être susceptibles d’être réemployées. Chargeurs et diffuseurs de medias, objets usuels (curseurs, barre de chargement, movieclips étendus permettant la création instantanée de d’objet graphiques longs à programmer)

En effet qu’il s’agisse du logiciel flash ou de FlashDevelop deux possibilités d’importation des classes sont offertes :

-    Soit l’on importe une série de classes de manière définitive pour l’utiliser dans le logiciel au fur et à mesure des applications. Nous l’avons déjà employée lors du chapitre sur les interpolations en important la classe tweenMax. Ces classes doivent être stables ou doivent être améliorées pour un compatibilité maximum. Avantage et inconvénient chaque modification à la classe est appliquée à chaque compilation de tout fichier qui l’utilise.

-    Soit on importe un ou plusieurs paquetages qui seront accessibles uniquement depuis le fichier en cours.

Ne vous créez pas un problème à propos de savoir dans quelle catégorie ranger la classe que vous créez.  

Si vous vous organisez de manière à ce que vos noms de paquetages soient identiques au fil de vos travaux, vous pourrez améliorer une classe d’application en application, en les copiant dans le dossier jusqu'à ce que vous la jugiez éligible à faire partie des classes globales (importées de manière permanente dans le logiciel). C’est ainsi qu’au fur et à mesure vous pourrez vous constituer une bibliothèque d’objets qui pourront vous aider à réduire le temps passé à la programmation.

J’ai par exemple dans mes classes globales :

Des objets graphiques complexes : Curseur générique, générateurs de bouton, Effets de planètes tournantes, formulaires, effets de glissement sur une image…

Des classes procédurales et de stockages : Objets regroupant les informations d’une image, chargeurs (dowloads, uploads) divers, classes statiques regroupant des fonction usuelles (placement, copie d’objet).

Ces manières de faire procèdent de deux stratégies de programmation différentes :

-    Soit l’on crée des classes attachées directement au logiciel que l’on modifie de travail en travail. Dans la partie suivante, suivre cette logique fera adopter au programmateur la première voie. 

-    Soit on crée des classes génériques permettant une utilisation sans modification. Cette voie concerne assez peu d’éléments en regard de l’ensemble du travail d’un programmateur. Si vous travaillez en équipe il est nécessaire de rendre disponible vos outils aux autres pour permettre la compilation. Par ailleurs leur mise en commentaire leur permettra leur utilisation par des tiers en facilitant leur portabilité, mais aussi pour permettra de rentrer dedans après de long mois sans pour autant passer une demi journée à comprendre ce que vous avez voulu faire !

La partie qui suit va essayer de vous présenter les deux logiques de la manière la plus globale possible afin que vous puissiez vous faire une idée, voire de savoir prendre une direction lors de l’organisation des classes d’une application. Nous allons prendre l’exemple de la manipulation d’un XML de données à travers l’ensemble d’une application.

4. Les classes non graphiques dans une application

Flash

Les classes non graphiques regroupent une partie non négligeable de la programmation AS3. Il s’agit de classe n’étendant pas d’objets graphiques. Donc ne pouvant directement être ajoutée à la liste d’affichage.

L’utilisation d’une classe non graphique s’avère très utile, dès qu’il s’agit de manipuler des données, de les stocker et les distribuer dans une application dynamiques ou encore de gérer des processus complexes, de diffuser des évènements personnalisés. 

C’est également un excellent moyen de subdiviser les instructions de la classe principales qui à dejà en charge la gestion de nombreux processus. De cette façon on ôte  un nombre conséquent de lignes à celle-ci, ce qui la rendra plus digeste à travailler. Au-delà de 300 lignes la logique et les attributions d’une classe deviennent plus difficiles à comprendre même pour le créateur qui l’ouvre six mois plus tard pour une nouvelle utilisation. 

Elles peuvent être uniquement un confort de programmation, comme une collection de fonctions destinés à alléger le codage du placement d’un objet : FlashDevelop est capable d’afficher les méthodes et propriétés de vos classes personnalisées vous gagnez ainsi un temps précieux à utilisés l’aide à la programmation. Une classe clairement identifiée sera d’un accès aisé, plutôt que de transmettre un xml de classe en classe et de risquer de rigidifier son code selon les variations d’identifiants ou de structure.

Elles peuvent aussi s’avérer obligatoires en de multiples occasions pour la gestion de processus internes comme un historique, un gestionnaire de scores, un système de socket pour l’échange des données etc…

Gestion de données xml dans une application.

Dès qu’il s’agit de gérer une galerie dynamique ou de gérer la playlist d’un lecteur musical ou vidéo on pourra utiliser un xml. La souplesse de ce media en fait un avantage autant qu’il peut s’avérer problématique si on le propage dans l’ensemble des classes de l’application. 

Il est courant alors de centraliser les informations afin que les classes de l’application n’ait pas à utiliser le xml en question. 

On peut utiliser pour ce faire deux méthodes : 

La première permet de normaliser les infos xml en un objet que l’on instancie, confortable à utiliser au fil des classes. 

La seconde est un peu plus pointue mais permet d’avoir comme objet de transmission uniquement un index, objet standard par excellence rendant ainsi possible une parfaite séparation entre les différentes classes de l’application. C’est une solution à privilégier pour un travail en équipe.

Nous évoquons les deux, mais je vous conseille dans un premier temps d’utiliser la première, bien qu’un peu moins souple, mais qui vous permettra de vous familiariser un peu plus avec les classes et la notion d’objet. 

Utiliser une classe de stockage de données.

Deux stratégies peuvent être employées et même conjuguées en fonctions du cahier des charges concernant les interactions utilisateurs de l’application.

La première - utilisée dans les applications ne nécessitant pas de stockage de données d’interaction. ( Ex : diaporamas )  -  consiste à créer une classe capable d’aller chercher chacune des informations d’un xml interne ou externe, Puis de distribuer chacune des propriétés grâce à un système de méthodes statiques. 

Voici la structure d’un xml chargeant un diaporama et donc chaque image est cliquable et renvoi vers un lien.

<?xml version="1.0" encoding="utf-8" ?>

<main>

                  <plages path="musique/">

                                     <piste url="01.mp3" artiste="Jaques Brel" titre="Les Bourgeois" />

                                     < piste url="02.mp3" artiste ="Arthur h " titre ="Negresse blanche" />

                                     < piste url="03.mp3" artiste ="Massive Attack" titre ="Sly" />

                                     < piste url="04.mp3" artiste ="John coltrane" titre ="wise one" />

                </plages>

</main>

Stockage dans un objet

Stocker un objet auquel on attribue  les propriétés du xml est d’une part la manière la plus simple d’envisager ce type de fonctionnement. En effet ici chaque info xml devient un objet facile à manipuler  

Et une façon de voir la p.o.o. dans son fonctionnement le plus basique. C’est donc un bon moyen de débuter.

Cette méthode consiste à créer un classe d’objet dont chaque occurrence stockera les propriétés et nœuds textes de chacune des lignes du xml.

 Dans certains cas, elle peut être utilisée conjointement avec la classe xml… Cela permet de profiter d’une assistance au code maximum mais aussi de créer des méthodes de gestion de données intégrées…

Une telle classe pourra avoir cet aspect :

package {

       public var index : int = -1;     public var lien_fichier : String;            public var titre : String;

       public var artiste : String;

       // dynamic permet de pouvoir ajouter une propriété à l’objet à la volée sans    // qui n’existe à l’origine.      public dynamic class BadgeSon

       {

              public function BadgeSon() 

              {

              }

       }

}

Ainsi si un changement doit intervenir dans ce xml il sera juste nécessaire de modifier l’adressage dans cette boucle. Au lieu d’aller dans chacune des classes ou les infos xml interviennent. Nous allons constituer un tableau avec un objet typé qui aura pour avantage de proposer toujours la même variable pour la même valeur. 

                  for each(var donnee : * in donnees_xml.plages.*)

                  {

                        var badge :BadgeSon = new BadgeSon() ;                  badge.titre = donnee.@titre;

                        badge.fichier = donnees_xml.plages.@path+donnee.@url;                badge.artiste = donnee.@artiste;

                        (badge) ;

                  }

Le parcours du xml aura pour effet de classer les données dans le même ordre. Ainsi la première valeur du tableau, à l’index 0  correspondra au premier nœud xml stockant votre galerie ou votre playlist.

Stockage et gestion depuis une classe de parcours du xml :

C’est une autre manière d’envisager la centralisation et la normalisation des données. Ici nous créeons une classe statique s’occupant de charger le xml, puis de le parcourir au travers de ses fonctions en lui fournissant un numéro d’index correspondant à l’ordre du xml. 

Etant une classe statique on peu aussi l’utiliser dans toute l’application. Il peut ainsi être utilisé sans se contraindre à isoler les classes les unes des autres

/*Classe Statique de chargement et gestion du XML */

package {

       import flash.events.Event         import .Rectangle;      import .URLLoader;

       import .URLRequest;

       public final class XMLManager {

//XML dans lequel va être stocké le contenu XML du fichier chargé          public static var dataXML:XML;

 //Loader qui va charger le XML

              public static var loader:URLLoader;            private static var diffuseur:EventDispatcher;          /*-- Fonctions de chargement des données --*/

              //Fonction de chargement du XML de base               public static function load(purl:String, pdiffuseur

:EventDispatcher):void

 {

Diffuseur = pdiffuseur ; 

                     loader = new URLLoader(new URLRequest(purl));                         //création du loader et chargement des données                loader.addEventListener(Event.COMPLETE, loadComplete);                    //déclenché à la fin du chargement du XML

              }

              //Fonction déclenchée à la fin du chargement du XML

              private static function loadComplete(even:Event):void

{

                     dataXML = new XML();                 

                     diffuseur.dispatchEvent(even)           

              }

              /*-- Fonctions Publiques de Données de musique--*/

              /*-- fonction renvoyant le nombre d'images contenu dans le XML--*/         public static function get imgs():int {

                     return dataXML.plages.piste.length();

              }

              //fonction renvoyant le chemin des images             public static function get chemin():String {

                     return dataXML.plages.@path

              }

              //fonction renvoyant l'url complète (path+url) de l'image

              public static function getURL(nb:int):String {

                     return chemin + dataXML.plages.piste[nb].@url

              }

              public static function getTitre(nb:int):String 

              {

                     return dataXML.plages.piste [nb].@titre;

              }

public static function getArtiste(nb:int):String 

              {

                     return dataXML.plages.piste [nb].@artiste;

              }

       }

}              

Nous voyons donc que ces deux méthodes peuvent produire les mêmes effets sans toutefois procéder de la même méthode. C’est ensuite une question de pratique assidue de souhaiter appliquer l’une ou l’autre solution. 

Toutefois l’objet contient pour moi un avantage primordial pour certaines utilisations, celui de pouvoir embarquer beaucoup plus que les données du xml. Par exemple, pour un chargement d’image mon objet peut également stocker les données d’image. Ainsi le chargement du swf ou du bitmap associé à l’objet n’aura à être chargé qu’une fois. Si il est déjà chargé on évite le lourd processus du loader.


Partie 5

Maîtriser La gestion des données entrantes

L’objet loaderInfo.

L’objet loaderInfo est un objet qui va rassembler l’ensemble des informations et des classes du fichier auquel il appartient.

Bien que l’on trouve cet objet a la racine des DisplayObject,  il ne concerne que l’objet root (ou stage) de l’application, ou un graphique (swf ou image) chargé extérieurement. Un Sprite ou un MovieClip créés dans le code renvoient null si l’on appelle cette propriété.

Parmi les diverses propriétés et méthodes de cet objet nous pouvons connaître entre autres :  

-    la version d’actionScript utilisée : loaderInfo.actionScriptVersion. -         Le contenu du fichier chargé : loaderInfo.content.

-    Le framerate du clip chargé :loaderInfo.framerate

-    …

Cet objet nous donne accès aussi à deux possibilités particulièrement intéressantes :

Récupération et utilisation de variables issues de la page html.

En nous servant du paramètre flashVars lors de l’intégration du swf il est possible de définir des variables qui seront transmises à l’application par le biais de l’objet loaderInfo.parameters. Les variables placées sur la page html devront nécéssairement être des chaines. Les nombres seront donc aussi placés entre guillemets.

La démarche de récupération de ces variables est fort simple : 

Dans le fichier html , Dans la balise script qui gère le lancement grace à swfObject, le paramètre flashvars définit une variable contenant l’ensemble des paramètres que nous souhaitons faire passer à flash :

var flashvars = 

{

parametre_quelconque: "false",

parametre_langage : "en"

}; 

Nous cherchons à récupérer les variables passées à la racine de notre swf. 

Nous sommes dans le fichier servant de classe de document :

var parametre_utilisateur :String ;

// je verifie que cette  variable se trouve bien à la racine de mon application 

// à l’aide d’un test 

if(root.loaderInfo.parameters.parametre_quelconque != undefined)

{ parametre_utilisateur = root.loaderInfo.parameters.parametre_quelconque ; parametre_langage = root.loaderInfo.parameters.parametre_langage;

 } else

{

       // je m’assure de fournir à mon script un valeur par défaut

       // pour ne pas perturber l’animation en la testant hors navigateur.

 parametre_utilisateur = « false » ; parametre_langage = « false » ;

}

Exploiter les classes de la bibliothèque d’un swf externe.

Une des fonctions des plus intéressantes de l’objet loaderInfo, lorsque nous chargeons un swf externe, est de nous donner accès à tous les éléments de sa bibliothèque. Bien entendu, ceux qui auront été exporté pour actionScript dans le menu liaison des symboles de bibliothèque. Cela nous donne donc la possibilité de déléguer une partie conséquente du poids de notre application en chargeant les éléments graphiques et/ou polices dont nous aurons besoin depuis une bibliothèque externe. Dès lors, le poids de l’application pourra être consacré aux graphique et processus de l’intro du site et fournir à l’internaute une ouverture quasi-immédiate de la page.

C’est cette fois ci le sous-objet applicationDomain de loaderInfo que nous allons explorer, c’est a cet endroit que se trouve la bibliothéque des objets exportés, nous cherchons un clip auquel nous avons donné le nom de liaison « slider » dans le swf chargé : 

                -      Récupération de classes de clips et autres boutons

// un swf contenant mes éléments de bibliothèque

var clip_charge : MovieClip 

// l’objet qui sera renvoyé losque nous interrogerons la 

// bibliothèque sera une classe générique qu’il nous faudra  // transtyper

                     var Reference : Class;

                     // stocke l'occurence de la classe               var instance:*;

                     // nom de classe des différents éléments déposés dans la 

// bibliothèque

                     var identifiant:String;

                      identifiant='helvetica'; // une police de la bibliothèque

                     // identifiant='slider'; // un MovieClip

                     // identifiant='bouton'; // un bouton

                     // verification de l'existence de la classe

                     if(root.loaderInfo.applicationDomain.hasDefinition(identifiant))

                     {

                            // je stocke l'élément en le transtypant (as Class)

                            Reference =

clip_charge.loaderInfo.applicationDomain.getDefinition(identifiant) as Class;                  trace ("création de l'instance : " + Reference);                          // je crée une instance de ma classe

                            instance = new Reference();

                            // les tests suivants me permettent de connaitre la 

// nature de l'instance créée 

                            if(instance is MovieClip)

                            {

                                   trace("MovieClip : "+(instance is MovieClip) );

                            }

                            if(instance is Sprite) 

                            {

                                   trace("Sprite : "+(instance is Sprite) );

                            }

                            if(instance is SimpleButton)

                            {

                                   trace("Bouton : "+(instance is SimpleButton) );

                            }

                            if(instance is Font)

                            {

                                   trace("Font : "+(instance is Font) );                         //trace((Font.enumerateFonts()[0].fontName))

                            }

                     }

                     else 

                     {

                            trace(" L'élément invoqué n'est pas répertorié en librairie ")

                     }

Récupération et incorporation de polices

Les polices étant intégrées sous forme de classes, on suit exactement le même processus que précédemment en donnant le nom de classe de la police.

Nous devrons simplement enregistrer la classe de la police (non son instance) à l’aide de la méthode statique de la classe Font :

Font.registerFont(fontechargée : Class);

Nous faisons au passage aussi la connaissance de la méthode statique :

Font.enumerateFonts(policeOrdiClient :Boolean = false) : Array 

Renvoie un tableau contenant l’ensemble des polices disponibles dans l’application ou sur la machine client. 

Puis nous modifions le code pour charger la police dans l’animation racine : 

if(instance is Font)

{

trace(("polices avant intégration : " + Font.enumerateFonts().length))

// cette instruction transfère la police du swf chargé à notre application

Font.registerFont(Reference);

trace(("polices après intégration : " + Font.enumerateFonts().length))

if(Font.enumerateFonts().length > 0 )

trace((Font.enumerateFonts()[0].fontName))// nom veritable de la police

}

La police est désormais disponible dans l’application. Cela signifie que nous allons pouvoir utiliser des interpolations et des rotations sur nos champs texte sans risquer de voir le texte disparaître !

Il suffit pour cela d’appeler la police par son nom véritable, et de passer le paramètre embedFont à true ;

var texte : TextField = new TextField();

                     texte.htmlText = "<p>L'appli est testée avec succès</p>";                 var format:TextFormat = new TextFormat();

                     // la police chargée doit être appelée par son nom officiel //              = "HelveticaNeue LightExt";

                     // permet d’inclure le tracé des fontes dans le champ texte//               texte.embedFonts = true;                texte.multiline = true;                  texte.wordWrap = true;

                     format.color = 0xDD0000;           = 50;                texte.setTextFormat(format);              texte.width = 400;

                     texte.height = 150;

                     texte.autoSize = TextFieldAutoSize.RIGHT;               texte.selectable = false;                       texte.rotation = 45;                texte.x = 200;                    this.addChild(texte);


Partie 6

Utiliser le son en actionScript 3 (P.O.O).

Du contrôle basique à la création d’un lecteur audio.

La lecture d’un son dans les applications flash est relativement simple. Il suffit pour cela de poser sur la scène l’occurrence d’un son importé dans la bibliothèque (wav, mp3). Très pratique pour des sons courts et légers, comme ceux pouvant accompagner un click, par exemple. En revanche les manipulations plus avancées que la simple lecture d’un son font très vite appel à des connaissances en actionScript. 

Afin de poursuivre dans notre apprentissage. Je vous propose de mettre en place un lecteur musical complet en mesure de lire une liste de pistes référencées dans un xml. C’est un travail relativement long mais nécessaire pour avoir conscience d’une mise en place d’application dans une situation de production

Comme la vidéo, l’insertion d’un son d’une durée importante, et d’une qualité décente peut devenir très handicapant quant au poids final de l’animation.  Lors de la compilation flash échantillonne le son en mp3 à 96 Kbps ce qui en terme de qualité procède d’une grande dégradation de la qualité sonore. Dès que l’on augmente cette qualité à un niveau plus élevé dans les paramètres, l’animation prend du poids.

Nous allons dans ce chapitre résoudre les questions basiques posées par la manipulation d’un son (positionnement de la tête de lecture, arrêt, pause) et son chargement par le biais d’un fichier externe.

La lecture et le contrôle d’un son sont un peu similaire aux processus en jeu dans la vidéo. L’objectif de cet atelier et de créer un petit lecteur musical en mesure de diffuser un titre, de le pauser, et de le reprendre. Et d’ajouter un contrôle pour le volume.

La difficulté principale de cet atelier sera de créer un système qui offrira une prise en charge complète des contrôles et d’un chargement mp3.

Commençons par l’essentiel à savoir la manipulation du son lui-même. Pour cela nous chargeons un mp3 dans la bibliothèque d’un fichier flash. Et dans le panneau de liaison nous activons l’exportation pour actionScript. (le nom de liaison donné ici est  ”Death_Watch”).

Pour commencer la lecture d’un son en programmation ces deux lignes suffisent : 

// instanciation du son de la bibliothèque. Le type d’objet est Sound.  var son:Sound=new Death_Watch();

// Nous demandons à l’instance de commencer à jouer à l’aide 

// de la methode play() de la classe Sound var canalson:SoundChannel= ();

Remarquons que lors de l’appel de la méthode play(), nous avons pris soin de poser une variable recueillant l’objet renvoyé par la méthode. Cette variable est du type SoundChannel, que l’on peut traduire par canal de diffusion. C’est cet objet qui va nous permettre d’agir sur le volume,  de stopper la progression du morceau, ou en lui adjoignant un écouteur, d’être averti de la fin de la diffusion. Il est donc extrêmement important de recueillir ce paramètre. 

Nous avions vu dans la lecture d’une vidéo, que nous contrôlions la lecture et le volume par l’objet NetStream.

En somme, s’il nous faut transposer, il faut voir l’objet Sound comme le support de l’enregistrement, et l’objet SoundChannel comme l’amplificateur et la platine cd dont il nous reste à construire la façade et ses contrôles. 

Organisation de l’application 

Cette application doit contenir la gestion du chargement et de la diffusion d’une série de fichiers mp3. Ce type de gestion étant fréquent nous allons nous attacher à créer une classe qui ne sera pas liée aux aspects graphiques ou de fonctionnement général (par quel biais arrivent les infos ? présentation…). Ces aspects seront eux, déployés sur la classe principale.. Ainsi il sera possible d’utiliser cette application en standalone (player) ou en se servant du cœur au sein d’une application plus complexe. Nous allons déployer trois classes différentes pour son fonctionnement.

1 la classe principale : LecteurSonIDE.

Notre classe principale est attachée à un fichier fla. Cela va nous permettre de nous faciliter la tache pour la gestion des graphiques et des placements.

Nous avons l’opportunité de placer des éléments sur la scène et d’utiliser leur noms d’occurrences comme si il s’agissait de variables de classes.

Pour le bon fonctionnement de l’application nous utiliserons une structure attendant que les éléments soient initialisés pour se déployer. Il est d’usage pour les langages asynchrones d’employer cette technique (cf : javascript ). C’est particulièrement important ici car nous avons des éléments déployés sur la scène.

Le point de départ du déploiement sera dont la fonction init() et non la fonction constructeur.

package {

      import flash.display.MovieClip;     import flash.events.Event;

      public class LecteurSonIDE extends MovieClip

      {            

            public function LecteurSonIDE() 

            {

                  if(stage)

                  addEventListener(Event.ADDED_TO_STAGE, init);

                  else               init()

            }

            private function init(e:Event = null):void 

            {

 //point d’entrée de l’application

            }

}} 

A – Chargement des données xml

Partant de la nous allons charger maintenant la liste XML. Nous allons la structurer de manière à pouvoir ajouter des éléments par la suite, comme la personnalisation des couleurs de l’application.

<data>

      <playlist>

            <morceau genre="rock"><titre>Je tuerai la

pianiste</titre><artiste>Bashung</artiste><fichier>medias/Death_Watch.mp3</ fichier></morceau>

            <morceau

genre="classique"><titre>Classique</titre><artiste>Debussy</artiste><fichie r>medias/Pigs.mp3</fichier></morceau>

            <morceau

genre="jazz"><titre>pianiste</titre><artiste>Bashung</artiste><fichier>medi as/Mixdown_Nes.mp3</fichier></morceau>

      </playlist>

</data>

L’adresse du xml peut être stockées par défaut sur la classe principale. Mais il serait bien plus interessant de la stocker dans la page html. De cette manière le lecteur pourrait diffuser différentes playlist sur un seul et même site ! Nous placerons donc notre adresse dans une flashVar. Nous laisserons malgré cela l’adresse par défaut dans la classe principale pour pouvoir tester l’animation depuis le logiciel.

Nous allons utiliser un stockage des informations un peu particulier sous forme d’une classe personnalisée. Chaque info sera stockée dans un objet au sein d’une liste. Nous plaçons cette liste dans nos variables de classes. Nous stockons aussi le xml chargé non pour la listedes morceaux mais pour les paramètres de configuration qu’il est possible de lui adjoindre..

// Liste des Objets BadgeSon

private var tab_liste:Array = new Array(); // stockage des infos xml (optionnel) private var donnees_xml:XML;

private function init(e:Event = null):void 

{

removeEventListener(Event.ADDED_TO_STAGE, init);

// recherche de la playlist

if (loaderInfo.parameters.liste != undefined)

{

      // loaderInfo permet l’accès aux flashvars chargées  //depuis la page html (identifiant : liste)

      chargementXML(loaderInfo.parameters.liste)

}

else

{

      chargementXML("")

}

}                  

public function chargementXML(url:String) : void

            {

      var loader:URLLoader = new URLLoader();  var requete :URLRequest = new URLRequest(url);  loader.addEventListener(Event.COMPLETE, XMLCharge);  (requete);

}

// Le cœur de l’application peut être déployé ici private function XMLCharge(even:Event):void

{

      var loader:URLLoader = even.target as URLLoader;

var donnees_xml:XML = new XML();

      XML.prettyIndent = 0;

      XML.ignoreWhitespace = true; 

}

2 – La classe BadgeSon

Charger un ensemble de données via un xml est certes très pratique mais qui peut singulièrement compliquer chaque modification dès lors ou l’on modifie la structure. L’idée est de standardiser les informations à l’aide d’un objet de telle manière à ce que chaque manipulation du xml n’impacte que la boucle de remplissage de la liste. Tout accès aux informations délivrées par l’objet se fait de manière unique quelle que soit l’application ou il est utilisé. Nous détachons donc le contenu de la manière de le délivrer ce qui est la méthode la plus viable pour conserver un maximum de portabilité.

L’intérêt dans cette méthode est sa souplesse. En effet les fichiers mp3 sont en principes dotés d’informations sur le morceau codés sous forme d’un objet appelé ID3. L’objet aura la capacité de compléter ses informations grâce à la récupération de cet objet durant le chargement. Ce qui permettra d’afficher les informations complètes du morceau si elles sont stockées (artiste, titre, genre, album, année). 

Les développements ultérieurs de cette classe pourraient charger les étiquettes id 3 des fichier de manière autonome ce qui pourrait permettre de nous passer des champs concernés dans le xml.

Mieux nous pourrons récupérer ces informations sous forme d’une chaîne de caractère prête à être affichée !

package {

      import flash.media.ID3Info;

      public dynamic class BadgeSon

      {

            private var tagID3 : ID3Info;

            public var artiste:String = "artiste inconnu";          public var titre:String = "sans titre";        public var annee:String ="";             public var genre:String="Pas de genre";        public var album : String= "pas d'infos";             public var index:int;        public var fichier:String;          private var id3ToString:String;

            public function BadgeSon() 

            {}

            public function set ID3 (tag:ID3Info):void

            {

                  //trace("annee : ["+tagID3.artist.toString()+"]")

                  tagID3 = tag;

                  var desc : String = new String();

                  if (tagID3.artist != null)

                  {

                        artiste = tagID3.artist;

                        desc += tagID3.artist +"  -  ";

                  }

                  if (tagID3.songName != null)

                  {

                        titre = tagID3.songName;

                        desc += tagID3.songName+" \n";

                  }

                  if (tag.album != null)

                  {

                        album = tagID3.album;

                        desc += "Album : " + tagID3.album+" ";

                  }

                  if ( != null)

                  {

                        annee = ;                      desc += "( " + +" ) \n";

                  }

                  if (tagID3.genre != null)

                  {

                        genre = tagID3.genre;                    desc += "Genre : " + tagID3.genre;

                  }

                  id3ToString = desc;

            }           

// récuperation d’une chaine affichable.

            public function get description():String {return id3ToString;}

      }

}

Nous voyons au passage l’intérêt d’un type de fonction particulier appelé getter/setter. Une variable privée liée à ce type de fonction permet d’une part de faire appel à cette fonction comme si il s’agissait d’un variable. La seconde mais plus intéressante qualité de cette fonction est d’être en mesure d’effectuer des opérations subalternes ce qui n’est bien sur pas possible si l’on met en place une simple variable.

Dans notre classe, son utilisation pour l’étiquette id3 permet au passage de rafraîchir les variables si les infos correspondante sont présentes et aussi de constituer une chaîne préformatée affichable au moyen de la méthode text d’un TextField.

Nous ajoutons alors la boucle de construction a notre fonction de réception du xml. 

// L'application est deployée ici private function XMLCharge(even:Event):void

{      

      var loader:URLLoader = even.target as URLLoader;      donnees_xml = new XML();

      // ces proprietés semblent être obsoletes dans le fp10

//XML.prettyIndent = 0;

      //XML.ignoreWhitespace = true;

      //standardisation des infos xml au moyen d'un objet personnalisé

BadgeSon

      for (var i :int; i < donnees_xml.playlist.morceau.length();i++ )

      {

            var badge:BadgeSon = new BadgeSon();

            badge.index = i;

            badge.titre = donnees_xml.playlist.morceau[i].titre;          badge.artiste = donnees_xml.playlist.morceau[i].artiste;          badge.fichier = donnees_xml.playlist.morceau[i].fichier;        (badge);

      }

}

Nous reviendrons sur notre classe principale pour connecter l’ensemble des comportements pour le moment nous allons nous pencher sur l’archistcture de notre classe PisteSon qui va se charger de toute les opérations de chargement et de diffusion des fichiers.

3 -  la classe « cœur » : PisteSon.

Voyons dans la documentation actionScript Les possiblités que nous avons de manipuler le son à l’aide d’actionScript.

---------------

Documentation (dictionnaire de référence actionscript)

Sound : L’objet Sound contient les méthodes qui vont nous permettre de charger le son à partir d’un fichier externe. Le passer en lecture. L’objet diffuse aussi les évènements de chargement, de réception des informations id3 du fichier.

SoundTransform : L’objet SoundTransform agit sur le volume et la balance du son.

SoundMixer : Objet par le biais duquel il est possible de modifier les propriétés générales du son dans flash. Il contient un objet SoundTransform valant pour le volume et la balance générale du swf. Pour les niveaux plus avancés, cet objet peut nous fournir un objet à partir duquel il est possible de construire un analyseur de spectre et sa représentation graphique.

SoundChannel : L’objet SoundChannel et le plus important en ce qui concerne le comportement d’un son durant la lecture puisque c’est lui qui contient l’objet SoundTransform et qui permet l’arrêt de la lecture. Il envoie également l’évènement qui prévient de la fin de la lecture du fichier.

---------------

Nous voyons que plusieurs objets sont susceptibles de délivrer des évènements.  L’intérêt de notre classe sera d’unifier la diffusion au travers d’un seul objet, et d’assurer la gestion des différents processus pour que nous puissions l’utiliser de manière simple.

Le son n’est pas un objet d’affichage il n’est donc pas nécessaire qu’elle étende MovieClip ou Sprite. En revanche l’implantation d’un diffuseur qui lui serait attaché nous simplifierait les choses. Nous allons donc faire hériter cette classe d’EventDispatcher. Nous aurons ainsi la possibilité d’émettre ou de rediriger des événements via le diffuseur intégré.

Cahier des charges et choix de la stratégie de programmation.

Le modèle de classe que nous allons utiliser ici sera en mesure de diffuser plusieurs son à la suite. Ainsi nous n’aurons qu’a instancier une seule fois cette clase pour obtenir une piste de lecture en mesure de diffuser tout les morceau de la liste. Cette stratégie pourra ainsi nous permettre de l’utiliser au sein d’une application multipiste. Par exemple il sera possible de ce fait de proposer en option le volume sonore de la musique de fond séparément des bruitages.  

Chargement du son et mise en lecture

Le chargement du son en streaming utilise les mêmes évènements de base que celui d’une image. Nous pourrions directement utiliser nos objet badge son pour charger les fichier et stocker les informations de chargement. Mais cela reviendrait à rendre notre classe moins souple pour quelques lignes de fonctionnement. Nous choisissons donc de charger les fichier son grâce à une url et de confier la synchronisation des infos à la classe principale.

On remarquera que les retraits d’écouteurs sont susceptibles d’être employés plusieurs fois. On les place dans un méthode qui va nous faire gagner quelques lignes de code.

 package  {

      import flash.events.Event;    import flash.events.EventDispatcher;       import flash.events.IOErrorEvent;    import flash.events.ProgressEvent;  import flash.media.ID3Info;    import flash.media.Sound;  import flash.media.SoundChannel;    import flash.media.SoundLoaderContext;     import flash.media.SoundTransform;

      import .URLRequest;

      /**

      public class PisteSon extends EventDispatcher

      {

            private var son:Sound;              private var canal_son:SoundChannel;             private var position_lecture : Number = 0;             private var volume_son : Number = 1;        public var ID3 :ID3Info;            public var longueur_morceau:Number=0;

            public var index_piste:int = 0;

            public function PisteSon()

            {           }

// ------------- CHARGEMENT SON EXTERNE ------------- //

 public function chargerNouveauSon (pfichier : String , tamponLecture : int

= 5000):void

{

// instanciation d'un objet Sound son = new Sound();

var contexteAudio : SoundLoaderContext = new SoundLoaderContext

(tamponLecture);

// mise en place des divers écouteurs permettant d’établir les

// comportements adequats

son.addEventListener( ProgressEvent.PROGRESS, chargementEnCours ); son.addEventListener( Event.COMPLETE, retraitEcouteursSon); son.addEventListener( IOErrorEvent.IO_ERROR, erreurChargement ); son.addEventListener(Event.ID3, id3Detecte);

// chargement dynamique du son

var requete : URLRequest = new URLRequest (pfichier) ; (requete, contexteAudio);

}

public function id3Detecte(even:Event):void

{ trace("Detection ID3");

son.removeEventListener(Event.ID3, id3Detecte);

ID3 = son.id3;

dispatchEvent(even);

}

public function chargementEnCours ( pEvt:ProgressEvent ):void

{

      dispatchEvent(pEvt);

      // la longueur du morceau correspond au bytes chargés       longueur_morceau = son.length;

}

private function retraitEcouteursSon(even:Event = null):void

{ if (son.hasEventListener(Event.COMPLETE))

{

son.removeEventListener( ProgressEvent.PROGRESS, chargementEnCours ); son. removeEventListener ( Event.COMPLETE, retraitEcouteursSon); son. removeEventListener ( IOErrorEvent.IO_ERROR, erreurChargement );

}

if (son.hasEventListener(Event.ID3))

      {

                      son.removeEventListener (Event.ID3, id3Detecte);

      }

}

private function erreurChargement (even : IOErrorEvent):void

{

retraitEcouteursSon();

}

}

dispatchEvent(even);

}  

Nous avons installé les processus de chargement. Il nous reste maintenant à mettre le son qui se charge en lecture. 

Nous allons ensuite modifier ces fonctions pour  avoir la possibilité de charger plusieurs morceaux à la suite. 

La fonction jouerSon 

La methode est critique dans notre classe car son fonctionnement est le seul moyen de placer la tête de lecture à un endroit donné de la piste. La fonction Play() de l’objet son renvoie aussi un objet SoundChannel qui lui aussi est important pour le fonctionnement de la classe. Il reseinge sur la lecture en cours, on lui applique le volume, et enfin il émet un évènement lorsque la piste est en fin de lecture. Pour cela cet objet est placé sous une variable de classe. Nous voyons que chaque fois que nous faisons appel à la fonction play(), un nouvel objet SoundChannel est généré, le morceau et le canal de diffusion sont donc réinitialisés, ainsi que le volume.

Ce que nous devons remarquer en premier c’est la vérification de l’existence d’un SoundChannel (canalson). Si nous ne mettons pas en place ce comportement. Chaque fois que cette fonction sera invoquée pendant la lecture d’un son cela génèrera un nouveau canal son et le son se surajoutera au précédent puisque flash à des capacité multipistes. Ainsi nous vérifions que l’objet existe, pour ne pas émettre d’erreur au premier lancement, et nous coupons la diffusion du son en prévision de sa toute prochaine remise en route. Cela permet aussi d’éviter de mauvaise surprise au texte de debbugage : que se passerait-il si l’utilisateur appuie plusieurs fois sur le bouton play ?

Nous commençons à écrire notre méthode en considérant à présent qu’un son est peut être déjà en cours de diffusion. Nous commençons donc par vérifier si un canal de diffusion est ouvert. Si c’est la cas nous vérifions s’il à un écouteur d’évènement de fin de lecture et nous le retirons le cas échéant. Enfin on coupe la lecture du canal. 

Nous appliquons ensuite les processus de mise en lecture. Récupération du nouveau canal de diffusion à l’appel de la fonction play(). La fonction prend un paramètre pour le positionnement de la tête de lecture. Nous la relions à une variable de classe qui permettra les réglages depuis la classe principale. Puis nous lui (ré) attribuons le volume sonore en cours (sinon la piste part au volume maximum). 

public function jouerSon ():void

{

      if(canal_son != null)

      {

      if (canal_son.hasEventListener(Event.SOUND_COMPLETE));  canal_son.removeEventListener(Event.SOUND_COMPLETE, finLecture);  ()

}

canal_son = (position_lecture);

canal_son.addEventListener(Event.SOUND_COMPLETE, finLecture); volume = volume_son;

}

Processus attachés à la fonction jouerSon  

 - Processus de (re)positionnement de la tête de lecture. 

Dans la fonction jouerSon nous utilisons la variable  position_lecture afin de placer la tête de lecture à l’endroit choisi. Nous allons avoir besoin de manipuler cette variable pour la naviagtion, la pause ou l’arret ou de la consulter pour l’affichage du témoin de progression.

Nous choisissons à nouveau un getter setter pour cette fois-ci s’assurer que la fonction renverra toujours une valeur  de type number utilisable pour un processus automatique. Pour cela on verifie l’existence d’un canal  de diffusion sinon on renvoie la valeur 0.

public function get position():Number 

      if (canal_son != null)        var pos:Number = canal_son.position;

      else

      pos = 0;

      //trace("POSITION : " + pos);       return pos 

}

public function set position(pos:Number):void  {position_lecture  = pos;}   

-   Diffusion de l’évènement de fin de lecture

L’évènement SOUND_COMPLETE est relié à une simple fonction de redirection. De cette manière nous n’aurons qu’un écouteur permanent dans la classe principale.  Dont la fonction se chargera de lancer la piste suivante. Nous en profitons pour couper la connection au serveur afin de rendre l’objet éligible à la destruction.

private function finLecture(even:Event):void

{

      dispatchEvent(even); }

-   Adaptation de la function de chargement à la lecture multiple

Nous reprenons maintenant la function de chargement pour lui adjoindre les processus de retraits et de verification pour une diffusion multiple.

:

public function chargerNouveauSon (pfichier : String , tamponLecture : int

= 5000):void

{

      if(son != null)

{

      // remise à Zero        position_lecture = 0;

();

// nous devons verifier l'existence des écouteur dans le cas ou 

// l'utilisateur a actionné les boutons précédents et suivants

if (canal_son.hasEventListener(Event.SOUND_COMPLETE));

{canal_son.removeEventListener(Event.SOUND_COMPLETE, finLecture);} retraitEcouteursSon();

}

// instanciation d'un objet Sound son = new Sound();

var contexteAudio : SoundLoaderContext = new SoundLoaderContext

(tamponLecture);

// mise en place des divers écouteurs permettant d’établir les

// comportements adequats

son.addEventListener( ProgressEvent.PROGRESS, chargementEnCours ); son.addEventListener( Event.COMPLETE, retraitEcouteursSon); son.addEventListener( IOErrorEvent.IO_ERROR, erreurChargement ); son.addEventListener(Event.ID3, id3Detecte);

// chargement dynamique du son

var requete : URLRequest = new URLRequest (pfichier) ; (requete, contexteAudio);

}

Manipulation du son.

volume

Maintenant voyons comment nous pouvons agir sur le volume. Comme la plupart des propriétés exprimant une proportion dans flash, le volume est une valeur décimale comprise entre 0 et 1. Un processus un peu particulier est à remarquer ici : le fait de modifier l’objet SoundTransform ne modifie pas directement le volume. Il faut, pour que la transformation s’opère, réattribuer l’objet modifié SoundTransform à la propriété idoine de l’objet SoundChannel :

public function set volume(pvolume:Number):void

{ var transformation : SoundTransform = canal_son.soundTransform; transformation.volume = pvolume; canal_son.soundTransform = transformation;

}

Nous pourrons lancer la méthode simplement à l’aide d’un curseur ou un bouton de volume qui indiquerait le niveau entre 0 et 1. Dans n’importe quelle animation ou diffusion de vidéo, elle pourra nous servir de support pour un bouton « mute ». (ndr : Le son d’une vidéo est codé par le même objet SoundTransform.).

Arrêt, Pause, Avancer reculer dans la piste.

Il n’existe pas à proprement parler de fonction de recherche sur la piste ou de pause. Tous ces comportements peuvent malgré cela être programmés en utilisant et en stockant la propriété position de l’objet SoundChannel , puis de relancer la lecture avec la nouvelle position ou de reprendre à l’endroit ou elle à été pausée.

La propriété position transmet le temps écoulé en millisecondes par rapport au début du son. Mis en proportion avec la propriété  « length » de l’objet Sound, il devient possible de transmettre une proportion de la position d’un curseur et de la traduire en un point temporel sur lequel caler la tête de lecture. 

Pauser  /  lancer la lecture .

La pause, du fait de l’architecture de flash, consistera à stopper le morceau en diffusion en prenant soin de stocker sa position au moment de l’action. Remarquons que la méthode stop() se trouve sur l’objet SoundChannel et non l’objet Sound() comme on pourrait logiquement le croire.

public function pauserSon():void

{

      if(canal_son != null)

      {      

            position_lecture = canal_son.position;          ();

      }

}

Il suffit alors de laisser la fonction jouerSon () reprendre la lecture à la position stockée dans notre variable. 

Arrêt Complet

Un morceau une fois stoppé avec la méthode stop() de l’objet SoundChannel n’est pas fermé tant que la connexion n’est pas fermée.  Ceci se fait en utilisant la méthode close() liée à la classe Sound. Cette instruction n’est utile que si le fichier est arrêté alors que le chargement n’est pas terminé. Cette methode provoque une erreur à l’exécution si on y fait appel alors que le chargement du morceau à été complété.

De fait, Nous n’allons pas utiliser ce processus dans notre application. De plus le bouton stop sera une simple remise à zéro de la position de le tête de lecture son et l’arrêt de la diffusion. De ce fait l’utilisateur pourra utiliser la fonction play pour reprendre la lecture depuis la dernière plage lue :

public function stopperSon():void

{

      if(canal_son != null)

      {      

            position_lecture = 0;

            ();

            if (canal_son.hasEventListener(Event.SOUND_COMPLETE));        canal_son.removeEventListener(Event.SOUND_COMPLETE, finLecture);

      }

}

Notre classe est maintenant fonctionnelle. Nous allons tout de même lui adjoindre un fonction de confort pour l’affichage du temps écoulé et total. L’affichage en millisecondes n’est sans doute pas du meilleur effet et il nous faut trouver un moyen de présenter les temps sous la forme commune MM : SS. 

 public function formaterDuree(nb_millisecondes:Number):String

{

      var tps : Date = new Date();

      tps.setHours(null, null, null, nb_millisecondes);     var min:String = tps.getMinutes().toString();       var sec:String = tps.getSeconds().toString();

      if(tps.getMinutes() < 10)

      {

            min = "0"+ tps.getMinutes();

      }

      if(tps.getSeconds() < 10)

      {

            sec = "0"+ tps.getSeconds();

      }

      var temps_formate:String= min+":"+sec;

      return temps_formate;

}  

Voici l’ensemble de la classe dotées des fonctions que nous avons détaillé : 

 package  {

import flash.events.Event; import flash.events.EventDispatcher; import flash.events.IOErrorEvent; import flash.events.ProgressEvent; import flash.media.ID3Info; import flash.media.Sound; import flash.media.SoundChannel; import flash.media.SoundLoaderContext; import flash.media.SoundTransform; import .URLRequest;

public class PisteSon extends EventDispatcher

{

private var son:Sound;

private var canal_son:SoundChannel; private var position_lecture : Number = 0; private var volume_son : Number = 1; public var ID3 :ID3Info; public var longueur_morceau:Number=0;

public var index_piste:int = 0;

public function PisteSon()

{

}

public function pauserSon():void

{

      if(canal_son != null)

      {      

            position_lecture = canal_son.position;          ();

      }

}

public function stopperSon():void

{

      if(canal_son != null)

      {      

            position_lecture = 0;

            ();

  if (canal_son.hasEventListener(Event.SOUND_COMPLETE)); canal_son.removeEventListener(Event.SOUND_COMPLETE, finLecture);

      }

}

public function get position():Number 

      if (canal_son != null)        var pos:Number = canal_son.position;

      else

      pos = 0;

      //trace("POSITION : " + pos);       return pos 

}

public function set position(pos:Number):void  

      position_lecture  = pos;

public function jouerSon ():void

{

      if(canal_son != null)

      {

  if (canal_son.hasEventListener(Event.SOUND_COMPLETE)); canal_son.removeEventListener(Event.SOUND_COMPLETE, finLecture);

            ()

      }

      canal_son = (position_lecture);

      canal_son.addEventListener(Event.SOUND_COMPLETE, finLecture);     volume = volume_son;

}

private function finLecture(even:Event):void

{

      dispatchEvent(even);

}

public function set volume( pvolume:Number):void

{

      // mise en mémoire du volume pour l'appliquer de piste en piste.

      volume_son = pvolume;

      if (canal_son != null)

      {

            var transfo_son: SoundTransform = canal_son.soundTransform;         transfo_son.volume = pvolume;             canal_son.soundTransform = transfo_son;  

      }

}

// -------------------- CHARGEMENT SON EXTERNE --------------------- //

public function chargerNouveauSon (pfichier : String , tamponLecture : int

= 5000):void

{

      if(son != null)

      {

            // remise à Zero          position_lecture = 0;        ();

// nous devons verifier l'existence des écouteur dans le cas ou 

// l'utilisateur a actionné les boutons précédents et suivants    if (canal_son.hasEventListener(Event.SOUND_COMPLETE));    canal_son.removeEventListener(Event.SOUND_COMPLETE, finLecture);

            retraitEcouteursSon();

      }

      // instanciation d'un objet Sound   son = new Sound();

      var contexteAudio : SoundLoaderContext = new SoundLoaderContext

(tamponLecture);

      // mise en place des divers écouteurs permettant d’établir les

      // comportements adequats

      son.addEventListener( ProgressEvent.PROGRESS, chargementEnCours );  son.addEventListener( Event.COMPLETE, retraitEcouteursSon);       son.addEventListener( IOErrorEvent.IO_ERROR, erreurChargement );      son.addEventListener(Event.ID3, id3Detecte);

      // chargement dynamique du son

      var requete : URLRequest = new URLRequest (pfichier) ;

      (requete, contexteAudio);

      jouerSon();

}

public function id3Detecte(even:Event):void

{

      trace("Detection ID3");

      son.removeEventListener(Event.ID3, id3Detecte);       ID3 = son.id3;

      dispatchEvent(even);

}

public function chargementEnCours ( pEvt:ProgressEvent ):void

{

      dispatchEvent(pEvt);    longueur_morceau = son.length;

}

private function retraitEcouteursSon(even:Event = null):void

{

      if (son.hasEventListener(Event.COMPLETE))

      {

      son.removeEventListener( ProgressEvent.PROGRESS, chargementEnCours );  son.removeEventListener( Event.COMPLETE, retraitEcouteursSon);  son.removeEventListener( IOErrorEvent.IO_ERROR, erreurChargement );

      }

      if (son.hasEventListener(Event.ID3))

      {

            son. removeEventListener(Event.ID3, id3Detecte);

      }

}

private function erreurChargement (even : IOErrorEvent):void

{

      retraitEcouteursSon();

      dispatchEvent(even);

}

public function formaterDuree(nb_millisecondes:Number):String

{

      var tps : Date = new Date();

      tps.setHours(null, null, null, nb_millisecondes);     var min:String = tps.getMinutes().toString();       var sec:String = tps.getSeconds().toString();

      if(tps.getMinutes() < 10)

      {

            min = "0"+ tps.getMinutes();

      }

      if(tps.getSeconds() < 10)

      {

            sec = "0"+ tps.getSeconds();

      }

      var temps_formate:String= min+":"+sec;    return temps_formate;

}

}

}

Nous disposons d’une classe de lecture de son personnalisée et suffisamment souple et simple pour être utilisé dans quasiment tous les contextes d’application. Il nous reste maintenant à gérer le déroulement de la lecture des pistes et l’utilisation des boutons de contrôles crées dans le fla.

Nous retournons donc dans notre classe principale pour relier  notre classe aux divers contrôles que l’on souhaite mettre en place.

Ajout et écoute du lecteur sur la classe principale.

Voici les différents noms d’occurrence donnés aux éléments de la scène de mon application. Je peux y accéder  directement dans ma classe principale. Ce qui me permet de ne pas me préoccuper du placement des différents élément. La seule contrainte de ce système est de veiller  à ne modifier les tailles en passant par l’intérieur des clips concernés. De cette manière les paramètres d’échelle restent à 1.

Nous allons placer maintenant notre lecteur dans la classe principale ainsi que deux écouteurs pour le rafraîchissement des informations du morceau et celui du signal de fin de lecture.  Nous aurons à l’issue de cela un départ de la lecture du premier morceau suivi des autres en boucle Le point d’insertion de notre application s’est reporté de la fonction init() à la fonction de reception du xml. Logiquement puisque nous devons disposer des adresses pour lancer la lecture. C’est par conséquent la fonction xmlCharge qui va recevoir nos instructions de départ. Le code qui suit ne reprend que les fonctions modifiées par ce comportement et les variables à ajouter.

  package

{

…  

public class LecteurSonIDE extends MovieClip

{            

// Coeur de l'application contient toutes les fonction pour la manipulation des pistes jouées

private var lecteur:PisteSon

// Liste des Objets BadgeSon

private var tab_liste:Array = new Array(); // stockage des infos xml (optionnel) private var donnees_xml:XML;   

// Premet la gestion de plages private var morceau_en_cours:int = 0; public function LecteurSonIDE() 

{…}

private function init(e:Event = null):void 

{ removeEventListener(Event.ADDED_TO_STAGE, init);

      // l’objet est instancé dès la fonction init lecteur = new PisteSon();

      //reception des infos du fichier

      lecteur.addEventListener(Event.ID3, messagesPiste);  lecteur.addEventListener(Event.SOUND_COMPLETE, jouerSuivant);

      …

}

private function jouerSuivant(even:Event = null):void 

{

      trace(tab_liste[morceau_en_cours].fichier);    if (morceau_en_cours < tab_liste.length -1)

      {

            morceau_en_cours++;

      lecteur.chargerNouveauSon(tab_liste[morceau_en_cours].fichier);

      }

      else if(morceau_en_cours == tab_liste.length -1)

      {

            morceau_en_cours = 0;

      lecteur.chargerNouveauSon(tab_liste[morceau_en_cours].fichier); }

}

// reception des infos ID3 et rafraichissement des badges  private function messagesPiste(even:Event):void 

{

      tab_liste[morceau_en_cours].ID3 = lecteur.ID3;        = tab_liste[morceau_en_cours].description; for (var v : * in lecteur.ID3)

            {

            trace(v+" : "+lecteur.ID3[v]);

            }

}

// ---------------------CHARGEMENT XML -------------------------//

public function chargementXML(url:String) : void

{…}

// L'application est deployée ici private function XMLCharge(even:Event):void

                {      

//standardisation des infos

// xml au moyen d'un objet personnalisé BadgeSon

for (var i :int; i < donnees_xml.playlist.morceau.length();i++ )

                      {…}

                      // Chargement du premier morceau

                      lecteur.chargerNouveauSon(tab_liste[0].fichier);

      lecteur.jouerSon();    // mise en route de l'horloge.

                      horloge.start()

                }                        

}

}  

Liaisons fla – classe principale – Classe PisteSon.

Parmi les éléments présents sur la scène de mon fichier fla, il en existe deux catégories : 

-    Les éléments finaux (boutons ply , pause, précédent…)

-    Ceux dont nous allons nous servir pour créer des comportements supplémentaires et manipuler pendant l’execution du lecteur. Parmi ceux là on discerne les Textfield des éléments de type curseur. (« curseur » et curseur_volume) .

Nous allons créer l’affichage et l’interactivité de ces derniers dans notre classe principale. Leur présence sur la scène nous permet de gérer leur taille et emplacement, éventuellement de permettre quelques traitements graphiques accessoires tels que l’ajout de filtre sur ces occurrences.

Traitement graphique de la navigation et du volume

En créant un curseur qui renvoie une proportion de sa position par rapport à sa longueur totale, soit un decimal compris entre 0 et 1, nous pouvons facilement faire des modifications en ce servant de ces chiffres comme opérateur, on obtient alors une proportion du morceau en cours, et donc d’afficher la position de al tête de lecture et inversement de recaler notre morceau à un autre endroit : La fonction de navigation consistera simplement à appeler la fonction jouer Son du lecteur en modifiant les données du curseur en un ratio entre 0 et 1 de la durée du morceau.

Le code suivant va dessiner un rectangle représentant une barre de progression. Chaque clic de souris sur cette barre va dessiner une forme graphique en prenant la valeur de l’abscisse local de la souris dans le rectangle. (dessinerTemoin())

Cette fonction pourra être également utilisée pour la visualisation du curseur de volume

Je vous propose ici deux variantes de cette même fonction, la seconde desine une barre à l’aide de l’outil de dégradé : 

function dessinerTemoin(largeur:Number, hauteur : Number, couleur : Number

= 0x0066FF,palpha : Number = 0.7):Sprite

{

      // création d’un objet doté des capacités de graphics       var dessin:Sprite = new Sprite();

      // Création du remplissage selon les paramètres de la fonction dessin.graphics.beginFill(couleur,palpha);

      // création de la forme

      dessin.graphics.drawRoundRect(0,0,largeur,hauteur,3,3);

      // desactivation des actions souris   dessin.mouseEnabled = false;        return dessin;

}

La seconde crée un graphique doté d’un dégradé

private function dessinerTemoin(largeur:Number, hauteur:Number, couleur_1:Number = 0x0066FF, couleur_2:Number = 0x0033CC):Sprite

{

      //on utilise un sprite pour l’ecriture de la forme    var objet:Sprite = new Sprite();

      // creation d’une matrice de transformation pour le dégradé       var matrix:Matrix = new Matrix();

      // Equivaut à l’outil de transformation de dégadé 

      // La rotation se donne en radians

      matrix.createGradientBox(largeur, hauteur, 1.575);

      // création d’un remplissage en dégradé

      objet.graphics.beginGradientFill(GradientType.LINEAR,

                                                [couleur_1, couleur_2],

                                                [.8, .8],                                        [65, 220],                                              matrix,                                              "pad",

                                                "rgb",

 1);

      // dessin de la forme

      objet.graphics.drawRoundRect(0, 0, largeur, hauteur,5,5);

      // retrait de la sensibilité souris   objet.mouseEnabled=false;     //renvoi dde la forme

      return objet;

}

Nous conservons la première version afin de ne pas alourdir par trop la compréhension du code. Cette même fonction va donc remplir trois rôles : Illustrer le chargement de la piste, celui de la progression du morceau et celui du curseur de volume.  On voit ici tout l’intérêt de créer un fonction la plus générique possible.

Nous allons employer la même technique de fonctionnement de l’affichage pour ces comportements : Nous plaçons dans les clips réceptacles, un container de type sprite qui contiendra les formes crées par l’une des méthodes ci-dessus. La forme sera ôtée à chaque renouvellement. En ce qui concerne celui-ci nous allons relier la barre de chargement à  l’écoute de celui-ci sur le lecteur. Nous allons avoir besoin également d’un timer et de son système évènementiel. pour assurer le rafraîchissement du témoin de progression.

Enfin nous allons écouter les click sur le témoin de progression pour la navigation dans la piste et sur le clip curseur_volume pour la gestion de celui-ci.

Nous ajoutons donc quatre variables de classe à celles déjà présentes dans lecteurSonIDE :

private var layer_lecture : Sprite = new Sprite(); private var layer_chargement : Sprite = new Sprite();

// Implanté dans le curseur_volume

private var layer_volume : Sprite = new Sprite(); 

// L'horloge premet de raffraichir la barre de progression private var horloge:Timer = new Timer(100);

puis à la fin de la fonction init nous assemblons les éléments et les mettons en écoute : 

private function init(e:Event = null):void 

{

      // ajout d’une ecouteur de progression de chargement 

       lecteur.addEventListener(ProgressEvent.PROGRESS, dessinerChargement);

                  ///… (code inchangé) ///

      // ajout des éléments au clip "curseur" de la scène   curseur.addChild(layer_chargement);      curseur.addChild(layer_lecture);

      // ajout du sprite d'affichage à l'élément curseur volume   curseur_volume.addChild(layer_volume);   curseur.buttonMode = true;

      curseur_volume.buttonMode = true;

      // Mise en ecoute de la navigation via la barre de progression   curseur.addEventListener(MouseEvent.CLICK, allerA);

      curseur_volume.addEventListener(MouseEvent.CLICK, gestionVolume);

      // préremplissage du curseur de volume ( A fond par defaut) var forme_volume :Sprite  = dessinerTemoin(curseur_volume.width, curseur_volume.height);

forme_volume.y = curseur_volume.height - forme_volume.height; layer_volume.addChild(forme_volume);

}  

private function gestionVolume(even:MouseEvent):void

{

      // conversion de l'emplacement du clic en une fraction de 1       var ratio = (curseur_volume.mouseY - curseur_volume.height) / curseur_volume.height;

      //retrait de le forme si presente

      while(layer_volume.numChildren >0)

      {

            layer_volume.removeChildAt(0);

      }

      // Commande de volume sur le lecteur.     lecteur.volume = ratio

// On place une nouvelle barre de la taille correspondante à l'emplacement 

// du clic

var forme_volume :Sprite = dessinerTemoin(curseur_volume.width, curseur_volume.height * ratio);

forme_volume.y = curseur_volume.height - forme_volume.height; layer_volume.addChild(forme_volume);

// Function de navigation dans la piste et de raffraichissement de la 

// progression

private function allerA(even : MouseEvent) :void

{

      lecteur.stopperSon();

// conversion de l'emplacement du clic en une fraction de 1       var ratio = curseur.mouseX / curseur.width;

      // reprise du son par rapport à la nouvelle position de la tête de lecture

      lecteur.position = lecteur.longueur_morceau * ratio;        lecteur.jouerSon();

      // Si un forme est déjà présente , on l'enlève   if(layer_lecture.numChildren > 0)

      {

            layer_lecture.removeChildAt(0);

      }

// On place une nouvelle barre de la taille correspondante à 

//l'emplacement du clic

layer_lecture.addChild( dessinerTemoin(curseur.mouseX, curseur.height));

// Fonctions de mise à jour du temoin de progression

private function rafraichirTemoin (even:Event):void

{

      var nouvelle_pos : Number = (lecteur.position / lecteur.longueur_morceau)* curseur.width;

      while(layer_lecture.numChildren > 0)

      {

            layer_lecture.removeChildAt(0);

      }

      var forme_curseur : Sprite = dessinerTemoin(nouvelle_pos, curseur.height);

      layer_lecture.addChild(forme_curseur);

      =

lecteur.formaterDuree(lecteur.position)+"/"+lecteur.formaterDuree(lecteur.l ongueur_morceau);

      //trace()

}

private function dessinerChargement(pEvt:ProgressEvent):void

{

      while(layer_chargement.numChildren > 0)

      {

            layer_chargement.removeChildAt(0);

      }

      var ratio:Number = pEvt.bytesLoaded / pEvt.bytesTotal ;     var taille_temoin:Number = curseur.width * ratio;  var temoin_chargement:Sprite= dessinerTemoin(taille_temoin, curseur.height, 0x99FF00, 0x00CC33)

      layer_chargement.addChild(temoin_chargement);

A partir de ce moment nous pouvons naviguer dans la piste modifier son volume et voir la progression de son chargement . Il nous reste à poser l’écouteur et la gestion des clics sur le clip « controles ».

Nous la donnons ici dans la classe maintenant au complet (en gras) : 

package {

import flash.display.MovieClip; import flash.events.Event; import flash.events.MouseEvent; import flash.events.ProgressEvent; import flash.events.TimerEvent; import .URLLoader; import .URLRequest; import flash.display.Sprite import flash.utils.Timer; import flash.display.SimpleButton; import .TextField;

public class LecteurSonIDE extends MovieClip

{

// Switch pour le statut du lecteur  

private var statut_silence : Boolean = false;

// Coeur de l'application contient toutes les fonction pour la manipulation 

// des pistes jouées private var lecteur:PisteSon

// Liste des Objets BadgeSon

private var tab_liste:Array = new Array(); // stockage des infos xml (optionnel) private var donnees_xml:XML;

// ces objets sont implantés dans le clip "curseur" pour afficher 

// les infos de progression de chargement de lecture . private var layer_lecture : Sprite = new Sprite(); private var layer_chargement : Sprite = new Sprite();

// Implanté dans le curseur_volume

private var layer_volume : Sprite = new Sprite();

// L'horloge premet de raffraichir la barre de progression private var horloge:Timer = new Timer(100);

// Premet la gestion de plages

private var morceau_en_cours:int = 0;

public function LecteurSonIDE() 

{

       if(stage)

       addEventListener(Event.ADDED_TO_STAGE, init);

       else   init()

}

private function init(e:Event = null):void 

{

       lecteur = new PisteSon();

       removeEventListener(Event.ADDED_TO_STAGE, init);

       // Mise en place des écouteurs

       horloge.addEventListener(TimerEvent.TIMER, rafraichirTemoin);

       //reception des infos du fichier

       lecteur.addEventListener(Event.ID3, messagesPiste);

       lecteur.addEventListener(ProgressEvent.PROGRESS, dessinerChargement);  lecteur.addEventListener(Event.SOUND_COMPLETE, jouerSuivant) ;

       //Mise en ecoute du clip contrôle en activant « useCapture »          controles.addEventListener(MouseEvent.CLICK, gestionControles, true);

       // recherche de l’adresse du xml

       if (loaderInfo.parameters.liste != undefined)

       {

              chargementXML(loaderInfo.parameters.liste)

       }

       else

       {

              chargementXML("")

       }

       // ajout des éléments au clip "curseur" de la scène   curseur.addChild(layer_chargement); curseur.addChild(layer_lecture);

       // ajout du sprite d'affichage à l'élément curseur volume   curseur_volume.addChild(layer_volume);        curseur.buttonMode = true;

       curseur_volume.buttonMode = true;

       // Mise en ecoute de la navigation via la barre de progression   curseur.addEventListener(MouseEvent.CLICK, allerA);

       curseur_volume.addEventListener(MouseEvent.CLICK, gestionVolume);

       // préremplissage du curseur de volume ( A fond par defaut)        var forme_volume :Sprite  = dessinerTemoin(curseur_volume.width, curseur_volume.height);

       forme_volume.y = curseur_volume.height - forme_volume.height;  layer_volume.addChild(forme_volume);

}

private function gestionVolume(even:MouseEvent):void

{

// conversion de l'emplacement du clic en une fraction de 1

       var ratio = (curseur_volume.mouseY - curseur_volume.height) / curseur_volume.height;

// reprise du son par rapport à la nouvelle position de la tête de lecture

while(layer_volume.numChildren >0)

       {

              layer_volume.removeChildAt(0);

       }

       lecteur.volume = ratio

// On place une nouvelle barre de la taille correspondante à l'emplacement 

// du clic

   var forme_volume :Sprite = dessinerTemoin(curseur_volume.width, curseur_volume.height * ratio);

                     forme_volume.y = curseur_volume.height - forme_volume.height;               layer_volume.addChild(forme_volume);

}

private function gestionControles(even:MouseEvent):void

{

if ( == "bt_prochain")

       {

              jouerSuivant();

       }

if ( == "bt_precedent")

       {

if (morceau_en_cours > 0)

              {

                     morceau_en_cours--;

                     lecteur.index_piste = morceau_en_cours;

              lecteur.chargerNouveauSon(tab_liste[morceau_en_cours].fichier);

              }

else if(morceau_en_cours == 0)

              {

                     morceau_en_cours = tab_liste.length -1;        lecteur.index_piste = morceau_en_cours; 

       lecteur.chargerNouveauSon(tab_liste[morceau_en_cours].fichier);

              }

       }

       if (even.target.name == "bt_stop")

       {

              horloge.stop();            lecteur.stopperSon();

champ_temps.text =

lecteur.formaterDuree(0)+"/"+lecteur.formaterDuree(0000);   layer_lecture.removeChildAt(0);

       }

if ( == "bt_play")

       {

              horloge.start()

              lecteur.jouerSon();

       }

if ( == "bt_pause")

       {

              horloge.stop();

              lecteur.pauserSon()

       }

}

// Connexion des éléments de la scène et du lecteur 

private function allerA(even : MouseEvent) :void

{

       // conversion de l'emplacement du clic en une fraction de 1        var ratio = curseur.mouseX / curseur.width;

       // reprise du son par rapport à la nouvelle position de la tête de lecture   lecteur.position = lecteur.longueur_morceau * ratio;        lecteur.jouerSon();

       // Si un forme est déjà présente , on l'enlève       if(layer_lecture.numChildren > 0)

       {

              layer_lecture.removeChildAt(0);

       }

       // On place une nouvelle barre de la taille correspondante à l'emplacement du clic

layer_lecture.addChild( dessinerTemoin(curseur.mouseX, curseur.height)); }

private function jouerSuivant(even:Event = null):void 

{

       trace(tab_liste[morceau_en_cours].fichier);   if (morceau_en_cours < tab_liste.length -1)

       {

              morceau_en_cours++;

              lecteur.chargerNouveauSon(tab_liste[morceau_en_cours].fichier);

       }

       else if(morceau_en_cours == tab_liste.length -1)

       {

              morceau_en_cours = 0 ;

lecteur.chargerNouveauSon(tab_liste[morceau_en_cours].fichier);

       }

              }

private function messagesPiste(even:Event):void 

{

              tab_liste[morceau_en_cours].ID3 = lecteur.ID3;              = tab_liste[morceau_en_cours].description;            for (var v : * in lecteur.ID3)

              {

                     trace(v+" : "+lecteur.ID3[v]);

              }

}

// ---------------------CHARGEMENT XML -------------------------//

public function chargementXML(url:String) : void

{

       var loader:URLLoader = new URLLoader();        var requete :URLRequest = new URLRequest(url);  loader.addEventListener(Event.COMPLETE, XMLCharge);  (requete);

}

// L'application est deployée ici private function XMLCharge(even:Event):void

{

var loader:URLLoader = even.target as URLLoader;

donnees_xml = new XML();

//XML.prettyIndent = 0;

//XML.ignoreWhitespace = true;

//standardisation des ifos xml au moyen d'un objet personnalisé BadgeSon

for (var i :int; i < donnees_xml.playlist.morceau.length();i++ )

{

 

var badge:BadgeSon = new BadgeSon();

badge.index = i;

badge.titre = donnees_xml.playlist.morceau[i].titre;

badge.artiste = donnees_xml.playlist.morceau[i].artiste;

badge.fichier = donnees_xml.playlist.morceau[i].fichier;

(badge);

}

 

        // Chargement du premier morceau

        lecteur.chargerNouveauSon(tab_liste[0].fichier);

        lecteur.jouerSon();

        // mise en route de l'horloge.



}

        horloge.start()

// Fonctions de mise à jour du temoin de progression

private function rafraichirTemoin (even:Event):void

{

 var nouvelle_pos : Number = (lecteur.position / lecteur.longueur_morceau)* curseur.width;

       while(layer_lecture.numChildren > 0)

       {

              layer_lecture.removeChildAt(0);

       }      var forme_curseur : Sprite = dessinerTemoin(nouvelle_pos, curseur.height);

       layer_lecture.addChild(forme_curseur);

=

lecteur.formaterDuree(lecteur.position)+"/"+lecteur.formaterDuree(lecteur.longueur_ morceau);

       //trace()

}

private function dessinerChargement(pEvt:ProgressEvent):void

{

       while(layer_chargement.numChildren > 0)

       {

              layer_chargement.removeChildAt(0);

       }

       var ratio:Number = pEvt.bytesLoaded / pEvt.bytesTotal ;     var taille_temoin:Number = curseur.width * ratio;

       var temoin_chargement:Sprite= dessinerTemoin(taille_temoin, curseur.height,

0x99FF00, 0x00CC33)

       layer_chargement.addChild(temoin_chargement);

}

private function dessinerTemoin(largeur:Number, hauteur : Number, couleur : Number

= 0x0066FF,palpha : Number = 0.7):Sprite

{

       var dessin:Sprite = new Sprite();  dessin.graphics.beginFill(couleur,palpha); dessin.graphics.drawRoundRect(0,0,largeur,hauteur,3,3); dessin.mouseEnabled = false;     return dessin;

}

}

.

Mise en place de la playlist 

Nous finirons avec notre lecteur en abordant un objet optionnel : l’affichage d’une liste de lecture interactive.

Nous allons en profiter pour faire la rencontre d’une fonction fort interessante dans le langage actionscript qui va nous permettre de copier l’aspect d’un clip et de ce qu’il contient sur un objet bitmap et cela à l’exécution. 

Prenons d’abord le temps de réfléchir aux besoins d’un tel objet :  J’ai besoin : 

-    D’une série de bouton sur lesquels j’inscrirai ces informations. Il faudra donc que je les crée de façon dynamique.

-    d’une liste constituée des données à afficher. 

-    Je vais avoir besoin de relier ces boutons au fonctionnement de notre lecteur.

-    Je vais avoir besoin d’habiller les boutons et l’objet lui-même. -       Je voudrai pouvoir rafraîchir les titres à l’aide des infos id3.

Prenons ces différents point et considérons les solutions que nous pouvons appliquer à ces questions. 

Pour cela gardons en tête que la construction d’une class prend du temps et essayons d’appliquer des solution qui soient transposables dans d’autres situations.

1 les boutons. 

Les boutons ici auront un fonctionnement très simple. Inutile de créer une sous-classe pour cela. Une seule fonction créant l’objet à l’aide des données de la classe et des informations des objets BadgeSon devrai suffire. Nous utiliserons une boucle de remplissage qui appellera la fonction de création de boutons.

/* La fonction de création montre un procédé intéressant Comment faire une copie visuelle de type Bitmap à partir un objet d'affichage ou un groupe d'objet : On obtient alors une "photo" de l'objet

(plus d'interactivité). Manupulable à volonté. */

/* Pour les différents états du bouton je modifie les propriété d'un même objet. Comme si, en photo je demandais au modèle de changer de pose pour chaque nouvelle image... */

private function creerEtiquette(badge:BadgeSon):MovieClip

{

// création d'un container pour pouvoir utiliser les outils de dessin

(surbrillance)

var cont:MovieClip = new MovieClip();

// création du bouton 

var bt : SimpleButton = new SimpleButton();

// je donne au bouton comme nom l'index transformé en chaine de caractères. = String(badge.index);

// Je crée un sprite

var fond:Sprite = new Sprite();

// Création de l'état normal à partir de données du xml fond.graphics.beginFill(skin.normal.@couleur, skin.normal.@alpha) fond.graphics.drawRect(0, 0, limites.width, 20);

// Création du champ texte à l'état normal var tf:TextField = new TextField();

// définition du format

var format : TextFormat = new TextFormat(fonte, skin.@taille_txt, skin.normal.@c_texte);

// Attribution d'un format par défaut tf.defaultTextFormat = format;

var str:String = (badge.index + 1) + ": " + badge.artiste + " : " + badge.titre;

// limitation du texte à 30 catactères (pour qu'il ne depasse pas du bouton).

= str.substr(0, 30); tf.height = tf.textHeight + 5; tf.autoSize = ;

tf.x = 10;

// placement du champ texte dans le sprite "fond" fond.addChild(tf);                   

// configuration pour l'état enfoncé et cliqué.

/* L'objet bitmapData est l'objet qui contient effectivement les données  d'image dans un bitmap ("fichier" image). Je l'initialise en lui donnant ses dimensions.*/

// ici je prépare l'image de l'état enfoncé du bouton. var dataDown:BitmapData = new BitmapData(fond.width, fond.height, true, 0); // Puis je le remplis avec la copie de pixels du clip "fond" à l'aide de sa méthode draw.

(fond);

//Copie pour l'état cliqué

var dataHit:BitmapData = new BitmapData(fond.width, fond.height, true, 0); (fond);

//modification du fond et du champ texte avant copie pour l'état normal fond.graphics.clear();

fond.graphics.beginFill(skin.normal.@couleur, skin.normal.@alpha); fond.graphics.drawRect(0, 0, limites.width, skin.@h_etiquette); tf.setTextFormat(new TextFormat(fonte, skin.@taille_txt, skin.normal.@c_texte));

var dataUp:BitmapData = new BitmapData(fond.width, fond.height, true, 0); (fond);

// puis même opération pour l'état survolé fond.graphics.clear();

fond.graphics.beginFill(skin.dessus.@couleur, skin.dessus.@alpha); fond.graphics.drawRect(0, 0, limites.width, 20); tf.setTextFormat(new TextFormat(fonte, skin.@taille_txt, skin.dessus.@c_texte));

var dataOver:BitmapData = new BitmapData(fond.width, fond.height, true, 0); (fond);

// J'instancie un bitamp vide qui va contenir les données des objets bitmapData  

bt.downState = new Bitmap(dataDown); bt.hitTestState = new Bitmap(dataHit); bt.upState = new Bitmap(dataUp); bt.overState = new Bitmap(dataOver);

// placement dynamique de l’objet badge sur le clip pour d’autres modes de fonctionnement que celui présentement employé    cont["badge"] = badge; cont.addChild(bt); return cont;

}            

2 la liste.

Nous allons nous servir de la liste d’objets que nous avons constitué au chargement du xml. Nous fournirons cette liste dès le constructeur de la classe. Elle nous servira à établir la liste graphique et a retrouver le morceau joué lors de la sélection d’un nouveau morceau par l’emploi de la playlist ou d’un autre système de navigation du lecteur.

La création d’une liste graphique en actionScript passe presque toujours par le parcours d’un tableau d’objet ou d’une liste xml à l’intérieur d’une boucle chargé d’interpréter les informations de leur donner une apparence et de les afficher dans l’ordre et l’axe souhaité.

Nous plaçons les boutons dans un container pour pouvoir modifier l’emplacement et de masquer les boutons autres que celui en lecture, le précédent et le suivant.

private var tab_morceaux : Array; // container des boutons

private var layer_liste:MovieClip = new MovieClip();

public function contruirePlayList(): void

{

      for each(var musique:BadgeSon in tab_morceaux)

      {

            var etiquette:MovieClip = creerEtiquette(musique);

// on place le bouton

            etiquette.y = this.height;

// on crée une propriété à la volée dans le badge faisant référence au bouton

            musique["etiquette"] = etiquette          layer_liste.addChild(etiquette);

      }

      creerEtiquetteéation et activation du masque    var forme:Shape = new Shape()     forme.graphics.beginFill(0, 0);

forme.graphics.drawRoundRect(0, 0, limites.width, limites.height, 2, 2);   addChild(forme);        = forme; }

3 Liaison au fonctionnement du lecteur musical.

D’ores et déjà nous savons que nous allons avoir dans cette objet des interactions entrantes ( utilisation de la console, passage automatique au nouveau morceau) et des interactions sortantes (clic sur l’un des morceau de la liste.)

Dans ce cas une bonne solution pourrait être de mettre au point un système de sélection passive. Une fonction publique pourrait être mise en place demandant en paramètre l’index du morceau à sélectionner. De cette manière nous pourrions externaliser également la sélection souris et la leguer à la classe principale qui à déjà en charge l’ensemble des interactions du lecteur. Le problème sera ensuite de pouvoir différencier les boutons entre eux pour retrouver le bon morceau. Bien des solutions s’offrent à nous pour résoudre cette question : 

-    Nous pouvons rendre la classe BadgeSon dynamique afin de lui faire accepter des paramètres non encore écrits sous forme de variables publique. Ainsi nous pourrions y stocker quel est le bouton qui lui est attribué et le retrouver aisément dans la fonction évènementiel et comparant la cible du clic et la référence du bouton dans une boucle.

-    Nous pouvons référencer le numéro d’index , voire le l’objet badge dans une instance de MovieClip englobant le bouton (le MovieClip est une classe dynamique).

-    Nous pouvons tricher avec actionScript et nommer nos boutons avec leur numéro d’index transformé en chaine (le paramètre name ne peut être un objet de type Number). 

Nous choisirons cette dernière pour sa simplicité d’utilisation même si ce n’est pas une méthode très orthodoxe ! Cependant elle fonctionne nous allons le voire sans produire de comportement non attendu.

public function selection(index:int):void

{

      var badge_sel:BadgeSon;

      for each(var badge in tab_morceaux)

      {

            // on recupère le bouton.           var etiquette : MovieClip = badge.etiquette;              if (index != badge.index)

            {

      // si ce n'est pas la selection on efface la surbrillance eventuelle

                  etiquette.graphics.clear();

            }

            else

            {

      // stockage du badge selectionné

                  badge_sel = badge;

      // mise en surbrillance

                  etiquette.graphics.beginFill(0xffffff, 1);              etiquette.graphics.drawRect(1, 1, etiquette.width -2, etiquette.height-2);

            }

      }

      // Placement du bouton au centre de la partie visible de la liste         layer_liste.y = - (badge_sel.etiquette.y - badge_sel.etiquette.height);

            if (badge_sel.index == tab_morceaux.length -1)

            {// si c'est la dernier on le recale en bas

                  layer_liste.y += badge_sel.etiquette.height;

            }

            if (badge_sel.index == 0 )

            {

                  // si c'est le bouton du haut on le laisse au dessus                layer_liste.y = 0;

            }

}

4  l’habillage

L’habillage de notre objet concerne deux choses différentes l’aspect des boutons et l’habillage de la liste. : 

-    Pour les boutons nous allons stocker les données de couleur  pour les différents aspect au sein d’un fragment de xml. Nous disposerons ainsi d’un comportement par defaut que nous pourrons aisement réécrire en fournissant à notre classe un habillage alternatif que nous pourrions faire transiter par les xml externe. Cet aspect implique un éléement important dans la structure de notre classe. Elle ne doit se déclencher qu’une fois les éventuels paramètres alternatifs attribués. Nous choissons pour cela de créer une fonctions qui mettra effectivement en place la liste graphiquement.

private var _skin :XML = 

<skin_playlist h_etiquette = "20" taille_txt="12">

            <select couleur="0xCFCFCF" alpha=".6" />

            <normal couleur="0x12337E" alpha="0.2" c_texte = "0x333333"/>

            <dessus couleur="0x4575E4" alpha="0.2" c_texte = "0xFFFFFF"/>

            <click couleur="0xFFFFFF" alpha="0.4" c_texte = "0x000000"/>

      </skin_playlist>

public function get skin():XML { return _skin; } public function set skin(value:XML):void { _skin = value; }

-    Nous nous laissons également la possiblité de donner à la playlist une typo différente en

stockant la chaine l’identifiant (police.fontName)  et en placant un setter 

private var fonte:String = "arial";

public function set police (_fonte:String) : void {fonte = _fonte; }

-    L’habillage de notre liste elle pourrait être gérée par un clip créé dans le logiciel et rendu exportable dans la bibliothèque. Nous pourrons ainsi en modifier l’aspect pour le conformer aux différentes chartes graphiques dans lesquelles ils devra s’insérer. Le clip comportement un avant plan : un clip imbriqué portant le nom d’occurrence

« coque » et un clip de fond qui sera automatiquement placé sous la liste.

public function set habillage (hab : MovieClip):void

{

      addChildAt(hab, 0)

      var coque:MovieClip = hab.getChildByName("coque") as MovieClip;  addChild(coque)

}

5  rafraîchissement

Nous classe principale comporte déjà une fonction chargé du rafraîchissement des objets lors de la receptions des étiquettes ID3.  Nous pourrions l’exploiter en créant ici une fonction publique prenant en paramètre le numéro d’index pour retrouver le bouton concerné le recréer et le replacer à ses coordonnées initiales.

public function raffraichirTitre(index:int)

{

      for each(var badge in tab_morceaux)

      {

            var etiquette : MovieClip = badge.etiquette; 

            if (index == tab_morceaux.indexOf(badge))

            {

                  etiquette.graphics.clear();          var limites:Rectangle = etiquette.getBounds(layer_liste).clone();

                  etiquette.parent.removeChild(etiquette)         etiquette = creerEtiquette(badge);                badge["etiquette"] = etiquette                  etiquette.y = limites.y;              etiquette.x = limites.x;              layer_liste.addChild(badge.etiquette)

            }

      }

      selection(index)

}

6 Classe playlist au complet :

package {

      import flash.display.Bitmap;        import flash.display.BitmapData;     import flash.display.DisplayObject;  import flash.display.MovieClip;      import flash.display.Shape;    import flash.display.SimpleButton;  import flash.display.Sprite;  import flash.events.MouseEvent;      import .Rectangle;  import .TextField;  import .TextFieldAutoSize;

      import .TextFormat;

      /**

*     - La classe playlist receuille un tableau d'objet badge son .      * - Crée des occurences de boutons à l'aide de la liste et des données du xml (_skin) pour definir couleurs et tailles      * - On deploie la playlist à partir de la fonction construirePlaylist(). Cela laisse la possibilité de fournir        * d'habillage alternatif :

*     - Il est possible de definir un clip contenant le fond de la liste et son habillage. On lui envoie

*     un clip créé dans le logiciel composé de deux autre clips portant comme nom d'occurence "fond" et "coque"

*     - Il est possible aussi de fournir un xml alternatif.        * - la classe ne contient pas d'écouteur. Elle fonctionne mécaniquement en lui fournissant le numéro d'index      * à mettre en surbrillance.

       */

public class PlayList extends MovieClip

{

      // receuille le tableau composé d'objets BadgeSon (funct. const.) private var tab_morceaux : Array;

      // stocke une police alternative (setter : police) private var fonte:String = "arial";       // container des boutons

private var layer_liste:MovieClip = new MovieClip();

// Taille maximale de l'affichage de la liste (funct. const.). Definit la taille du masque

private var limites : Rectangle = new Rectangle(0, 0, 300, 60); // Les paramètres de couleur des bouttons sont paramétrables via un xml

structuré de cette manière : (get. set. skin) private var _skin :XML = 

<skin_playlist h_etiquette = "20" taille_txt="12">

            <select couleur="0xCFCFCF" alpha=".6" />

            <normal couleur="0x12337E" alpha="0.2" c_texte = "0x333333"/>

            <dessus couleur="0x4575E4" alpha="0.2" c_texte = "0xFFFFFF"/>

            <click couleur="0xFFFFFF" alpha="0.4" c_texte = "0x000000"/>

      </skin_playlist>

public function PlayList(morceaux:Array, plimites : Rectangle = null) 

{

      // on definit les nouvelles limites si le paramètre n'est pas nul       if (plimites != null )

      {

            limites = plimites;

      }

      // on associe la liste d'objet BadgeSon au tableau tab_morceau.

      tab_morceaux = morceaux;

      // placement de la liste à x = 0 et Y = 0;      addChild(layer_liste)

}

      // On fournit le nom de la police alternative

public function set police (_fonte:String) : void {fonte = _fonte; }

// On peu fournir un xml sur le modele de skin afin de modifier les couleurs des boutons

public function get skin():XML { return _skin; }

public function set skin(value:XML):void { _skin = value; }

// On peut habiller la playlist avec un clip crée dans flash

// celui ci contient deux clips on récupère celui nommé "coque" 

// pour le placer au dessus de la liste

public function set habillage (hab : MovieClip):void

{

      addChildAt(hab, 0)

      var coque:MovieClip = hab.getChildByName("coque") as MovieClip;  addChild(coque)

}

// Boucle à l'intérieur du tableau de badge afin de créer les boutons et de les placer dans la liste

public function contruirePlayList(): void

{

      for each(var musique:BadgeSon in tab_morceaux)

      {

            var etiquette:MovieClip = creerEtiquette(musique);

// on place le bouton

            etiquette.y = this.height;

// on crée une propriété à la volée dans le badge faisant référence au bouton

            musique["etiquette"] = etiquette          layer_liste.addChild(etiquette);

      }

      creerEtiquetteéation et activation du masque    var forme:Shape = new Shape()     forme.graphics.beginFill(0, 0);

forme.graphics.drawRoundRect(0, 0, limites.width, limites.height, 2, 2);   addChild(forme);        = forme;

}

// On trouve le badge séléctionné pour le mettre en surbrillance. public function selection(index:int):void

{

      var badge_sel:BadgeSon;

      for each(var badge in tab_morceaux)

      {

            // on recupère le bouton.           var etiquette : MovieClip = badge.etiquette;              if (index != badge.index)

            {

      // si ce n'est pas la selection on efface la surbrillance eventuelle

                  etiquette.graphics.clear();

            }

            else

            {

      // stockage du badge selectionné

                  badge_sel = badge;

      // mise en surbrillance

                  etiquette.graphics.beginFill(0xffffff, 1);              etiquette.graphics.drawRect(1, 1, etiquette.width -2, etiquette.height-2);

            }

      }

      // Placement du bouton au centre de la partie visible de la liste         layer_liste.y = - (badge_sel.etiquette.y - badge_sel.etiquette.height);

            if (badge_sel.index == tab_morceaux.length -1)

            {// si c'est la dernier on le recale en bas

                  layer_liste.y += badge_sel.etiquette.height;

            }

            if (badge_sel.index == 0 )

            {

                  // si c'est le bouton du haut on le laisse au dessus                layer_liste.y = 0;

            }

}

// cette fonction permet de raffraichir l'affichage du bouton Pour prendre en compte les infos ID3

public function raffraichirTitre(index:int)

{

      for each(var badge in tab_morceaux)

      {

            var etiquette : MovieClip = badge.etiquette; 

            if (index == tab_morceaux.indexOf(badge))

            {

                  etiquette.graphics.clear();          var limites:Rectangle = etiquette.getBounds(layer_liste).clone();

                  etiquette.parent.removeChild(etiquette)         etiquette = creerEtiquette(badge);                badge["etiquette"] = etiquette                  etiquette.y = limites.y;              etiquette.x = limites.x;              layer_liste.addChild(badge.etiquette)

            }

      }

      selection(index)

}

/* La fonction de création montre un procédé intéressant Comment faire une copie visuelle de type Bitmap à partir un objet d'affichage ou un groupe d'objet : On obtient alors une "photo" de l'objet

(plus d'interactivité). Manupulable à volonté. */

/* Pour les différents états du bouton je modifie les propriété d'un même objet. Comme si, en photo je demandais au modèle de changer de pose pour chaque nouvelle image... */

private function creerEtiquette(badge:BadgeSon):MovieClip

{

// création d'un container pour pouvoir utiliser les outils de dessin

(surbrillance)

var cont:MovieClip = new MovieClip();

// création du bouton 

var bt : SimpleButton = new SimpleButton();

// je donne au bouton comme nom l'index transformé en chaine de caractères. = String(badge.index);

// Je crée un sprite

var fond:Sprite = new Sprite();

// Création de l'état normal à partir de données du xml fond.graphics.beginFill(skin.normal.@couleur, skin.normal.@alpha) fond.graphics.drawRect(0, 0, limites.width, 20);

// Création du champ texte à l'état normal var tf:TextField = new TextField();

// définition du format

var format : TextFormat = new TextFormat(fonte, skin.@taille_txt, skin.normal.@c_texte);

// Attribution d'un format par défaut tf.defaultTextFormat = format;

var str:String = (badge.index + 1) + ": " + badge.artiste + " : " + badge.titre;

// limitation du texte à 30 catactères (pour qu'il ne depasse pas du bouton).

= str.substr(0, 30); tf.height = tf.textHeight + 5; tf.autoSize = ; tf.x = 10;

// placement du champ texte dans le sprite "fond" fond.addChild(tf);                   

// configuration pour l'état enfoncé et cliqué.

/* L'objet bitmapData est l'objet qui contient effectivement les données  d'image dans un bitmap ("fichier" image). Je l'initialise en lui donnant ses dimensions.*/

// ici je prépare l'image de l'état enfoncé du bouton. var dataDown:BitmapData = new BitmapData(fond.width, fond.height, true, 0); // Puis je le remplis avec la copie de pixels du clip "fond" à l'aide de sa méthode draw.

(fond);

//Copie pour l'état cliqué

var dataHit:BitmapData = new BitmapData(fond.width, fond.height, true, 0); (fond);

//modification du fond et du champ texte avant copie pour l'état normal fond.graphics.clear();

fond.graphics.beginFill(skin.normal.@couleur, skin.normal.@alpha); fond.graphics.drawRect(0, 0, limites.width, skin.@h_etiquette); tf.setTextFormat(new TextFormat(fonte, skin.@taille_txt, skin.normal.@c_texte));

var dataUp:BitmapData = new BitmapData(fond.width, fond.height, true, 0); (fond);

// puis même opération pour l'état survolé fond.graphics.clear();

fond.graphics.beginFill(skin.dessus.@couleur, skin.dessus.@alpha); fond.graphics.drawRect(0, 0, limites.width, 20); tf.setTextFormat(new TextFormat(fonte, skin.@taille_txt, skin.dessus.@c_texte));

var dataOver:BitmapData = new BitmapData(fond.width, fond.height, true, 0); (fond);

// J'instancie un bitamp vide qui va contenir les données des objets bitmapData  

bt.downState = new Bitmap(dataDown); bt.hitTestState = new Bitmap(dataHit); bt.upState = new Bitmap(dataUp); bt.overState = new Bitmap(dataOver);

// placement dynamique de l’objet badge sur le clip pour d’autres modes de fonctionnement que celui présentement employé    cont["badge"] = badge; cont.addChild(bt); return cont;

}            

}

}

7 implatation dans la classe principale

Revenus dans notre classe principale nous placons maintenant une variable de classe pour pouvoir accéder à l’objet depuis l’ensemble de nos fonctions : 

private var playlist : PlayList;

puis dans la fonction XMLChargé nous instancions et affichons la playlist au dessus panneau infos. Nous plaçons la propriété visible de celui-ci sur false. Nous pouvons placer dans l’animation un clip chargé de rendre visible soit l’un soit l’autre ainsi l’utilisateur en cliquant accède soit à la playlist soit aux infos ID3 du morceau en cours.

 private function XMLCharge(even:Event):void

{

var loader:URLLoader = even.target as URLLoader;

donnees_xml = new XML();

//XML.prettyIndent = 0;

//XML.ignoreWhitespace = true;

//standardisation des ifos xml au moyen d'un objet personnalisé BadgeSon

for (var i :int; i < donnees_xml.playlist.morceau.length();i++ )

{

       var badge:BadgeSon = new BadgeSon();

       badge.index = i;

       badge.titre = donnees_xml.playlist.morceau[i].titre;

       badge.artiste = donnees_xml.playlist.morceau[i].artiste;

       badge.fichier = donnees_xml.playlist.morceau[i].fichier;

       (badge);

}

       // Installation de la playlist

playlist = newPlayList(tab_liste);

playlist.police = Font.enumerateFonts()[0].fontName;

playlist.contruirePlayList();

addChild(playlist);

playlist.habillage = new HabillagePlaylist();

playlist.x = champ_info.x;

playlist.y = champ_info.y;

playlist.addEventListener(MouseEvent.CLICK, selectionPL, true);

champ_info.visible = false;

fond_infos.visible = false;

// Chargement du premier morceau

lecteur.chargerNouveauSon(tab_liste[0].fichier);

//mise en surbrillance du morceau en cours

playlist.selection(0)

lecteur.jouerSon();

// mise en route de l'horloge.

}

horloge.start()

private function selectionPL(even:MouseEvent):void 

{

      lecteur.chargerNouveauSon(tab_liste[int()].fichier); lecteur.index_piste = int()  playlist.selection(int());      morceau_en_cours = int()

}

Nous ajoutons les comportements adaptés dans les différentes fonctions : 

Dans gestionControles : 

 …

if ( == "bt_precedent")

      {

            if (morceau_en_cours > 0)

            {

                  morceau_en_cours--;

                  lecteur.index_piste = morceau_en_cours;    lecteur.chargerNouveauSon(tab_liste[morceau_en_cours].fichier);

            }

            else if(morceau_en_cours == 0)

            {

                  morceau_en_cours = tab_liste.length -1;          lecteur.index_piste = morceau_en_cours;

      lecteur.chargerNouveauSon(tab_liste[morceau_en_cours].fichier);

            }

            playlist.selection(morceau_en_cours);

      }

…  

Nous plaçons également la même instruction à la fin de la fonction jouerSuivant : 

playlist.selection(morceau_en_cours);

enfin dans la fonction messagesPistes nous assurons le rafraîchissement des boutons  par les étiquettes id3 : 

private function messagesPiste(even:Event):void       

{

      tab_liste[morceau_en_cours].ID3 = lecteur.ID3;  playlist.raffraichirTitre(morceau_en_cours);

      = tab_liste[morceau_en_cours].description;

}

Mise en place du bouton infos 

Le bouton infos permettra de faire apparaître alternativement la playlist ou bien les informations id3 du morceau en cours . Nous mettons d’abord en place les éléments supplementaires sur la scène de notre application : 

La deuxième image du clip est un changement de couleur. L’action est un stop pour éviter la mise en lecture du clip.

Voici les éléments ajoutés à notre application et les noms d’occurrence attribués : 

Enfin nous ajoutons un écouteur de clics à l’élément « infos » dans la fonction xmlCharge (peu importe l’endroit dans la fonction : 

bt_infos = infos;

bt_infos.addEventListener(MouseEvent.CLICK,affichageInfos)

voici la fonction de gestion du bouton

private function affichageInfos(e:MouseEvent):void 

{

     if(playlist.visible)

     {

          playlist.visible = false;       champ_info.visible = true;          fond_infos.visible = true;

          bt_infos.gotoAndStop(2);

     }

     else

     {

          playlist.visible = true;       champ_info.visible = false;                    fond_infos.visible = false;                  bt_infos.gotoAndStop(1);

     }

}

Conclusion

Nous venons de mettre en place un lecteur musical complet et en très grande partie dynamique. Nous pouvons voir que la mise en place d’une application pourtant d’aspect simple peut être longue si l’on se préoccupe de l’ensemble de opérations et de interactions indispensables à sa bonne ergonomie. 

Nous voyons ici tout l’intérêt qu’il y a à préparer son travail par les étapes que nous avons évoqué au chapitre précédent.

Ce chapitre nous aura aussi permis de bien mettre en évidence l’importance des évènement dans la bonne synchronisation de ce type d’application. Nous aurons eu également l’occasion d’aborder la notion de classe au plus proches des véritables intérêts qu’elle peut offrir. En effet, en créant un application spécialisée nous avons également mis en place une classe de gestion de sons qui pourra sans problème être réutilisable dans la plupart des cas où un son lourd doit être diffusé par un chargement externe et éventuellement contrôlé à divers degré. Cette façon d’opérer nous permettra à l’avenir d’économiser le temps dédié à l’écriture d’une classe semblable.

Optionnellement nous aurons enfin pu rencontrer l’objet BitmapData et de la possibilité de copie d’objet qu’il recèle.


Partie 7

Autour de la syntaxe as3

Maitriser l’utilisation du mot clé « is »

Lors du chapitre sur le modèle évènementiel, nous avons abordé les phases de captures et de bouillonnement. Nous avons vu que, la diffusion d’évènements était déclenchée par l’ensemble des objets enfants de la liste d’affichage du container écouté.

Nous nous sommes retrouvés devant l’obligation de désactiver la réactivité souris des DisplayObject dont nous ne souhaitions pas voir déclencher notre action.

Nous avons une autre solution c’est de filter les objets.  Dans la fonction associée à l’évènement souris, nous allons effecteur un test en utilisant le mot clé is et de traiter les clicks souris en fonction de l’objet cliqué.

if(mon_objet is MovieClip)

{

//Effectuer une action

}

Lorsque l’on formule un test qui utilise cette syntaxe. Le processus va nous renvoyer un booléen qui nous indiquera si l’occurrence est bien du type que l’on recherche.

Nous pouvons bien sur rentrer toutes les classes possibles, qu’elles soient intrinsèques à flash ou qu’elles soient des classes personnalisées. 

Le test renverra true sur l’ensemble des types dont hérite la classe.

Admettons que j’ai une classe personnalisée se nommant Vignette héritant de MovieClip 

var ma_vignette :Vignette=new Vignette() ;

if(ma_vignette is MovieClip) {

 //Effectue l’action car ma classe Vignette hérite de MovieClip.

}  

if(ma_vignette is Vignette) {

 //Effectue l’action il s’agit bien de ma classe Vignette.

}

if(ma_vignette is DisplayObject)

{

//Effectue l’action car ma classe Vignette hérite de MovieClip qui

 // hérite de DisplayObject.

}

if(ma_vignette is Object)

{

//Effectue l’action car ma classe Vignette hérite de MovieClip qui herite à la base de la classe Object().

}  

if(ma_vignette is TextField) {

 //N’effectue pas l’action car vignette n’hérite pas de TextField.

}

L’utilisation de la syntaxe crochet dans AS3 .

La classe Object est la brique de base en AS3. sur elle reposent l’ensemble des fonctionnalités du langage. Cela veut dire que tous les types complexes et primitifs héritent de la classe Object().

Quand en as2 il s’agissait de créer un objet, nous pouvions simplement implémenter ses propriétés à l’aide de la syntaxe pointée. 

var mon_objet =new Object() ; mon_objet.propriete_1= « une chaine » ; 

En as 3 cette syntaxe provoquera une erreur car la syntaxe pointée ne fonctionne qu’avec des méthodes et des propriétés déjà existantes dans l’objet créé. Or dans un objet rien ne préexiste.

Je devrais donc pour assigner une propriété ou une méthode sur un objet générique ou une classe dynamique utiliser la syntaxe suivante : 

 var mon_objet : Object =new Object() ; mon_objet [«propriete_1»]=  «chaine de caractères»;

mon_objet [«propriete_2»]= {identifiant : 1 , valeur : «perdu» };

 pour récupérer les données stockées nous disposons alors de deux méthodes possibles :

Soit en utilisant la syntaxe pointée : 

                 mon_objet. propriete_1 ; // trace renvoie : [object Object]

 soit en utilisant de nouveau la syntaxe à crochet :

mon_objet [«propriete_1»] ; // trace renvoie : [object Object]

Il est possible de parcourir l’architecture de l’objet entièrement avec cette syntaxe :

                mon_objet [«propriete_2»] [«valeur»]; // trace renvoie : « perdu »  

La première remarque à formuler c’est que je peux récupérer une propriété de n’importe que objet héritant d’Objet() (soit TOUS les types primitifs et composites ) qui utilisent la syntaxe pointée.

Il me suffit  d’utiliser cette syntaxe en lieu et place de la syntaxe pointée.

 var clip :MovieClip = new MovieClip();

 clip.x = 200 ; // place le clip à 200 pixel du bord du container parent.  Trace(clip[«x»])// renvoie 200 ;

La deuxième chose remarquable, c’est qu’entre les crochets nous n’utilisons pas la propriété en elle-même mais une chaine de caractère correspondant à sa valeur. C’est l’aspect de chaine qui va nous intéresser, car cette façon d’ecrire est en mesure de nous apporter une souplesse énorme dans notre code. En effet la valeur de la chaine peut être assignée à une variable et nous pouvons utiliser cette variable pour modifier les propriétés sélectionnées var clip :MovieClip = new MovieClip();

//je crée un tableau avec quelques une des propriétés de mon clip sous forme

//de chaines de caractère

var tableau_choix:Array = ["x","y", "rotation" ] ;

// Je crée un chiffre au hasard entre 0 et le nombre d’éléments du tableau moins un ;

var chiffre_hasard :Number = Math.round( Math.random()*(tableau_choix.length1));

       clip.graphics.beginFill(0xCC33FF,.6) clip.graphics.drawRect(0,0,200,100); this.addChild(clip);

       clip.x=stage.stageWidth/2-clip.width/2;

       clip.y=stage.stageHeight/2-clip.height/2;

       // J’affecte une valeur de 200 à la propriété du clip qui sera sélectionnée 

//dans le tableau   clip[tableau_choix[chiffre_hasard]] = 200 ; 

 trace(chiffre_hasard+" :  propriété : "+tableau_choix[chiffre_hasard]+" : "+clip[tableau_choix[chiffre_hasard]]);   

Nous pouvons aussi appliquer cette syntaxe à travers l’arbre d’un XML. Dont nous nous servirons pour stocker les différents états d’un Sprite que nous aurions configuré en bouton : 

var donnees_xml:XML= <main>

              <config largeur="200" hauteur="30"></config>

              <normal couleur="0x333333" alpha_fond="0.5"></normal>

              <dessus couleur="0xDD00EE" alpha_fond="0.8"></dessus>

                                   </main>;

var fond:Sprite = new Sprite(); fond.mouseEnabled = false; decorer("normal");

var bouton:Sprite = new Sprite(); bouton.buttonMode=true;

bouton.addChild(fond); this.addChild(bouton);

bouton.addEventListener(MouseEvent.MOUSE_OVER,dessus); bouton.addEventListener(MouseEvent.MOUSE_OUT,dehors);

function dessus(even:MouseEvent):void 

{decorer("normal");}

function dehors(even:MouseEvent):void 

{decorer("dessus");}

function decorer(etat:String):void 

{

fond.graphics.clear();

fond.graphics.beginFill(donnees_xml[etat].@couleur,donnees_xml[etat].@alpha_fond); fond.graphics.drawRoundRect( 0 , 0 , donnees_xml.config.@largeur, donnees_xml.config.@hauteur , 4 , 4);

}


Partie 8

Diffuser de la vidéo à l’aide de flash

Préparer une vidéo pour l’incorporation.

Codecs et formats sont deux choses différentes.

Une vidéo d’un format donné peut être encodée et lue par des codecs différents. Codec est l’abréviation de « codeur décodeur ».

Le format de fichier est donc une enveloppe dans laquelle on insère les données du film codées selon un cryptage donné. Cependant toutes les enveloppes ne supportent pas tous les codecs. Par exemple un fichier vidéo encodée avec sorenson spark(.mov) ne pourra pas être lue si elle est dans une enveloppe avi.

L’encodage d’une vidéo doit donc donner lieu à deux questions : 

Quel est le format de sortie du contenu (quel format de fichier). Ici nous devrons le lire au travers d’un swf…

Quels sont les codecs accepté par le format qui contiendra ma vidéo ?  

La vidéo dans flash : le player 9 accepte de lire les formats suivants :

-       .mp4 : sera en terme de compression le plus léger. D’autres médias peuvent également lire ce format. Proche de l’avi, il peut être encodé à l’aide des codecs « divx, xvid, h264 ». Pour être lue dans flash il faudra choisir par exemple le « h264 » lors de l’encodage. - .mov : Format vidéo utilisé par quicktime (sorenson spark).

Toutefois, préférez travailler dans les formats natifs du player flash : 

-       .flv : format vidéo par défaut de flash. Comporte entre autre  la possibilité de diffuser des évènement durant la diffusion. Nous choisirons ce format pour le tutorial. Par défaut ces vidéo seront codées en On2 VP6 dans « adobe media encoder ».

Le son des vidéos pourra être diffusé dans le format mp3, flash supportant nativement ce format.

-       F4v : format construit autour du codec ( libre ) h264. C’est le format qui doit à terme prendre la relève. Toutefois le système de diffusion des points de repère de ce type de vidéo à été modifié. Les évènements ne sont plus transmis de la même manière.

Pourquoi l’un plus que l’autre ? La question se pose en terme de taille du fichier et des capacités multimédias que voudrions employer (action particulière à un moment donné). Attention aussi aux mauvaises surprises sur le codec employé. Une bonne expérience est conseillée pour editer d’autres formats que le simple flv en dehors du logociels fourni dans la suite CS3 : « adobe CS3 media encoder »

Flash permet l’utilisation des codecs suivants :  

 Sorenson Spark : il s’agit du premier codec vidéo à être apparu dans le lecteur Flash. La qualité d’affichage n’est pas optimale, ce codec est voué à disparaître. 

•      Screen Video : Il s’agit du codec utilisé pour la capture d'écran par Connect (anciennement Breeze). 

•      On2 VP6 : ce codec fut introduit au sein du lecteur Flash 8. Il introduit une qualité d’image supérieure ainsi que la gestion du canal alpha. 

•      H.264 : ce codec fut introduit au sein du lecteur Flash 9.0.115. Il améliore à nouveau la qualité de l’image tout en garantissant l'interopérabilité et l'universalité des données vidéo. 

(source : T. Imbert pratique de l’actionscript 3)

Les versions antérieures à flash cs3 ne permettaient de ne charger que des fichiers flv.

Mettre en place une vidéo dans l’environnement auteur.

Une note importante. Parce qu’une vidéo même compressée et de petite taille pèse rapidement très lourd, il n’est pas conseillé d’importer dans la bibliothèque plus de dix secondes de vidéo sans quoi on est sur de voir le swf de votre application monter bien au delà du méga, ce qui représente en terme d’ergonomie une vraie catastrophe.

Méthode 1

Dans le menu « fichier/ importer » sélectionner importer une vidéo, la boite de dialogue suivante apparait. 

D’une manière générale on préfèrera importer des fichiers flv plutôt que d’autres formats. Que flash essaiera de modifier pour le rendre compatible.

Figure 1 : bien que la selection de fichier permettent un vaste choix de format, veillez à lui fournir de préférence des fichiers flv

Figure 2 : le choix du déploiement dépend de la finalité du media : sert-il d'élément graphique dans l'application ou doit-il être lu et contrôlé par le biais d'un composant flvPlayback ?

Si l’on sélectionne le fichier pour l’intégrer à la bibliothèque, afin de s’en servir d’élément graphique on choisira la dernière option : « Incorporer la vidéo…». La vidéo apparait donc dans la bibliothèque. Il suffit alors de la glisser sur la scène. Elle sera lue alors en boucle.

Si au contraire on souhaite l’intégrer et la doter de contrôle on choisira l’option

« téléchargement progressif … »

Flash placera alors le fichier à la racine du répertoire où se trouve le swf et appellera un composant que vous pourrez configurer. (cf figure 3).

L’application se composera alors de votre fichier swf, du fichier flv et d’un swf comportant les contrôles. Ces trois fichiers seront nécessaires au bon fonctionnement de l’application.

Figure 3 : L'habillage permet de sélectionner les contrôles et de modifier les couleurs de ceux-ci

Il est possible également d’importer directement le composant flvPlayback depuis le panneau des composants, de la configurer au travers de l’inspecteur des composants. Puis de le lier à une vidéo flv en renseignant le paramètre « source » dans l’inspecteur.

Le fichier flv servant d’exemple ici pèse 1,17mo, le swf incluant la vidéo 1,05Mo. Quand au swf contenant juste le composant il pèsera 49ko sans autres éléments. 

Personnaliser un des habillages intégrés.

Il est possible également de vous servir de la source du composant pour personnaliser un des habillages et le conformer à la charte graphique de l’application que vous créez. Pour trouver le fichier fla du composant rendez-vous dans le dossier « program files » (windows) ou applications (« mac »). 

Adobe\Adobe Flash CS4\Common\Configuration\Component Source\ActionScript

3.0\FLVPlayback

Mettre en place et contrôler une vidéo externe dans flash à l’aide du script.

Objets à étudier  en particulier : 

.NetConnection; .NetStream; flash.media.Video;  

Pour des raisons évidentes de fluidité et de rendu final, certaines animations flash comportent des séquences vidéo permettant de mixer animations scriptées et rendu vidéo avec toutes les possibilités de post-production que le media permet. Cela permet des effets graphiques enrichis tout en conservant une possibilité d’interaction.

Il est donc bien utile parfois de pouvoir charger et  contrôler une vidéo par le script sans pour autant avoir besoin d’une console pour l’utilisateur. Ne serait que pour presenter une video en tant que fond pour un site comme cela est actuellement courant dans nombre de sites.

Pour afficher une vidéo, un conteneur flash à besoin de réaliser les opérations suivantes : 

Créer une connexion persistante avec le serveur à l’aide de l’objet NetConnection.

D’établir un flux vidéo entre l’application et le serveur pour permettre la visualisation couplé au téléchargement progressif. C’est le rôle de la classe NetStream

De créer un moteur de rendu recevant le flux et le traduisant en image et son insérable dans le liste d’affichage sous la forme de l’objet ActionScript : Vidéo.

Il est important de noter que la plupart des opérations de contrôles et de transformations de son s’effectue sur l’objet netStream soit le flux lui-même. L’objet vidéo possède quelques fonctions liées à l’objet en diffusion mais on l’utilise surtout pour les opérations graphiques commune  

Utilisation de la Web Cam de l’utilisateur.

Qu’il s’agisse d’une application de chat ou d’un divertissement mettant en jeu l’image de l’utilisateur, il est utile de savoir comment accéder à la webcam de l’utilisateur. La manipulation est très simple et ne pose aucune difficulté particulière :

                 // Création d’une fenetre de diffusion video.

                 var fenetre : Video = new Video();

                // La camera se récupère en appellant une function statique de la classe Camera                var camera:Camera = Camera.getCamera();

                 // Verification de la presence d’une camera.

                 if (camera != null)

                {               

                                // Lissage de l’image                       fenetre.smoothing = true;

                                // Transmission du flux à la fenêtre            fenetre.attachCamera(camera);                               // Ajout à la liste d’affichage.                        addChild(fenetre);

                }

                else

                {

                                 trace("pas de camera détectée");

                }

Création d’une classe vidéo

L’utilisation de l’objet Video peut donner lieu à la création d’une classe spécialisée que nous pourrons :

-   Soit utiliser simplement à des fins décoratives.

-   Soit s’en servir pour une présentation synchronisée en manipulant les points de repères - Soit la faire fonctionner de manière autonome et l’enrichir de fonctions utiles comme la diffusion multiple à l’aide d’une playlist la rendre autonome afin d’être déployée simplement sur un site. (player).

Organisation de la classe

Philosophie.

La classe que nous allons créer n’est pas aussi générique que celle que nous avons mise en place lors de la création d’un lecteur musical. Le but est ici de se familiariser avec la diffusion de contenu vidéo. Et au passage de voir comment synchroniser des actions d’un clip de la scène ou de tout autre produit de votre imagination. De nombreux enrichissements sont bien sur possibles en vous servant de cette base. Nous la rendrons toutefois assez souple pour être utilisée par toutes les possibilités offertes en terme d’instanciation (extension d’un clip de la bibliothèque, classe proposant la diffusion d’une vidéo…)

Difficultés particulière ou codes inhabituels : 

La réception de certains « évènements » se fait de façon particulière en vertu du mode de diffusion de la vidéo. Les messages provenant du fichier vidéo lui-même (Métadonnées et point d’ancrage personnalisés) sont envoyés à trois fonction qui doivent porter un nom précis. (onCuePoint, onXMPData et onMetaData). Elle reçoivent toutes trois en paramètre un objet contenant les données concernées. Ces fonctions doivent nécessairement être trouvées par le flux vidéo. Elles ne necessitent pas d’être remplies. Vous pouvez simplement fournir cette fonction sans aucune instruction dans le corps.

La plus intéressante de ces fonctions est sans nul doute celle qui va recevoir les points de repères, car grâce aux points d’ancrage il est possible de synchroniser un flux vidéo et le déroulement d’une animation. Nous verrons comment construire notre classe de manière à ce qu’un évènement courant soit émis. De cette manière nous n’aurons pas à nous préoccuper des spécificités de la classe mais simplement de récupérer l’identifiant du point de repère emis par la video.

Il faut aussi indiquer au flux vidéo quelle classe contient ces fonctions. Il est par conséquent possible d’indiquer au flux d’envoyer ces informations à la classe appelante plutôt qu’à la classe FenetreVideo, ce qui représente un avantage plutôt qu’une difficulté supplémentaire. Il est possible de lisser de comportement par l’emploi d’une classe d’évènement personnalisée.

Les modifications depuis Flash CS4.

Depuis le flash player 10 quelques modifications ont été apportées au fonctionnement de la video dans flash. La première est l’ajout de la fonction de reception onXMPData aux deux autres mises en place avec l’utilisation d’AS3. 

Ajout du fichier XMP

Cette fonction est déclenchée lorsque le fichier XMP inclus est reçu par le lecteur flash. Un fichier XMP est un fichier XML normalisé, qui se généralise dans la suite adobe et permet de placer des informations supplémentaires sur le fichier. Ce fichier permet selon le créateur de logiciel de faciliter la communication de données entre les différents logiciels de la suite. On retrouve ce fichier par exemple lorsque l’on travaille dans photoshop sur un fichier de type raw, toutes les modifications sont inscrites dans ce fichier, alors que les pixels de l’image ne sont pas modifiés. Lors de l’ouverture suivante les données XMP sont lues et appliquée au fichier raw. Le fichier XMP video dans flash va contenir toutes les données concernant la vidéo, son encodage, ses points de repères… A ce jour aucune documentation n’explique clairement comment utiliser ce fichier. Il est probable qu’il prenne une importance essentielle dans les prochaines versions du player avec la modification du format de base des videos.

Vers le nouveau format F4V

Le format flv est en effet en train d’être remplacé par les fichier portant l’extension F4V. Ceux-ci sont maintenant encodés à l’aide de l’algorythme h264, qui est open source contrairement aux anciennes versions. Or ce fichier F4V ne transmet plus les points de repères de la même manière, comme nous allons le voir dans le tutoriel qui suit. Il est donc probable que la diffusion video soit profondément retravaillée afin d’unifier ces modes de diffusion avec le système évènementiel courant, et qu’a terme l’ensemble des infos transitent par le fichier XMP pour être traité directement par le lecteur flash et non plus incus dans la vidéo… A suivre de près si vos applications doivent faire appel à la vidéo avancée.

Création de la classe

La classe que nous créons étend MovieClip nous pourrons agir sur ses taille et emplacement afin de modifier l’aspect de la video que nous laisserons à sa taille par défaut.

Nous créons trois variables de classe privées qui nous servirons à manipuler le flux video : 

private var chargeurVideo:NetConnection; private var fluxVideo:NetStream; private var ecranVideo:Video;

Par défaut le flux video à besoin de pouvoir appeler trois  fonctions pour transmettre les message de la video. Il est donc obligatoire de mettre à disposition du flux trois corps de fonctions fussent-elles vides. Par défaut, le flux video ne va pas chercher ces fonctions dans le corps de la classe. Il est nécessaire de lui indiquer quel objet les renferme. Cela pourrait donc être une classe principale, ou même une classe personnalisée. Pour cela nous allons passer un paramètre dans le constructeur permettant d’indiquer l’emplacement de ces fonctions. Nous donnons une valeur par défaut : null de manière à ce que si nous ne passons pas de paramètres, le flux aille utiliser les fonctions « onXxxxXxxx » de la classe.

Nous ajoutons par conséquent une variable de classe pour stocker le containeur des fonctions. Nous assurons la mise en place dans la fonction constructeur : 

private var client_fonctions:Object;

public function FenetreVideo( clientFonctions:Object = null) 

{

      if (clientFonctions == null){ client_fonctions = this;}     else { client_fonctions = clientFonctions }; }

Nous placerons dans le scénario trois fonctions différentes 

La référence à ces fonctions doivent obligatoirement porter un nom précis

                onCuePoint:Function    onMetaData:Function

                 onXMPData:Function

public function onMetaData ( pMeta:* ):void

{

/* Cette boucle sert de controle pour lister l'ensemble de propriétés de l'objet reçu par la fonction */

      trace("------------------------------------")   trace("METADONNEES")    for ( var p:* in pMeta )      trace( p + " : " + pMeta[p] );

      trace("------------------------------------")

// mise en lecture       jouerFlux();

}

/* cette fonction permet d'intercepter lefichier joint XMP lors de l'encodage video du flv/f4v */

public function onXMPData ( pMeta:* ):void

{

      for ( var p:* in pMeta )      {trace( p + " : " + pMeta[p] );} }  

La fonction onCuePoint permet d'intercepter les points de repères programmés lors de l'encodage du flv. Afin de ne pas rendre obligatoire le transfert de cette fonction nous allons nous en servir pour diffuser, grâce à l’héritage MovieClip de notre classe, un évènement générique à chaque fois qu’un point de repère est reçu.

Pour ce faire, nous allons ajouter une variable de classe à notre script qui stockera le nom du point de repère et sera accessible via la publicité de la variable ou par un « getter » 

private var point_repere : String;

public function get repereActuel():String { return point_repere; }

En recevant notre point de repère dans la fonction  nous le stockons dans la variable et diffusons un évènement générique pour prévenir de la réception. 

Nous le recupèrerons en ayant placé l’ecouteur event.CHANGE sur l’instance de FenetreVideo et en appelant dans la fonction évènementielle : evenement.target.repereActuel.

public function onCuePoint ( pMeta:* ):void

{

      point_repere = ;

      dispatchEvent(new Event(Event.CHANGE));

      for ( var p:* in pMeta )      {trace( p + " : " + pMeta[p] );}

}

En dehors de ces figures imposées, le lancement à proprement parler de la vidéo est relativement simple. Il nous faut mettre en œuvre les objets que j’ai évoqués plus haut. Nous ne les plaçons pas dans la fonction constructeur mais dans  une fonction dédiée à son déploiement, rendant la classe éligible pour une multi diffusion sans avoir à la recomposer entièrement. Cette façon de procéder permettra d’éviter de modifier la source d’un précédent swf qui utilise la même classe mais selon une architecture devenue obsolète.

public function chargerNouvelleVideo(adresse:String):void

{

      //Fonction de retrait des écouteurs s’ils existent (détaillé ciaprès)

      retraitEcouteurs() ;

// instanciation de la connection   chargeurVideo = new NetConnection();

// Si la connection s'effectue sur le même domaine on laisse l'argument à null

      chargeurVideo.connect(null);

// instanciation du flux. 

      fluxVideo = new NetStream ( chargeurVideo );    fluxVideo.bufferTime = 10;

// Fonction d’ajout des écouteurs au flux (détaillé ci-après) ajoutEcouteurs();

      ecranVideo = new Video();

/* Tout comme une image on peut appliquer un lissage en cas de redimensionnement. */   ecranVideo.smoothing=true;

// on connecte le flux à l'écran vidéo   ecranVideo.attachNetStream( fluxVideo ); // ajout à la liste d'affichage du clip en cours.

      addChild ( ecranVideo );

// lecture en chargement progressif du fichier vidéo flv    (adresse);

/* mise en pause afin de synchroniser la diffusion a l'affichage des informations */    fluxVideo.pause();

// !! Le flux video envoie des appel de fonctions tels que onMetaData ou onCuePoint pour les intercepter il faut donc créer les fonction correspondantes et indiquer au flux à quel endroit sont stockées ses fonctions */

      fluxVideo.client = client_fonctions;      volume = 0; 

      addChild ( ecranVideo );

}

L’objet NetStream diffuse les informations de lecture (début,fin) et les éventuelle erreurs. Pour différencier les différents messages il faut interroger la propriété code de l’objet info inclus dans les évènement de type NetStatusEvent.

J’ai donc choisi de placer deux écouteurs pour le même type d’évènement. Afin de ne pas finir avec une énorme fonction évènementielle mais aussi afin de séparer le traitement des erreurs et celui des messages de diffusion de la séquence vidéo. 

private function ajoutEcouteurs ( ):void

{

fluxVideo.addEventListener( NetStatusEvent.NET_STATUS, gestionErreurs ); /* écoute de l'événementNetStatusEvent.NET_STATUS ecoute l'evenement de début et de fin de diffusion de la video : me permet par exemple de mettre en place une liste de lecture. */

fluxVideo.addEventListener( NetStatusEvent.NET_STATUS, etatVideo ); /* création de l'objet Video : le conteneur ou ecran qui va permettre de

gérer et d'interpréter le flux de données */

}  

Toujours dans la visée de faire évoluer la classe dans le futur, je place également une fonction de retrait des écouteurs que j’activerai en cas d’erreur de chargement ou de fin de lecteur. 

private function retraitEcouteurs (e:Event ):void

{

      if (fluxVideo.hasEventListener(NetStatusEvent.NET_STATUS))

      {

fluxVideo.removeEventListener( NetStatusEvent.NET_STATUS, gestionErreurs ); fluxVideo.removeEventListener( NetStatusEvent.NET_STATUS, etatVideo ); 

      }

}

La fonction de réception des erreurs coupe les écoutes d’évènements. Evitant ainsi de s’en préoccuper depuis la classe appelante si l’on souhaite détruire l’instance en cas d’erreur de diffusion.

private function gestionErreurs ( pEvt:NetStatusEvent ):void

{

      pEvt.target.removeEventListener( NetStatusEvent.NET_STATUS, gestionErreurs );

      if ( == "NetStream.FileStructureInvalid" )

      {

trace("fichier non compatible"); retraitEcouteurs() ;

      }

      else if ( == "NetStream.NoSupportedTrackFound" )

      {

trace("aucune piste trouvée"); retraitEcouteurs() ;

      }

Dans la fonction gérant les signaux de fonctionnement normaux, nous retransmettons les évènements importants tels que les signaux de début et de fin de lecture en nous servant d’évènements génériques de la classe Event : le signal de début fera émettre à notre instance un et celui de fin un Event.COMPLETE.

Je vous conseille ici de créer un évènement personnalisé qui vous offrira l’opportunité de transmettre directement une chaîne de caractère. En adaptant cette méthode à la diffusion des point de rrepère vous n’auriez plus alors qu’a poser un seul écouteur pour l’ensemble des signaux émis par une instance de cette classe. 

private function etatVideo(even:NetStatusEvent):void

{

/* cette boucle sert de controle pour lister l'ensemble de propriétés de l'objet reçu par la fonction */      //trace("VIDEO")

      //trace("-----------------------------------");

      //for ( var p:* in )

      //trace( p + " : " + [p] );

      //trace("-----------------------------------");

/* diffusion d'un evenement de base "OPEN" si le code suivant est detecté, une playlist pourra ecouter cet evenement et par exemple modifier l'affichage du titre; */

      if(==".Start")      dispatchEvent(new Event());

/* diffusion d'un evenement de base "COMPLETE" si le code suivant est detecté; */

      if(=="")  dispatchEvent(new Event(Event.COMPLETE)); }

Contrôles sur la vidéo

Puis je donne accès à quelques fonctions basique afin de permettre la création d’un lecteur minimal, on fait appel pour les deux première à des fonctions déjà existantes dans l’objet de flux. De cette manière on ouvre la classe à la possibilité de la diffusion multiple :

public function jouerFlux():void

{

      fluxVideo.resume();

}

public function pauser():void

{

      fluxVideo.pause();

}

La fonction de volume est identique à celle utilisée dans le lecteur audio. On extrait l’objet SoundTransform du flux (NetStream), on modifie la propriété volume puis on lui réattribut.

public function set volume(vol:Number):void

{            

//J'extraie l'objet soundTransform de l'objet video pour agir sur ses paramètres var transformation:SoundTransform = fluxVideo.soundTransform; // modification du volume (ex ici suppression du son).             transformation.volume = 0;

// Puis réattribution à la video pour prendre en compte les modifications

            fluxVideo.soundTransform = transformation;

      }

La classe est maintenant prète à être instanciée et manipulée. Elle reste ouverte pour des développements ultérieurs tels que l’ajout d’une fonction de navigation dans la piste.

La classe finale

 package {

      import flash.display.MovieClip;     import flash.events.Event;     import flash.events.NetStatusEvent;  import flash.media.Camera;     import flash.media.Sound;  import flash.media.SoundTransform;        import flash.media.Video;      import .NetStream;  import .NetConnection;

public class FenetreVideo extends MovieClip

{

private var chargeurVideo:NetConnection; private var fluxVideo:NetStream; private var ecranVideo:Video; private var client_fonctions:Object;

private var point_repere : String;

public function FenetreVideo( clientFonctions:Object = null) 

{

      if (clientFonctions == null){ client_fonctions = this;}     else { client_fonctions = clientFonctions }; }

public function get repereActuel():String { return point_repere; }

public function chargerNouvelleVideo(adresse:String):void

{

      retraitEcouteurs()

// instanciation de la connection   chargeurVideo = new NetConnection();

// Si la connection s'effectue sur le même domaine on laisse l'argument à null

      chargeurVideo.connect(null);

// instanciation du flux. 

      fluxVideo = new NetStream ( chargeurVideo );  fluxVideo.bufferTime = 10;    ajoutEcouteurs(); ecranVideo = new Video();

/* Tout comme une image on peut appliquer un lissage en cas de redimensionnement. */   ecranVideo.smoothing=true;

// on connecte le flux à l'écran vidéo   ecranVideo.attachNetStream( fluxVideo ); // ajout à la liste d'affichage du clip en cours.

      addChild ( ecranVideo );

// lecture en chargement progressif du fichier vidéo flv    (adresse);

/* mise en pause afin de synchroniser la diffusion a l'affichage des informations */    fluxVideo.pause();

// !! Le flux video envoie des appel de fonctions tels que onMetaData ou onCuePoint pour les intercepter il faut donc créer les fonction correspondantes et indiquer au flux à quel endroit sont stockées ses fonctions */

      fluxVideo.client = client_fonctions;      volume = 0; 

      addChild ( ecranVideo );

}

private function ajoutEcouteurs ( ):void

{

fluxVideo.addEventListener( NetStatusEvent.NET_STATUS, gestionErreurs ); /* écoute de l'événementNetStatusEvent.NET_STATUS ecoute l'evenement de début et de fin de diffusion de la video : me permet par exemple de mettre en place une liste de lecture. */

fluxVideo.addEventListener( NetStatusEvent.NET_STATUS, etatVideo ); /* création de l'objet Video : le conteneur ou ecran qui va permettre de

gérer et d'interpréter le flux de données */

private function retraitEcouteurs (e:Event ):void

{

      if (fluxVideo.hasEventListener(NetStatusEvent.NET_STATUS))

      {

fluxVideo.removeEventListener( NetStatusEvent.NET_STATUS, gestionErreurs ); fluxVideo.removeEventListener( NetStatusEvent.NET_STATUS, etatVideo ); 

      }

}

/* cette fonction gére le debut et la fin et envoie un evenement de base à la racine du swf pour pouvoir être eventuellement ecouté par une console personnalisée.*/

private function etatVideo(even:NetStatusEvent):void

{

/* cette boucle sert de controle pour lister l'ensemble de propriétés de l'objet reçu par la fonction */      //trace("VIDEO")

      //trace("-----------------------------------");

      //for ( var p:* in )

      //trace( p + " : " + [p] );

      //trace("-----------------------------------");

/* diffusion d'un evenement de base "OPEN" si le code suivant est detecté, une playlist pourra ecouter cet evenement et par exemple modifier l'affichage du titre; */

      if(==".Start")      dispatchEvent(new Event());

/* diffusion d'un evenement de base "COMPLETE" si le code suivant est detecté; */

      if(=="")

      {

dispatchEvent(new Event(Event.COMPLETE)); retraitEcouteurs()

}

}

public function onMetaData ( pMeta:* ):void

{

/* Cette boucle sert de controle pour lister l'ensemble de propriétés de l'objet reçu par la fonction */

      trace("------------------------------------")  trace("METADONNEES")    for ( var p:* in pMeta )      trace( p + " : " + pMeta[p] );

      trace("------------------------------------")

// mise en lecture       jouerFlux();

}

/* cette fonction permet d'intercepter les points de repères programmés lors de l'encodage du flv. Nous plaçons le repère dans une variable accessible et diffusons un évènement générique pour prévenir de la réception */

public function onCuePoint ( pMeta:* ):void

{

      point_repere = ;    dispatchEvent(new Event(Event.CHANGE));  for ( var p:* in pMeta )  {trace( p + " : " + pMeta[p] );}

}

/* cette fonction permet d'intercepter lefichier joint XMP lors de l'encodage video du flv/f4v */ public function onXMPData ( pMeta:* ):void

{

      for ( var p:* in pMeta )      {trace( p + " : " + pMeta[p] );}

}

/* cette fonction sert à prévoir un comportement particulier et adapté en cas d'erreur de la connexion. ex renvoi à une page html pouvant traiter le format. */

private function gestionErreurs ( pEvt:NetStatusEvent ):void

{

      pEvt.target.removeEventListener( NetStatusEvent.NET_STATUS, gestionErreurs );

      if ( == "NetStream.FileStructureInvalid" )

      {

trace("fichier non compatible"); retraitEcouteurs() ;

      }

      else if ( == "NetStream.NoSupportedTrackFound" )

      {

trace("aucune piste trouvée"); retraitEcouteurs() ;

      }

}

/* Ces fonctions simples me permettent de faire appel à aux methodes qu'elles renferment depuis l'exterieur. Par exemple un bouton placé sur la scéne var activer cette fonction en appelant cette methode. Ainsi toutes les methodes propres à la video restent contenues à l'interieur de cet

objet au lieu d'être dispersées à travers l'animation. */

///------------------------------------------ public function jouerFlux():void

{

      fluxVideo.resume();

}

public function pauser():void

{

      fluxVideo.pause();

}

/*--Gestion du son de la video. --*/

public function set volume(vol:Number):void

{            

//J'extraie l'objet soundTransform de l'objet video pour agir sur ses paramètres var transformation:SoundTransform = fluxVideo.soundTransform; // modification du volume (ex ici suppression du son).             transformation.volume = 0;

// Puis réattribution à la video pour prendre en compte les modifications

            fluxVideo.soundTransform = transformation;

      }

}

}

Annexes

Source du code utilisé pour le script sur le scénario

-    thibaut Imbert :  pratique de l’actionScript 3

Logiciels gratuit pour la compression d’image

-    Super : permet la conversion de multiples format (testé)

-    free-flv converter : (non testé).

-    Riva : (non testé).

La même … dans le panneau actions-image d’une animation de base :

//Script source :  Thibault Imbert "Pratique de l'actionscript 3" .

Chapitre 17 – Son et vidéo – version 0.1.1

var chargeurVideo:NetConnection = new NetConnection(); chargeurVideo.connect(null); 

var fluxVideo:NetStream = new NetStream ( chargeurVideo );

// Je precise le nombre de seconde à mettre en tampon sur le flux fluxVideo.bufferTime=10;

fluxVideo.addEventListener( NetStatusEvent.NET_STATUS, gestionErreurs );

fluxVideo.addEventListener( NetStatusEvent.NET_STATUS, etatVideo );

var ecranVideo:Video = new Video(); ecranVideo.smoothing=true;

ecranVideo.attachNetStream( fluxVideo );

function etatVideo(even:NetStatusEvent)

{

      if(==".Start")     stage.dispatchEvent(new Event());     if(=="") stage.dispatchEvent(new Event(Event.COMPLETE));

addChild ( ecranVideo );

(""); fluxVideo.pause(); fluxVideo.client = this; 

var transformation:SoundTransform = fluxVideo.soundTransform; transformation.volume = 0;

fluxVideo.soundTransform = transformation;

function onMetaData ( pMeta ):void 

      trace("METADONNEES")

      trace("------------------------------------") for ( var p in pMeta )

      trace( p + " : " + pMeta[p] );

      trace("------------------------------------")

      ecranVideo.width = pMeta.width;     ecranVideo.height = pMeta.height;

      if ( !contains ( ecranVideo ) )

      { 

            // dans quel cas nous l'ajoutons

            addChild ( ecranVideo );

      } 

      jouerFlux();

}

function onCuePoint ( pMeta ):void 

      trace("CUE POINT nom = ")

      for ( var p in pMeta )        trace( p + " : " + pMeta[p] );

      if(=="pauser")

      {

            stage.dispatchEvent(new Event(Event.UNLOAD));

      }

function gestionErreurs ( pEvt:NetStatusEvent ):void

      pEvt.target.removeEventListener( NetStatusEvent.NET_STATUS, gestionErreurs );

      if ( == "NetStream.FileStructureInvalid" )         trace("fichier non compatible");

      else if ( == "NetStream.NoSupportedTrackFound" )

      trace("aucune piste trouvée");

function jouerFlux()

{

      fluxVideo.resume();

function pauser()

{

      fluxVideo.pause();


Appendice 1

Outils géométriques : Rectangle et Point.

Les outils de géométrie peuvent sembler bien rudimentaire et austère mais il peuvent s’avérer d’une grande utilité lorsqu’il s’agit de manipuler des coordonnées et grandeur. Ils possèdent aussi des méthodes très intéressantes, comme certains tests permettant de vérifier si deux rectangles sont en intersection tout comme le hitTest d’un display Object. Des calcul de distance entre deux points sont possibles aussi. Nous allons nous attacher à décrire certains aspects qui pourront simplifier notre programmation.

L’objet Rectangle.

L’objet Rectangle du package est un objet relativement simple, mais qui trouve son utilité dans quelques situations incontournables : Le scrolling de texte ou d’image, le passage en plein écran d’une portion de l’application, très utile en vidéo, d’autres application emploient aussi cet objet… enfin la simplicité de ce type est telle que les accès mémoire seront optimisés si l’on demande à notre application d’aller chercher des informations dans un objet léger, plutôt que dans un héritier de DisplayObject beaucoup plus lourd à manipuler pour le processeur. Nous allons faire connaissance avec cet objet qui peut nous rendre bien d’autres services…

La signature du constructeur de l’objet se présente comme cela :

Rectangle (coordonnees_X :Number = 0, coordonnees_Y :Number = 0, largeur :Number = 0 , hauteur :Number=0) : Rectangle

Tous les paramètres contiennent une valeur par défaut je peux par conséquent initialiser cet objet sans lui donner de valeur.

On l’instancie de cette manière :

import .Rectangle ;

var rect :Rectangle = new Rectangle() ;

Il peut être parfois utile de pouvoir stocker en mémoire les dimensions et coordonnées initiales d’un objet pour par exemple le replacer après une interpolation.

rect.x=20 ; rect.y=20 ; rect.width =100 ;

rect.height =100 ;

Nous avons paramétré cet objet pour qu’il décrive un carré de 100 pixel sur 100 pixels situé à 20 pixels en x et y du bord de l’espace de coordonnées. Cela peut être une bonne alternative de donner un objet Rectangle en paramètre lors d’un redimensionnement d’une image chargée plutôt que les dimensions du container. La fonction gagnera en souplesse. De plus comme nous

allons le voir ici il est extrêmement facile d’initialiser un objet Rectangle à partir d’un DisplayObject celui possédant deux fonctions dédiées pour le faire à notre place : 

récupérer les limites d’un DisplayObject

getBounds (espace_reference : DisplayObject):Rectangle

getrect (espace_reference : DisplayObject) :Rectangle

Tout DisplayObject renvoie avec ces fonctions un objet rectangle instruit avec la taille et les coordonnées de l’objet interrogé par rapport à l’espace de référence passé en paramètre de la fonction.

Exemple créons deux container de base et observons les variantes de nos deux fonctions :

var container:MovieClip=new MovieClip(); container.graphics.beginFill(0x000000,0.5); container.graphics.lineStyle(10,0xCCFF33,0.5); container.graphics.drawRect(0,0,200,280); container.x=100; container.y=100; addChild(container);

var contenu:MovieClip=new MovieClip(); contenu.graphics.beginFill(0x000000,0.5); contenu.graphics.drawRect(0,0,70,20);

contenu.x=50; contenu.y=50;

container.addChild(contenu);

var rect_cont:Rectangle = container.getRect(stage)

trace("rectangle container (par getRect) : "+rect_cont.toString());

// renvoie : rectangle container (par getRect) : (x=100, y=100, w=200, h=280) // ne prend pas en compte la ligne de décoration.

var rect_bounds:Rectangle = container.getBounds(stage)

trace("rectangle container (par getBounds) : "+rect_bounds.toString()); // renvoie : rectangle container (par getBounds) : (x=95, y=95, w=210, h=290)

// la ligne chevauche de 5 pixel l'intérieur et l'exterieur du graphique

// get bounds le prend en compte en montre qu'il fait 10 pixel de plus de chaque coté

// et que le chevauchement le décale de cinq pixels en y et x.

var rect_local:Rectangle = contenu.getBounds(container) trace("rectangle contenu (local) : "+rect_local.toString()); // renvoie rectangle contenu (local) : (x=50, y=50, w=70, h=20) // Les coordonnées sont ici passées en coordonnées locales.

var rect_global:Rectangle = contenu.getBounds(stage) trace("rectangle contenu (global) : "+rect_global.toString()); // renvoie rectangle contenu (global) : (x=150, y=150, w=70, h=20)

// Les coordonnées sont ici passées en coordonnées globales contenu se // situe bien à ces coordonnées par rapport à la scène.

Un Rectangle obtenu à partir d’un DisplayObject propose certaines propriétés qui peuvent nous aider dans un calcul de placement :

var rect_global:Rectangle = contenu.getBounds(stage)

// renvoie rectangle contenu (global) : (x=150, y=150, w=70, h=20)

Si nous interrogeons les propriétés suivantes :

rect_global.right : nous donnera une valeur correspondante aux coordonnés x du coté droit de l’objet. Soit ici 220.

: nous donnera une valeur correspondante aux coordonnés x du coté gauche de l’objet. Soit ici 150. (équivalent  à contenu.x si son parent est le même que l’espace mis en paramètre dans la fonction getBounds ).

: nous donnera une valeur correspondante aux coordonnés y du bas de l’objet. Soit ici 170.

rect_global.bottomRight : nous renvoie un objet Point contenant les coordonnées x et y du de l’angle en bas à droite de « contenu ». Nous allons voir par la suite en quoi cet objet Point peut s’avérer utile.

La méthode scrollRect

Tout DisplayObject offre parmi ses méthodes la méthode scrollRect qui agit un peu comme un masque mais qui possède ses propres particularités. 

Pour initialiser cette méthode il faut fournir en paramètre une instance d’objet Rectangle qui fournira un cadre en dehors duquel l’objet sur lequel nous appliquons cette méthode sera invisible.

La différence avec un masque est qu’il n’est pas possible de fournir une autre forme qu’un carré. 

Enfin il ne faut JAMAIS placer un objet masqué à l’aide de setMask à l’intérieur d’un conteneur auquel un scrollRect est appliqué. Cela fera planter non seulement votre swf mais aussi le logiciel dans lequel s’exécutera l’application (soit flash ou le navigateur)

Naviguer dans une image plus grande que le cadre : 

              // déclarations de variables            var layer_photo:MovieClip;

              // le rectangle qui servira de reférence pour la navigation dans l'image.

              var limites_appli:Rectangle;

              // le container sur lequel nous allons appliquer la méthode scrollRect          var support_image:MovieClip = new MovieClip();            // vitesse de defilement du gestionnaire de mouvement           var speed: Number = 10;

              // je verifie que l'objet stage est accessible

              if(stage)           init()

              else

              this.addEventListener(Event.ADDED_TO_STAGE,init);

              // sinon je crée un écouteur pour m'en assurer

              function init(even:Event=null):void

              {

                     // si l'ecouteur à été place sur la scène (this) je l'enleve                if(this.hasEventListener(Event.ADDED_TO_STAGE))

                     this.removeEventListener(Event.ADDED_TO_STAGE,init);

                     //j'instancie la photo qui se trouve dans la bibliothèque de mon appli

                     layer_photo = new photo();

                     // je crée un rectangle aux dimensions du swf                  var rect_scene : Rectangle = new

Rectangle(0,0,stage.stageWidth,stage.stageHeight);

                     // je clone le rectangle pour m'en servir de référence pour la 

// navigation:

// Pour voir le bas droit de l'image la souris devra en bas et à  // droite de la scene

                     limites_appli = rect_scene.clone();

                     // je diminue les cotés du rectangle de 50 pixels chacun pour

// avoir une fênetre plus petite

                     rect_scene.inflate(-50,-50);

                     this.addChild(support_image);

                     // j'applique le rectangle en tant que masque.

                     support_image.scrollRect= rect_scene;           support_image.addChild(layer_photo);

                     // je place mon container image aux coordonnées du rectangle.

                     // le clip se trouve bien centré car la 

// méthode rectangle.inflate() diminue ou élargit

                     // les bords par rapport au centre.              support_image.x = rect_scene.x;                support_image.y = rect_scene.y;

                     // je place un ecouteur sur le parcours du scenario par la tête de lecture.

                     this.addEventListener(Event.ENTER_FRAME, mouvement);

              }

              function mouvement(even:Event):void

              {

                            // L'algorithme gerant le deplacement par rapport à la souris

       layer_photo.x += (-((mouseX)*((layer_photo.width/limites_appli.width) *(1-

(limites_appli.width/ layer_photo.width))))-( layer_photo.x))/speed; 

       layer_photo.y += (((mouseY)*((layer_photo.height/limites_appli.height)* (1-

(limites_appli.height/ layer_photo.height))))-( layer_photo.y))/speed;  }

L’objet Point.

L’objet Point est encore plus simple que l’objet rectangle. Il s’agit simplement d’une paire de coordonnées x et y.

On l’instancie de cette manière :

var point_a=new Point (200,200);

 point.x et point.y renvoient 200.

Je viens donc de définir un simple point se situant au croisement d’une abscisse et d’une ordonnée de 200 pixels chacun.

Deux propriétés interéssantes

Point.distance(point_a : Point , point_b :Point ) : Number 

Cette méthode me permet de connaître la distance en pixels entre deux points. Et puisque nous pouvons, comme nous venons de le voir, récupérer facilement les coordonnées de deux points à partir d’éléments sur la scène, calculer la distance entre eux, même s’ils ne sont pas alignés devient aisé 

J’obtiendrai avec ces paramètres la distance entre le coin bas-droit de l’objet 1 et le coin hautgauche de l’objet 2 

var distance :Number = Point.interpolate(objet_1.getBounds(stage).bottomRight, objet_1.getBounds(stage).topLeft);

J’utilise ici la possibilité que m’offre as3 de chainer les différentes méthodes en une seule instruction. 

Point.interpolate(point_a : Point , point_b :Point, ratio :Number ) : Point

Cette méthode me renvoie un objet Point. Ce point est celui qui se trouve entre les deux premiers entrés en paramètres, modérs par le paramètre ratio (qui est un décimal compris entre 0 et 1).

-    ratio = 0 : les coordonnées seront celle du point b 

-    ratio = 1 le Point renvoyé sera équivalent aux coordonnées du point a. 

-    ratio = 0.5 le point renvoyé sera équidistant aux deux autres. 

Pour un exemple de son utilisation reportez vous au dernier code du chapitre 6 sur les interpolations.  

Appendice 2

Envoyer une requête http :

Flash c’est bien mais comment envoyer le visiteur vers une autre page se son site ou vers un autre site ?

Le getUrl d’as 2 à été remplacé par une fonction globale du package : 

navigateToURL(requête : URLRequest, fenetre :String)

La requête est une instance d’URLRequest(). Cet objet mérite toute votre attention parce que c’est par son entremise que l’on pourra envoyer des variable par GET ou POST. Ajouter des headers. Reportez-vous au langage de référence pour son emploi et au chapitre 14 de l’ouvrage de thibaut Imbert : « Pratique de l’actionScript 3 ». Pour l’explication détaillée d’URLRequest (envoi de données par GET ou POST).

import .navigateToURL import .URLRequest;

//…

//…                          

bouton.addEventListener(MouseEvent.CLICK,ecouteClic,true);

function ecouteClic(even:MouseEvent):void

var requete:URLRequest = new URLRequest(“”);

       navigateToURL(requete,"_blank")

}



208