Forums Développement Multimédia

Les formations Mediabox
Les formations Mediabox

Un album photo dynamique et rigolo…

Compatible ActionScript 2. Cliquer pour en savoir plus sur les compatibilités.

Voici un petit album photo rigolo.
Il nous permettra d'aborder divers sujet comme XML, remplir un tableau en utilisant les Objets, les preloaders avec MovieClipLoader, setInterval, le déplacement exponentiel…
Soulignons toutefois que sur certains navigateurs, l'animation “rame” à cause du fond transparent…

Préparatifs

Tout d'abord le fichier “albumPhoto.xml”, qui sera enregistré au même niveau que le swf, et strucuré comme suit:

<?xml version="1.0" encoding="utf-8" ?>
<album>
	<image src="album/maPhoto01.jpg" desc="photo n°01"/>
	<image src="album/maPhoto02.jpg" desc="photo n°02"/>
	<image src="album/maPhoto03.jpg" desc="photo n°03"/>
	<image src="album/maPhoto04.jpg" desc="photo n°04"/>
	<image src="album/maPhoto05.jpg" desc="photo n°05"/>
	<image src="album/maPhoto06.jpg" desc="photo n°06"/>
	<image src="album/maPhoto07.jpg" desc="photo n°07"/>
	<image src="album/maPhoto08.jpg" desc="photo n°08"/>
	<image src="album/maPhoto09.jpg" desc="photo n°09"/>
	<image src="album/maPhoto10.jpg" desc="photo n°10"/>
</album>

Un petit rappel de maths sur le glissement exponentiel:
c'est un déplacement dans lequel on parcourt à chaque fois la moitié de la distance à parcourir, ce qui donne un effet de décélération ou de glissement, par exemple:

var destination:Number = 500;
var vitesse:Number = 2;
this.monClip.onEnterFrame = function(){
    var distance:Number = destination - this._x;
    this._x += distance/vitesse;
}

Et puis…c'est tout ! On va pouvoir rentrer directement dans le code.
Ah non, quand même, par souci esthétique j'ai placé un MC contenant une image d'appareil photo en haut à droite de la scène (fond noir, 800×600). Comme c'est original… LOL

Les mains dans le cambouis !

Allez, zou !

Declarons quelques variables

//==> échelle de départ vos photos (ici 20%, 5x plus petites)
var startScale:Number = 20;
//==> grossissement max de la photo
var maxScale:Number = 500;
/*
attention apres le chargement des photos, startSale devient l'echelle de référence,
donc vos photos réduites auront pour _xscale et _yscale 100,
ce qui revient à dire que maxScale = echelleVoulue*(100/startScale),
où echelleVoulue est l'echelle d'arrivée désirée...
*/
//==>les vitesses des différentes transformations`
//attention plus la vitesse augmente plus la transformation est lente...
var vitesseRotationIn:Number = 12;
var vitesseRotationOut:Number = 8;
var vitesseZoomIn:Number = 12;
var vitesseZoomOut:Number = 8;
//==> les identifiants de nos setIntervals qui vont effectuer les fonctions en boucle..
var nZoom:Number;
var nDeZoom:Number;
//==> la marge de l'image par rapport au background
var marge:Number = 1;
//==> le tableau qui contiendra les propriétés des images à charger
var aPhotos:Array = new Array();
//==>on crée le clip qui va contenir tout ça !
var Album:MovieClip = this.createEmptyMovieClip("album_mc",this.getNextHighestDepth());
//==>et enfin on crée le champ de texte dynamique qui va nous servir à afficher les descriptions,
this.createTextField("desc_txt",this.getNextHighestDepth(),20,550,760,40);
this.desc_txt.multiline = true;
this.desc_txt.selectable = false;
//ainsi que son TextFormat associé
var tfFormat:TextFormat = new TextFormat();
tfFormat.font = "Arial";
tfFormat.size = 20;
tfFormat.color = 0xffffff;
tfFormat.align = "center";

Voilà, passons maintenant à la récupération des données de notre fichier XML…

Le XML, les objets et le tableau …

