Forums Développement Multimédia

Les formations Mediabox
Les formations Mediabox

Créer une animation en mosaïque

Compatible ActionScript 1 et 2. Utilise des techniques dépréciées. Cliquer pour en savoir plus sur les compatibilités.

Salut à tous

Ce tuto vise à se rapprocher de l'animation ci-dessous :

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

N'étant pas encore familier avec la POO je me contente ici de prototypes

La première chose à faire est de découper notre image favorite en une mosaïque de jpeg dans Photoshop, voici la marche à suivre:

1. Se munir de l'outil tranche, et créer une grande tranche pour toute la taille du document.

2. clic droit sur la tranche et…

slicedivide8sn.jpg

choisir son maillage. Pour ce tuto il est indispensable de chosir le même nombre de divisions en X qu'en Y. Pour ma part j'ai choisi 10 * 10 (on ne peut aller au-delà).

slicedivide29jq.jpg

3. Enfin enregistrer pour le web les tranche (CTRL + SHIFT + ALT + S). Choisir le format JPEG, et valider. Puis, procéder comme ci-dessous:

save0iy.jpg

Pour “paramètres” mettre “autre”, une boite de dialogue s'affiche:

saveasparameters8cr.jpg

Chaque JPEG ainsi enregistré va se situer dans le dossier “image” et avoir pour nom le numero de la tranche, ce qui va nous permettre de les charger dans le SWF à l'aide d'une boucle for(){}…

Nos fichiers image sont fin prêts, bifurquons de ce pas sur Flash

Ouvrons un nouveau document de la taille de notre image finale, et créons 2 clips vides dans la bibliothèque. Nommons leurs identifiants de liaison respectifs: “id_carre”, et “id_mc_conteneur”

Créons une image clé vide sur la scène principale, et copions-collons ce code:

#include "easing_equations.as" 
 
 
 MovieClip.prototype.animeFlashZone = function(imageWidth, imageHeight, divWidth, divHeight, equaPenner, decalage, duration, action){ 
 
this.attachMovie("id_mc_conteneur", "mc_conteneur1", 0) 
 
coordCarreX = new Array() 
coordCarreY = new Array() 
 
for(var i = 1; i<= divHeight; i++){ 
   for(var j = 1; j<= divWidth; j++){ 
 
      x = imageWidth * ((2 * j - 1) / (2 * divWidth)) 
      y = imageHeight * ((2 * i - 1) / (2 * divHeight)) 
 
      coordCarreX.push (x) 
      coordCarreY.push (y) 
   } 
} 
 
 
 
var carreWidth = imageWidth / divWidth 
var carreHeight = imageHeight / divHeight 
 
loader = new MovieClipLoader(); 
 
 loadListener = new Object(); 
 
loader.addListener(loadListener); 
 
  for(var i = 1; i <= divWidth * divHeight; i++){ 
 
   this.mc_conteneur1.duplicateMovieClip("mc_conteneur" + i, (i - 1)) 
   this["mc_conteneur" + i].attachMovie("id_carre", "carre", 0) 
 
   this["mc_conteneur" + i].carre._x = - carreWidth/2 
    this["mc_conteneur" + i].carre._y = - carreHeight/2 
 
loader.loadClip("images/" + i + ".jpg", this["mc_conteneur" + i].carre); 
 
   this["mc_conteneur" + i]._x = coordCarreX[i - 1] 
 
   this["mc_conteneur" + i]._y = coordCarreY[i - 1] 
 
   this["mc_conteneur" + i]._alpha = 0 
} 
 
 
 
 
loadListener.onLoadInit = function() {  
 
for(var i = 0; i <= (orderOccu.length - 1); i++){ 
 
   for(var j = 0; j <= (orderOccu[i].length - 1); j++){ 
 
   _root["mc_conteneur" + String(orderOccu[i][j])].id = i + 1 
 
   _root["mc_conteneur" + String(orderOccu[i][j])].t = 0 
 
   } 
}  
 
_root.onEnterFrame = function(){ 
   this.animeMosaique(decalage, duration, equaPenner, action) 
} 
 
} 
 
 
this.setMosaiDiagoOrder(divWidth, divHeight) 
 
t = 0 
 
} 
 
 
 
