Forums Développement Multimédia

Les formations Mediabox
Les formations Mediabox

Charger des images à la file

Compatible ActionScript 3. Cliquer pour en savoir plus sur les compatibilités.Par Nataly, le 18 septembre 2010

Bonjour :)

Si vous êtes là, c'est que vous êtes confronté à l'un des problèmes récurrents rencontrés sur le forum : le chargement d'images à la file.

Différents cas de figure se présentent :

  1. Des images extérieures dont on connait le nom et la localisation
  2. Des images extérieures qui sont décrites dans un fichier XML (susceptibles de changer donc tant dans la dénomination que la localisation).



Je souhaitais un document qui réponde à la préoccupation : je veux afficher plusieurs images (illustration : jpg, png, gif…).
Mais, charger des images extérieures ou ajouter dynamiquement des instances de symboles contenant des photos, sont des techniques qui, si elle ont en commun les mots plusieurs et images, n'en font pas moins appel à des techniques différentes.

Mon oreillette et moi même avons donc convenu de découper ce tuto en deux parties : ici le traitement d'images extérieures, là-bas la manipulation de symboles.

Je ne reviendrai pas sur l'utilisation des techniques mises en œuvres : coup de chance pour nous Tannoy s'en est occupé :)
Les classes Loader, LoaderInfo et URLRequest : Charger des images ou des swf en ActionScript 3
Plus spécifiquement avec un XML : Charger et lire un XML en ActionScript 3

Nous aurons aussi recours aux tableaux, ne fuyez pas si ça vous rebute, allez plutôt faire un tour par là.

Les images sont extérieures

Le cas de figure le plus fréquent quand on manipule des images, c'est de laisser à l'extérieur - afin de ne pas alourdir le swf - et on les charge à la demande.

Remarque préliminaire

On sait (si si) charger une unique image:

// Charger une  image
var ldrFichier:Loader = new Loader();
ldrFichier.contentLoaderInfo.addEventListener(Event.COMPLETE, fini);
ldrFichier.load(new URLRequest("image.jpg"));

On sait aussi que les opérations de chargement sont asynchrones, comprendre chaque chargement mène sa vie dans son coin, et le premier fichier dont on demande le chargement ne sera pas forcément le premier dont le chargement sera terminé. Par exemple:

// Charger une première image
var ldrFichier1:Loader = new Loader();
ldrFichier1.contentLoaderInfo.addEventListener(Event.COMPLETE, fini1);
ldrFichier1.load(new URLRequest("image1.jpg"));
// Charger une deuxième image
var ldrFichier2:Loader = new Loader();
ldrFichier2.contentLoaderInfo.addEventListener(Event.COMPLETE, fini2);
ldrFichier2.load(new URLRequest("image2.jpg"));

Si la première image est très lourde et la suivante au contraire toute légère il y a fort à parier que la deuxième “arrivera” avant la première. La fonction fini2 sera appelée avant la fonction fini1. De plus charger beaucoup d'images lourdes simultanément gave les ressources. J'ai testé 50 images de 1,5 Mo “simultanément”, c'est déjà trop :-(. Dans la suite nous ferons donc attention à cela.

On en connait le nom et la localisation

Imaginons que vous sachiez que les images seront toujours dans le même répertoire et porteront toujours le même nom.
Puisque les images sont extérieures, il faut avoir recours aux classes Loader, LoaderInfo et URLRequest.

Si on travaille dans le fla, on n'a pas besoin d'importer les bibliothèques, en revanche depuis un fichier .as il faudra penser à les importer. Pour mémoire :

import flash.display.Loader
import flash.display.LoaderInfo 
import flash.net.URLRequest

Je considère le cas le plus fréquent où on stocke les images au fur et à mesure dans un tableau.
D'abord déclarer ce dont on va avoir besoin :

//Le tableau pour stocker
var tabPhotos:Array=new Array();
// le nombre de photos à charger
var nbPhotos:int=10;

Et on est prêt à réfléchir à la marche à suivre :)

Comme les opérations de chargement sont asynchrones lancer tous les chargements dans une boucle, écouter pour chacun l'événement Event.COMPLETE, et stocker dans un tableau au fur et à mesure des chargements aboutis, ne garanti pas que les images seront stockées dans l'ordre dans lequel on en a appelé le chargement.
Pour charger les images à la file, il suffit de lancer le chargement de la première, écouter l'événement Event.COMPLETE dans lequel on demande le chargement de la suivante jusqu'à ce que mort s'en suive - bon d'accord, jusqu'à la dernière…

la fonction de chargement

Appelons-la charge (quelle créativité !) et précisons qu'elle attend un nom de fichier (String) :