//on crée l'objet xml
var listePhotos:XML = new XML();
listePhotos.ignoreWhite = true;
//lorsque le xml est chargé
listePhotos.onLoad = function(success:Boolean) {
	//si le chargement est réussi
	if (success) {
		//on associe une variable au noeud racine du xml (<album>...</album>)
		var noeudRacine:XMLNode = this.firstChild;
		//la propriété chidNodes est un tableau contenant tous les noeuds enfants du noeud appelé
                //donc là n est égal au nombre de noeuds enfants de noeudRacine
		var n:Number = noeudRacine.childNodes.length;
		//on effectue une boucle à chaque noeud 
		for (var i:Number = 0; i < n; i++) {
			//à chaque noeud//
			//on crée un nouvel objet nommé image
			var image:Object = new Object();
			//on définit une propriété src de l'objet image qui va contenir l'adresse de l'image…
			image.src = noeudRacine.childNodes[i].attributes.src;
			//…et une propriété desc qui va contenir la description de l'image
			image.desc = noeudRacine.childNodes[i].attributes.desc;
			//et on place cet objet dans notre tableau…
			aPhotos.push(image);
		}
		/*donc à partir de maintenant si je veux l'adresse de l'image 1, je n'ai plus qu'a faire:
		trace(aPhotos[0].src);
		et si je veux la description de l'image 8:
		trace(aPhotos[7].desc);
		Efficace, non ?
		*/
		//==> Maintenant on peut lancer la fonction qui va créer l'album photo,
		// en lui passant en parametres le clip Album et le tableau qui contient toutes les propriétés
                createAlbum(Album, aPhotos);
        //si le chargement a échoué...
	} else {
                //on envoie un message d'erreur
		throw new Error("erreur au chargement du XML");
	}
};
listePhotos.load("albumPhoto.xml");

Et maintenant les fonctions !

Tout d'abord celle qui entraine toutes les autres:
Rappel: “cible” fait référence au clip “Album” et “tableau” au tableau “aPhotos”

function createAlbum(cible:MovieClip, tableau:Array):Void {
	for (var i:Number = 0; i < tableau.length; i++) {
		//on crée le conteneur qui va subir toutes les transformations
		var conteneur:MovieClip = cible.createEmptyMovieClip("conteneur" + i, cible.getNextHighestDepth());
		//_x aléatoire entre 50 et (50+270) (pour éviter qu'elles n'empiètent sur la photo à gauche de la scène)
		conteneur._x = 50 + 270 * Math.random();
		//_y aléatoire entre 100 et (100+200) (pour éviter qu'elles n'empiètent sur le textField en bas de la scène)
		conteneur._y = 100 + 200 * Math.random();
		//rotation aléatoire entre -30 et 30 degrés, que l'on stocke en créant la propriété "startRoto"		conteneur.startRoto = -30 + 60 * Math.random();
		conteneur._rotation = conteneur.startRoto;
		//actions de bouton
		conteneur.onPress = cliquer;
		conteneur.onRelease = conteneur.onReleaseOutside = relacher;
		//creation du conteneur pour l'image
		var mcImage:MovieClip = conteneur.createEmptyMovieClip("image" + i, conteneur.getNextHighestDepth());
		//chargement de l'image
		loadJpeg(tableau[i].src, mcImage);
		//on associe notre clip à sa description en créant la propriété "desc"
		conteneur.desc = tableau[i].desc;
	}
}

Le gros morceau: celle qui va se charger de charger les images !

function loadJpeg(urlImage:String, cible:MovieClip) {
	//nouvelle instance de MovieClipLoader
	var mclLoader:MovieClipLoader = new MovieClipLoader();
	//objet qui va écouter le chargement de l'image
	var oEcouteur:Object = new Object();
	//on assigne oEcouteur à mclLoader
	mclLoader.addListener(oEcouteur);
	//au début du chargement (mcContent est une référence au clip chargé)...
	oEcouteur.onLoadStart = function(mcContent:MovieClip) {
		//on crée un preloader...
		var loader:MovieClip = mcContent.createEmptyMovieClip("loader", 0);
		//dans lequel il y a une barre de fond ...
		var loaderBg:MovieClip = loader.createEmptyMovieClip("loaderBg", 0);
		with (loaderBg) {
			beginFill(0x666666, 50);
			lineTo(50, 0);
			lineTo(50, 5);
			lineTo(0, 5);
			lineTo(0, 0);
			endFill();
		}
		//et une barre de chargement de mêmes dimensions...
		var loadBar = loader.createEmptyMovieClip("loadBar", 1);
		with (loadBar) {
			beginFill(0xffffff, 100);
			lineTo(50, 0);
			lineTo(50, 5);
			lineTo(0, 5);
			lineTo(0, 0);
			endFill();
		}
	};
	//pendant le chargement...
	oEcouteur.onLoadProgress = function(mcContent:MovieClip, loaded:Number, total:Number) {
		//on calcule le pourcentage de bytes chargés...
		var pourcentage:Number = Math.round(100 * (loaded / total));
		//et on l'assigne au _xscale de la barre de chargement
		mcContent.loader.loadBar._xscale = pourcentage;
	};
	//à la fin du chargement...
	oEcouteur.onLoadInit = function(mcContent:MovieClip) {
		//on suprime le preloader...
		removeMovieClip(mcContent.loader);
		//on met nos images à l'echelle de départ...
		mcContent._xscale = mcContent._yscale = startScale;
		//conteneur fait référence au conteneur crée dans createAlbum();
		var conteneur:MovieClip = mcContent._parent;
		//on crée un MC qui va se placer derrière l'image chargée (mcContent.getDepth() - 1)...
		var backGround:MovieClip = conteneur.createEmptyMovieClip("backGround", mcContent.getDepth() - 1);
		//on dessine le backGround...
		var bgWidth:Number = mcContent._width + marge * 2;
		var bgHeight:Number = mcContent._height + marge * 2;
		backGround.beginFill(0xffffff, 100);
		backGround.lineTo(bgWidth, 0);
		backGround.lineTo(bgWidth, bgHeight);
		backGround.lineTo(0, bgHeight);
		backGround.lineTo(0, 0);
		backGround.endFill();
		//et on repositionne l'image...
		mcContent._x = mcContent._y = marge;
	};
	//enfin on dit à mclLoader de charger l'image en question (tableau[i]...rappelez-vous !)
	mclLoader.loadClip(urlImage, cible);
}