MovieClip.prototype.setMosaiDiagoOrder = function(divWidth, divHeight){ 
 
   orderOccu = new Array() 
 
var totalDiagos = divWidth + divHeight - 1 
 
      for(var i = 1; i <= totalDiagos ; i++){ 
         orderOccu[i - 1] = new Array() 
      } 
 
      orderOccu[0][0] = 1 
 
      orderOccu[totalDiagos - 1][0] = divWidth * divHeight 
 
      for(var i = 1; i <= divWidth ; i++){ 
 
   if(i >= 2){ 
 
      var count = i 
 
   for(var j = 1; j <= i; j++){ 
 
   orderOccu[i - 1][j - 1] = count 
 
   count += (divWidth  - 1) 
 
       } 
 
   } 
 
} 
 
 
for(var i = totalDiagos; i >= (divWidth + 1); i--){ 
 
   if(i < (divWidth + divHeight - 1)){ 
 
   var count = divWidth * divHeight - (totalDiagos - i) 
 
   for(var j = 1; j <= (divWidth * 2) - i; j++){ 
 
   orderOccu[i - 1][j - 1] = count 
 
   count -= (divWidth  - 1) 
       } 
      } 
    } 
 
} 
 
 
 
MovieClip.prototype.animeMosaique = function(decalage, duration, equaPenner, action){ 
 
 
      for(var i = 0; i <= (orderOccu.length - 1); i++){ 
 
   for(var j = 0; j <= (orderOccu[i].length - 1); j++){ 
 
   if(t >= this["mc_conteneur" + String(orderOccu[i][j])].id * decalage){ 
 
      if(this["mc_conteneur" + String(orderOccu[i][j])].t <= duration){ 
 
   this["mc_conteneur" + String(orderOccu[i][j])]._xscale = this["mc_conteneur" + String(orderOccu[i][j])]._yscale = equaPenner(this["mc_conteneur" + String(orderOccu[i][j])].t, 1, 100, duration) 
 
   this["mc_conteneur" + String(orderOccu[i][j])]._alpha = equaPenner(this["mc_conteneur" + String(orderOccu[i][j])].t, 0, 100, duration) 
 
this["mc_conteneur" + String(orderOccu[i][j])].t++ 
 
   }else if(this["mc_conteneur" + 100].t == duration){ 
 
      action() 
      delete this.onEnterFrame 
 
   } 
   } 
 
   } 
 
} 
t++ 
} 
 
 
 
this.animeFlashZone(320, 240, 10, 10, Math.easeOutExpo, 3, 50, TR)

Explainations:

#include "easing_equations.as"

On appelle dans le code des equations de Penner situé dans un fichier .as dans le même dossier

MovieClip.prototype.animeFlashZone = function(imageWidth, imageHeigth, divWidth, divHeight, equaPenner, decalage, duration, action){

La fonction nécéssite 6 paramètres:

1. la largeur de l'image en pixels concernée par l'animation 2. la hauteur de l'image en pixels concernée par l'animation 3. le nombre de clips de la mosaïque sur la largeur de la scène 4. le nombre de clips de la mosaïque sur la hauteur de la scène 5. l'équation de Penner souhaitée pour le tween des propriétés _xscale _yscale et _alpha de chaqu'un des clips de la mosaïque 6. Le décalage de temps (fps) entre le début des tweens respective de chaque clips de la mosaïque 7. La duree de temps (fps) des tweens respectifs de chaque clips de la mosaïque 8. L'action choisie (fonction) à la fin du Tween du dernier clip de la mosaïque (à la fin de l'animation dans sa globalité)

this.attachMovie("id_mc_conteneur", "mc_conteneur1", 0) 
 