// Charger le premier fichier
charge("P0.jpg");
 
function charge(pNom:String):void {
        // le loader
	var ldrFichier:Loader= new Loader();
        // son loaderInfo
	var ldrInfo:LoaderInfo=ldrFichier.contentLoaderInfo;
        // l'écouteur de fin de chargement
	ldrInfo.addEventListener(Event.COMPLETE, fini);
        // Chargez !
	ldrFichier.load(new URLRequest(pNom));
}


La fonction de rappel


On considère que les fichiers sont nommés P0.jpg, P1.jpg, P2.jpg, etc.

function fini(e:Event) {
        // le loader
        var ldr:Loader= e.target.loader
        // remplir le tableau et compter le nb d'éléments
	var nb:int=tabPhotos.push(ldr);
	if (nb<nbPhotos) {
		// ce n'est pas fini : charger le suivant
		charge("P"+nb+".jpg");
	} else {
               // On a tout 
		trace("tableau prêt à être utilisé : "+ tabPhotos)
              // suite du traitement
	}
        // supprimer l'écouteur qui ne sert plus à rien
        ldr.contentLoaderInfo.removeEventListener(Event.COMPLETE, fini);
}

Le tour est joué :)

Les noms et autres infos sont décrits dans un fichier XML

Imaginons donc un fichier .xml simpliste qui décrit l'adresse et le titre de plusieurs photos :

<?xml version="1.0" encoding="UTF-8"?>
<visuels>
    <element image="http://………/ImgNature/campagne750.jpg" titre="Campagne" />
    <element image="http://………//ImgNature/desert750.jpg" titre="Desert" />
    <element image="http://………//ImgNature/paysage750.jpg" titre="Paysage" />
</visuels>

Il suffit de récupérer les informations dont nous avons besoin, ici l'attribut image, et de les utiliser avec des Loader à la file comme on vient de le voir.

Pour charger un fichier .xml on a recours aux classes XML URLRequest et URLLoader, si vous travaillez dans un fichier .as il faudra importer le deux dernières (XML est de niveau supérieur).

import flash.net.URLLoader;
import flash.net.URLRequest

Et hop c'est parti ! On déclare :

var xmChemin:String="donnees.xml";
var xml:XML;
var chargeurXML = new URLLoader();
var nbElt:int;

Un écouteur de fin de chargement et on charge :

chargeurXML.addEventListener(Event.COMPLETE, finChargementXML);
chargeurXML.load(new URLRequest(xmChemin));

Dans la fonction de rappel, il suffit de créer l'objet XML, d'en compter le nombre d'éléments et de lancer le chargement du premier :

function finChargementXML(e:Event):void {
	if ((e.target as URLLoader) != null ) {
                // nouvel objet XML contenant les données
		xml=new XML(chargeurXML.data);
		xml.ignoreWhitespace=true;
		trace(xml);
                // nombre déléments
		nbElt=xml.element.length();
		trace(nbElt);
                // Charger la première image
		charge(xml.element[0].@image);
                // supprimer l'écouteur
                e.target.removeEventListener(Event.COMPLETE, finChargementXML);
 
	}
}

Quant à la fonction charge, si l'idée c'est de remplir un tableau à utiliser à son gré, on l'a déjà : c'est ce qu'on a écrit au chapitre précédent.
A la nuance près que la fonction de rappel fini n'appèlera pas le fichier suivant par une simple concaténation : charge(“P”+nb+”.jpg”), mais en parcourant l'objet xml : charge(xml.element[idx_c].@image).
Reportez vous au code du pêle mêle, ci-dessous, pour une proposition de mise en œuvre.

Et bien voilà :) C'est déjà fini, youpi.

Mise en œuvre: un "pêle-mêle"

Les exemples de code que j'ai proposés jusqu'ici utilisent tous un tableau, c'est parce que c'est le cas de figure le plus courant, n'en faites pas une règle inaltérable.

Si, par exemple, on veut simplement charger des images pour en faire un pêle-mêle…

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

Ici, les images sont affichées puis manipulables à la souris (déplacement, passage au premier plan et autres fignoleries).
Aucun besoin d'un tableau pour y accéder dans ce cas : les écouteurs peuvent être associés au fur et à mesure des chargements, et l'objet MouseEvent des fonctions de rappel portera toutes les infos nécessaires.

Solution images externes et xml

Le code proposé ci-dessous considère des images externes et répond aux contraintes suivantes :