Celles qui gèrent le onPress et le onRelease: Rappel: dans un gestionnaire d'évènement, le this fait référence à l'objet qui emet l'évènement !
(qui donc dans ce cas-là ?) ;-)

//Au clic (onPress)
function cliquer() {
	//on appelle la description
	this._parent._parent.desc_txt.text = "Description:" + newline + this.desc;
        //on applique le TextFormat
        this._parent._parent.desc_txt.setTextFormat(tfFormat);
	//on cache la souris...
	Mouse.hide();
	//on stoppe l'éxécution de la fonction deZoom
	clearInterval(nDeZoom);
	//
	this._x0 = _xmouse - this._x;
	this._y0 = _ymouse - this._y;
	//on fait passer le clip au-dessus de tous les autres
	this.swapDepths(Album.getNextHighestDepth());
	//et on appelle la fonction zoom() toutes les 25millisecondes
	nZoom = setInterval(zoom, 25, this);
}
//onRelease,onReleaseOutside...
function relacher() {
	//on montre la souris
	Mouse.show();
	//on tue la fonction zoom
	clearInterval(nZoom);
	//on assigne une nouvelle valeur d'arrivée à la rotation du clip
	this.startRoto = -30 + (60 * Math.random());
	//appelle la fonction deZoom toutes les 25ms
	nDeZoom = setInterval(deZoom, 25, this);
}

Et enfin celles qui s'occupent du redimensionnement des images !

//ZOOM
function zoom(cible:MovieClip) {
	//on fait suivre la souris par l'image
	cible._x = _xmouse - cible._x0;
	cible._y = _ymouse - cible._y0;
	//on rétablit la rotation du clip
	cible._rotation += -cible._rotation / vitesseRotationIn;
	//et on Zoome (cf le rappel sur le déplacement exponentiel !)
	cible._xscale += (maxScale - cible._xscale) / vitesseZoomIn;
	cible._yscale += (maxScale - cible._yscale) / vitesseZoomIn;
	//si on est très proche de l'arrivée,
	if (cible._xscale > maxScale - 1) {
		//on y va directement, sans quoi on y arrive jamais...
		cible._xscale = cible._yscale = maxScale;
		cible._rotation = 0;
	}
	updateAfterEvent();
}
//DéZOOM
function deZoom(cible:MovieClip) {
	//on refait tourner le clip 
	cible._rotation += (cible.startRoto - cible._rotation) / vitesseRotationOut;
	//on dézoome
	cible._xscale += (100 - cible._xscale) / vitesseZoomOut;
	cible._yscale += (100 - cible._yscale) / vitesseZoomOut;
	//si on est très proche de l'arrivée,
	if (cible._xscale < 101) {
		//on y va directement, sans quoi on y arrive jamais...
		cible._rotation = cible.startRoto;
		cible._xscale = cible._yscale = 100;
	}
	updateAfterEvent();
}
//

Et voilà ! Si ce code vous a plu, que vous y avez apporté des améliorations, tant au niveau du script qu'au niveau graphique, ou que vous bloquez sur un passage du tutoriel, n'hésitez pas à me le faire savoir ! 8-)

par JulesB , le 08/10/2006 22:31