coordCarreX = new Array() 
coordCarreY = new Array()

chaqu'un de ces tableaux va stocker les coordonnées de chaqu'un des clips de la mosaïque

for(var i = 1; i<= divHeight; i++){ 
   for(var j = 1; j<= divWidth; j++){ 
 
      //Une boucle dans une boucle permet par exemple d'obtenir des coordonnées sur 2 dimensions (x, y) 
 
      x = imageWidth * ((2 * j - 1) / (2 * divWidth)) 
      y = imagedivHeight * ((2 * i - 1) / (2 * divHeight)) 
 
      //De cette façon on obtient non pas les 
//coordonnées pour un centre de coordonnées de clip de la mosaïque 
// sur son coin supérieur gauche mais centré 
 
      coordCarreX.push (x) 
      coordCarreY.push (y) 
   } 
} 
 
 
var carreWidth = imageWidth / divWidth 
var carreHeight = imageHeight / divHeight}

Soient 2 variables définissant respectivement la largeur et la hauteur en pixels d'un movieClip de la mosaïque

loader = new MovieClipLoader();

Afin de charger les jpeg tout en pouvant utiliser un gestionnaire d'évènements permettant de declarer des variables locales aux occurences APRES le chargement (sans quoi elles sont supprimées)

 loadListener = new Object();

Attribution de l'écouteur

loader.addListener(loadListener);

L'écouteur hérite ainsi des gestionnaires d'évènements de la classe MovieClipLoader

for(var i = 1; i <= divWidth * divHeight; i++){ 
 
   this.mc_conteneur1.duplicateMovieClip("mc_conteneur" + i, (i - 1)) 
   this["mc_conteneur" + i].attachMovie("id_carre", "carre", 0) 
 
//Chaque clip "mc_contenur" va contenir un clip "carre" qui lui va 
//charger le fichier image. On utilise un mc parent dont le centre 
//de coordonnées va être centré par rapport à son contenu, car il 
//est impossible de changer dynamiquement le centre de 
//coordonnées d'un clip. 
 
   this["mc_conteneur" + i].carre._x = - carreWidth/2 
    this["mc_conteneur" + i].carre._y = - carreHeight/2 
 
//On positionne d'avance le clip enfant qui va contenir le JPEG à cet effet 
 
 
loader.loadClip("images/" + i + ".jpg", this["mc_conteneur" + i].carre); 
 
//Chargement de l'image 
 
   this["mc_conteneur" + i]._x = coordCarreX[i - 1] 
 
   this["mc_conteneur" + i]._y = coordCarreY[i - 1] 
 
//Chaque clips conteneur sont positionnées de sorte à former la mosaïque 
 
   this["mc_conteneur" + i]._alpha = 0 
} 
 
 
 
 
 
loadListener.onLoadInit = function() { 
 
 //Fin du chargement... Déclaration de variables locales aux occurences 
 
for(var i = 0; i <= (orderOccu.length - 1); i++){ 
 
   for(var j = 0; j <= (orderOccu[i].length - 1); j++){ 
 
   _root["mc_conteneur" + String(orderOccu[i][j])].id = i + 1

Chaque clips conteneurs situés sur la même diagonale ont un même identifiant

ordreid5ks.jpg

_root["mc_conteneur" + String(orderOccu[i][j])].t = 0 
 
   //Une varible t locale à chaque clips qui va 
//s'incrémenter indépendament des autres, car il y aura un 
//décalage entre les animations respectives de chaque groupes de 
//clips (ceux qui ont le même identifiant) 
 
   } 
}  
 
 
_root.onEnterFrame = function(){ 
   this.animeMosaique(decalage, duration, equaPenner, action) 
 
   //A la cadence de l'animation on appelle la fonction 
//(ou méthode) qui sera déclarée plus loin 
 
} 
 
} 
 
 
this.setMosaiDiagoOrder(divWidth, divHeight) 
 
t = 0 
 
Variable de temps 
 
}