Soit un fichier .xml avec la même structure qu'en haut de chapitre, donc un attribut image pour l'adresse des photos et un attribut titre.
On affiche toutes les photos avec leur titre.
Elles ont pour dimension 200/200
Elles sont disposées de façon aléatoire
Elles sont en alpha 0.8

Au survol elles passent opaques
Elles sont déplaçables (drag and drop)
Elles passent en premier plan au clic ou déplacement
Majuscule-clic bascule de taille originale à taille réduite.

Attention au titre des photos qui ne doit pas subir de réduction d'échelle

var xmChemin:String="PeleMele.xml";
var xml:XML;
var chargeurXML = new URLLoader();
 
 
var nbElt:int;// nombre d'images à charger
var idx_c:int=0;// idx de la photo en cours 
chargeurXML.addEventListener(Event.COMPLETE, finChargementXML);
chargeurXML.load(new URLRequest(xmChemin));
 
 
// xml chargé
function finChargementXML(e:Event):void {
	if ((e.target as URLLoader) != null ) {
		xml=new XML(chargeurXML.data);
		xml.ignoreWhitespace=true;
		trace(xml);
		nbElt=xml.element.length();
		trace(nbElt);
		charge(xml.element[0].@image);
                e.target.removeEventListener(Event.COMPLETE, finChargementXML);
	}
}
 
// charge les photos
function charge(pNom:String):void {
	var ldrFichier:Loader=new Loader();
	var ldrInfo:LoaderInfo=ldrFichier.contentLoaderInfo;
 
	ldrInfo.addEventListener(Event.COMPLETE, fini);
	ldrFichier.load(new URLRequest(pNom));
}
// En fin de chaque photo chargée
function fini(e:Event) {
        var ldr:Loader=e.target.loader;
	// conteneur photo
	var cont:Sprite=new Sprite();
	// champ texte pour titre de la photo
	var txt:TextField = new TextField();
	txt.type=TextFieldType.DYNAMIC;
	txt.autoSize="left";
	// titre de la photo
	txt.text=xml.element[idx_c].@titre;
	var format:TextFormat=new TextFormat();
	format.size=14;
	format.font="Times";
	format.color=0xcccccc;
	txt.setTextFormat(format);
 
	with (cont) {
		buttonMode=true;
		// ajoute l'image chargée
		addChild(ldr);
		// ajoute le texte
		addChild(txt);
		// modifie la taille de l'image - pas celle du sprite : le texte serait lui aussi réduit
		ldr.width=ldr.height=200;
		// disposer aléatoirement
		x=Math.random()*200;
		y=Math.random()*300;
		alpha=0.8;
		addEventListener(MouseEvent.MOUSE_OVER,survol);
		addEventListener(MouseEvent.MOUSE_OUT,quiteSurvol);
		addEventListener(MouseEvent.MOUSE_DOWN,enfonce);
		addEventListener(MouseEvent.MOUSE_UP,lache);
	}
	// afficher
	this.addChild(cont);
	// incrément du compteur
	idx_c++;
	if (idx_c<nbElt) {
		// charger le suivant
		charge(xml.element[idx_c].@image);
	}
        ldr.LoaderInfo.removeEventListener(Event.COMPLETE, fini);
}
 
 
function survol(me:MouseEvent) {
	me.currentTarget.alpha=1;
}
 
function quiteSurvol(me:MouseEvent) {
	me.currentTarget.alpha=0.8;
}
 
function enfonce(me:MouseEvent) {
	// passer au premier plan
	addChild(Sprite(me.currentTarget));
	// si maj enfoncée
	if (me.shiftKey) {
		// on teste le loader contenu
		with (me.currentTarget.getChildAt(0)) {
			// si grand rétrécir et inversement
			if (scaleX==1) {
				width=height=200;
				me.currentTarget.x=this.mouseX-100;
				me.currentTarget.y=this.mouseY-100;
			} else {
				scaleX=scaleY=1;
				me.currentTarget.x=me.currentTarget.y=0;
			}
		}
		return;
	}
	// Maj non enfoncée : on glisse
	me.currentTarget.startDrag(false);
}
 
function lache(me:MouseEvent) {
	stopDrag();
}

Pour tout savoir, vraiment

Vous trouverez dans les ressources, nombre de tutos traitant du sujet chargement en général. Je vous invite à les consulter pour finir d'être tout à fait au point sur le sujet.

Les ressources externes et leur chargement par Goabonga
Chapitre 13 du désormais célèbre ouvrage de T. Imbert, traitant en détail du chargement de contenu

On peut aussi, dans le cadre de chargement massifs, avoir recours à la librairie MassLoad :
Chargement de fichier et MassLoad
API MassLoad - Masapi (Préchargement en masse)
ou encore à loaderMax