setMosaiDiagoOrder permet de stocker dans un nested array les identifiants de chaque occurences groupées en diagonale qui vont être concernés sur le même laps de temps par l'animation

MovieClip.prototype.setMosaiDiagoOrder = function(divWidth, divHeight){ 
 
   orderOccu = new Array() 
 
var totalDiagos = divWidth + divHeight - 1 
 
//Nbre total de diagonales pour cette mosaïque 
 
 for(var i = 1; i <= totalDiagos ; i++){ 
 
       orderOccu[i - 1] = new Array() 
 
    Les index respectifs des nested Array() du tableau orderOccu vont contenir les identifiants des moviClips inclus dans chaque diagonales 
 
   } 
 
      orderOccu[0][0] = 1 
 
     //Le tout premier movieClip en haut à gauche 
 
    orderOccu[totalDiagos - 1][0] = divWidth * divHeight 
 
       //Le tout dernier movieClip en bas à droite 
 
 
   for(var i = 1; i <= divWidth ; i++){ 
 
       //Premiere moitie de la mosaique 
 
   if(i >= 2){ 
 
   //Les premières diagonales sur la mosaïque d'objets 
 
      var count = i 
 
   for(var j = 1; j <= i; j++){ 
 
   orderOccu[i - 1][j - 1] = count 
 
   count += (divWidth  - 1) 
 
       } 
 
   } 
 
} 
 
for(var i = totalDiagos; i >= (divWidth + 1); i--){}

On attaque la seconde moitié de la mosaïque ( diagonales restantes) On procède de la même façon que précédemment mais en mirroir, on part donc de la dernière diagonale vers celle qui est juste à droite de la grande diagonale. Un petit shéma pour vous aider:

codeexplanation9cl.jpg

  if(i < (divWidth + divHeight - 1)){ 
 
   var count = divWidth * divHeight - (totalDiagos - i) 
 
// a chaque incrémentation de i, (totalDiagos - i) s'incrémente lui positivement 
 
   for(var j = 1; j <= (divWidth * 2) - i; j++){ 
 
   orderOccu[i - 1][j - 1] = count 
 
   count -= (divWidth  - 1) 
 
       } 
      } 
    } 
 
} 
 
 
 
MovieClip.prototype.animeMosaique = function(decalage, duration, equaPenner, action){ 
 
//Enfin le prototype regissant les tweens, les paramètres sont pour la plupart ceux des equations de tween de Penner. J'avais fait un tuto sur le sujet dans l'autre forum, je le reposterai donc très bientôt... 
 
 
      for(var i = 0; i <= (orderOccu.length - 1); i++){ 
//i s'incrémente jusqu'au nombre maximal de diagonales 
 
   for(var j = 0; j <= (orderOccu[i].length - 1); j++){ 
 
   //boucle enfant qui parcourt chaque index du nested Array() correspondant 
 
 
   if(t >= this["mc_conteneur" + String(orderOccu[i][j])].id * decalage){ 
 
//t comme vous pouvez le voir plus bas commence à s'incrémenter dès 
//le début de l'animation. Or on souhaite un décalage entre 
//chaque groupes de clips (diagonales). Plus l'identifiant d'un clip 
//se rapproche de 1, plus sa diagonale est proche de l'angle 
//supérieur gauche de la mosaïque, et plus ce clip est animé tôt 
//par rapport aux autres 
 
      if(this["mc_conteneur" + String(orderOccu[i][j])].t <= duration){ 
     //Merci Penner... 
 
   this["mc_conteneur" + String(orderOccu[i][j])]._xscale = this["mc_conteneur" + String(orderOccu[i][j])]._yscale = equaPenner(this["mc_conteneur" + String(orderOccu[i][j])].t, 1, 100, duration) 
 
   this["mc_conteneur" + String(orderOccu[i][j])]._alpha = equaPenner(this["mc_conteneur" + String(orderOccu[i][j])].t, 0, 100, duration) 
 
this["mc_conteneur" + String(orderOccu[i][j])].t++ 
 
   }else if(this["mc_conteneur" + 100].t == duration){ 
 
// si l'animation du dernier movieClip de la mosaïque est achevée, 
//on peut clore le onEnterFrame et pourquoi pas appeller une fonction 
 
      action() 
      delete this.onEnterFrame 
 
   } 
   } 
 
   } 
 
} 
t++ 
} 
 
 
 
this.animeFlashZone(320, 240, 10, 10, Math.easeOutExpo, 3, 50, TR)

Vous pouvez bien sûr essayer d'autres equations de Penner en paramètre, l'une d'elles rend particulièrement bien c'est la Math.easeOutBounce: Ca donne l'impression d'une onde qui se propage!

Et voilà, ce fût une animation du fortiche Vincent Crublé:

http://www.zoneflash.net/

Guimetz


OK en effet j'ai trouver que ce n'est pas tres evident de travaillez avec les tranche et le photoshop . figuez vous que vous avez une dizaine d'images à charger qu est que vous allez faire et ben vous allez orientez vers une solution plus facile a implémenter mais avec cette astuce tu va utiliser adorablement votre technique avec un minimum de code aussi puisque vous oubliez que chaque cellule ds la matrice possede la meme somme de i+j avec ces voisins en diagonales : Donc il sefit donner un identifiant qui vaut (i+j) a un clip pour l'associer a un groupe . et de cette façon tu va appliquer l'effet (equation de penner ou autre) a tous le groupe Or c'est quoi un groupe :

-Un group est un tableau ds le tableau global (c'est tab_grp) qui contient toutes les groupes -Un group est un ensemble de clip qui possede le meme id

Si on determine d'autre type de groupement par exemple du centre vers les cotés du tableau nous allons completement changer l'effet

seulement on applique le easing au premiers group qui contient un seuls clip et les autre suivent le mvt

enfin vous pouvez donc utuiliser le clip conteneur comme masque Dynamique sur n'importe qu'elle image chargé pour obtenir l'effet car j'imagine pas que Mester Vincent utilise le photoshop ds ces anims !!!

1-créer un clip avec le nom de liason est “dot” 2-dessinez un carré 3-copier coller le code suivant

#include "easing_equations.as"
space = 0;
nb = 5;
n = 0;
fric = .2;
var tab_grp = [];
scale_h = scale_w=100;
_root.createEmptyMovieClip("conteneur", 1);
conteneur._x = 50;
conteneur._y = 50;
for (var i = 0; i<2*nb-1; i++) {
	tab_grp[i] = new Array();
}
MovieClip.prototype.push_id = function() {
	var id = this.id;
	tab_grp[id].push(this);
};
for (var i = 0; i<nb; i++) {
	for (var j = 0; j<nb; j++) {
		var mc:MovieClip = conteneur.attachMovie("dot", "dot"+n, n);
		mc.id = i+j;
		mc.push_id();
		mc.ordr = n;
		mc._xscale = scale_w;
		mc._yscale = scale_h;
		mc._x = (mc._width+space)*i;
		mc._y = (mc._height+space)*j;
		mc._xscale = 0;
		mc._yscale = 0;
		n++;
	}
}
MovieClip.prototype.move_dot = function(tab:Array) {
	count = 0;
	this.onEnterFrame = function() {
		count += 0.2;
		tab[0][0]._xscale = easeOutExpo(tab[0][0]._xscale, 1, scale_w, 150);
		tab[0][0]._yscale = easeOutExpo(tab[0][0]._yscale, 1, scale_h, 150);
		for (var i = 1; i<tab.length; i++) {
			for (var j in tab[i]) {
				tab[i][j]._xscale = easeOutExpo(tab[i][j]._xscale, 1, tab[i-1][0]._xscale, 150);
				tab[i][j]._yscale = easeOutExpo(tab[i][j]._yscale, 1, tab[i-1][0]._yscale, 150);
			}
		}
	};
};
conteneur.move_dot(tab_grp);