Forums Développement Multimédia

Les formations Mediabox
Les formations Mediabox

Créez votre propre éditeur de dessin en AS3

Compatible ActionScript 3. Cliquer pour en savoir plus sur les compatibilités.Compatible Flash CS3. Cliquer pour en savoir plus sur les compatibilités.Par Stefbuet

Bonjour à tous. Vous avez sûrement déjà vu sur le net des éditeurs de dessins en Flash. Aujourd'hui, nous allons apprendre à en créer un en ActionScript 3 grâce à l'IDE Flash CS3. Pour suivre ce tutoriel, vous devez connaître les bases de Flash, de l'AS3 et de la POO (Organisation des packages, héritage, fonctions de l'IDE Flash). Alors, prêt pour créer le futur Photoshop ? C'est parti !

Notions abordées dans ce cours

  • Document class
  • Graphics API
  • Filters Class
  • BitmapData/Bitmap
  • Événements
  • Timers
  • Tweens class

À la fin de ce tutoriel, vous obtiendrez un éditeur de dessin vectoriel comme celui ci-dessous. À la fin du cours, les sources seront disponibles sous forme d'un fichier ZIP pour ceux qui n'auraient pas suivi jusqu'au bout ;)

Animation finale :

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

I. Préparation de l'animation

1) Le cahier des charges

Nous allons avant toute chose commencer par définir ce que nous allons faire, ce que nous souhaitons. Tout d'abord globalement, c'est un éditeur de dessin vectoriel que nous allons coder (étonnés? :) ). Nous allons lister les fonctions qui devront être présentes :

  • pouvoir dessiner librement
  • avoir une interface pas trop chargée
  • pouvoir choisir la taille du pinceau
  • pouvoir choisir la couleur de la peinture
  • pouvoir choisir la transparence de la peinture
  • avoir une application plutôt rapide

Maintenant que nous avons posé les objectifs, nous allons déterminer comment les atteindre.
Pour pouvoir sélectionner les options souhaitées (taille, couleur, transparence) nous allons créer un panneau outil situé sur la droite qui regroupera toutes ses options. On pourra régler chaque option simplement à la souris grâce à des sliders (des boutons à faire glisser). Pour que l'espace de travail soit libre, je propose d'intégrer un bouton au panneau outil qui permettrait de faire quitter/entrer du plan de travail le panneau des outils. On pourra utiliser des Tweens pour cela. Pour finir au niveau de l'optimisation, elle se fera lorsque toutes les autres fonctions marcheront. Cette optimisation consiste à réduire les temps de calcul, et nous verrons à la fin quelles parties du code peuvent êtres optimisées.

2) Régler quelques paramètres

Avant de commencer à proprement parler les graphismes ou le code de notre éditeur, nous allons faire quelques réglages de base à notre animation. Tout d'abord, nous allons régler la taille et le framerate (images par seconde) de l'animation.

Créez un nouveau document Flash AS3, puis cliquez sur Modification/Document…
Un panneau apparaît, modifiez les dimensions à 640 par 480 pixels et la cadence à 48 images par secondes. Pourquoi 48 img/s ? Flash aime les cadences multiples de 12, ne me demandez pas pourquoi, mais c'est mieux ainsi. Ceci fait, enregistrer votre animation dans un répertoire sous le nom de Main.fla

Flash CS3 nous permet d'assimiler notre scène à une classe. Nous allons ici utiliser cette fonctionnalité de document class afin d'avoir un projet plus lisible. Pour résumer cette fonctionnalité de Flash CS3, nous allons pouvoir créer une classe qui représentera notre scène, et au lieu de mettre notre code directement dans les frames de l'animation, nous utiliserons notre classe. Dans l'onglet propriété (Fenêtre/propriété si vous ne l'avez pas), indiquez en guise de classe de Document la class “Main” (sans les guillemets).

Maintenant il ne vous reste plus qu'à coder la structure de base de cette classe et nous aurons terminé la préparation :). Créez donc dans le même répertoire que votre fichier Main.fla un fichier nommé Main.as qui contiendra votre class Main (Document Class). Vous êtes censé connaître les bases de l'AS3 et cette classe ne devrait pas poser de problème. Nous allons la faire hériter de Sprite pour gérer les événements et contenir d'autres objets, sans pour autant avoir le scénario gênant du MovieClip.

///////////////////////////////
package {
 
	import flash.display.Sprite;
 
	public class Main extends Sprite {
 
		public function Main() {
 
		}//fin fonction Main
 
	}//fin class Main
 
}//fin package
///////////////////////////////

II. Les bases de l'éditeur

1) Créer un contexte visuel

Avant de commencer toute sorte de programmation qu'il soit, nous allons créer l'ensemble des graphismes nécessaires à notre animation. Cela comporte les interfaces (fonds, titres, panneaux…), les boutons (ouvrir/fermer panneau d'outil…) et les composants un peu plus complexes (les sliders de réglages dans les outils).

Commençons par le commencement. Vous n'êtes pas obligé de faire comme moi, mais il vous faudra quelque chose qui se rapproche au niveau de la structure.

Nous allons créer une sorte de bannière en haut de l'animation avec un titre, ici “Éditeur de dessin vectoriel AS3, dessinez vos rêves les plus fous!”:). Rien de trop compliqué pour le moment. Dans le même style, vous allez créer sur la majeure partie de la gauche, un rectangle blanc, qui représentera notre feuille de dessin. Convertissez ce rectangle en Clip (clique droit/Convertir en symbole/Clip) et donner lui “feuille” comme nom d'occurrence. Pour finir, créez un dernier rectangle qui prendra la partie droite restante de l'animation et convertissez-le en Clip avec “panneau” comme nom d'occurrence, ce sera notre panneau d'outils !

Certain de vous n'auront peut être pas la patience de faire ces graphismes et avant d'attaque la deuxième phase de l'interface graphique, je vous propose de télécharger le fichier Flash tout près, tout chaud des graphismes de l'image ci-dessus.

Telecharger le fichier ZIP du Fla contenant les graphismes

Très bien, maintenant nous allons attaquer la partie un peu plus “ardue” de cette création graphique. Dans le clip du panneau d'outils, nous allons créer les différents textes et sliders permettant de faire les réglages, aller on y va :) :

Nous allons commencer par mettre en place les titres des différents outils, repartis régulièrement de haut en bas de notre panneau, les outils taille, couleur et transparence. Pour cela, utilisons l'outil texte. Profitez-en pour créer un bouton qui permettra d'ouvrir ou fermer votre panneau d'outil comme nous en avons parlé dans le cahier des charges. Personnellement j'ai mis ce bouton de façon vertical à gauche, mais rien ne vous empêche de le placer ailleurs :). Donner “bougerPanneau_bnt” comme nom d'occurrence à votre bouton. Une petite astuce ici, on peut mettre à la fin d'un nom d'occurrence de bouton le préfixe “_bnt” pour que lorsque nous éditerons notre code, Flash nous propose toutes les propriétés et méthodes d'un objet de type bouton. Cette astuce marche avec les clips avec “_mc” (MovieClip) ou les textes avec le préfixe “_txt” et beaucoup d'autres objets (plus d'une trentaine).

Maintenant nous allons créer un slider. Un slider est une sorte de composant ou l'utilisateur peut déplacer une glissière de gauche à droite, ce qui fera varier une valeur en conséquence.
Commencez par tracer une ligne plutôt épaisse (environ 3px d'épaisseur) et qui tient dans la largeur du panneau d'outils. Convertissez cette ligne en clip (clique droit/Convertir en symbole/Clip). Entrez dans ce clip (double clique dessus) et sur un nouveau calque placé en première position (vous pouvez modifier la position en cliquant/glissant les calques) créez un nouveau bouton d'une forme rectangulaire arrondie, c'est ce bouton que l'utilisateur déplacera. Donnez-lui comme nom d'occurrence “slider_bnt”.

Maintenant, nous devrions avoir votre clip slider dans la bibliothèque (Fenêtre/Bibliothèque si vous ne la voyez pas). Placez le premier slider que vous avez produit juste en dessous du titre taille, puis glissez/déposez de la bibliothèque sur la scène 4 autres sliders. Un en dessous de transparence, et trois en dessous de couleur (pour les trois composantes de la couleur : rouge, vert et bleu). Nous allons réduire un peu la taille des sliders pour la couleur. Sélectionnez chaque slider de la couleur, et dans la fenêtre transformer (Fenêtre/Transformer si vous ne l'avez pas) cochez la case “conserver les proportions” et indiquez 75% de la taille originale.
Donnez comme nom d'occurrence à vos sliders : “taille_slider”, “rouge_slider”, “vert_slider”, “bleu_slider”, “alpha_slider”, les noms sont assez explicites :).

Nous avons bientôt fini notre panneau d'outil et donc notre interface graphique :). Vous devez maintenant placer des textes dynamiques à côté (à droite) de chaque sliders, ils indiqueront les valeurs des réglages en fonction de la position des sliders. Créez vos textes dynamiques et mettez comme texte par défaut 0 suivis de l'unité du réglage, par exemple 0px (pixel) pour le réglage de taille. Donnez comme nom d'occurrence à vos textes les suivant : “taille_txt”, “rouge_txt”, “vert_txt”, “bleu_txt”, “transparence_txt”, je pense que les noms sont assez explicites encore une fois :).

Pour finir, créez un petit rectangle noir d'environ 100*15px juste en dessous des sliders pour la couleur, et convertissez-le en clip. Nous changerons la couleur de ce clip directement depuis le code pour lui appliquer la couleur des réglages de l'utilisateur. Donnez comme nom d'occurrence “couleur_mc” à ce clip.

Et voilà, l'interface graphique est terminée, maintenant nous allons passer à la partie programmation de cet éditeur de dessins :). Avant tout, pour ceux à qui cette partie graphismes n'a rien donnée, voici le fichier Fla contenant tous les éléments graphiques nécessaires, avec les noms d'occurrence, etc. Comme sa tout le monde en est au même point pour la suite :), aller c'est parti!

Telecharger le fichier ZIP contenant le fichier Fla tout prêt pour la suite

2) Monter la structure des fonctions

a) Déclaration des fonctions

Nous allons créer toutes les fonctions dont nous avons besoin dans cet éditeur. Il faudra une fonction qui s'exécute toute les x secondes afin de mettre à jour le dessin, une autre fonction qui se déclenche lors d'un clique sur la feuille ou un relâchement ce qui fera changer une variable booléenne qui nous indiquera si l'utilisateur dessine ou non. Il faudra aussi des fonctions qui se déclencheront lors de l'appui sur les sliders qui permettront de changer les valeurs des réglages, une fonction pour ouvrir/fermer le panneau d'outil, et peut être une petite dernière qui se chargera avec les données RGB des sliders de donner une couleur (en hexadécimal) au clip couleur_mc.

Très bien, créons ses fonctions en les laissant vides pour le moment. Attention, les fonctions pour les boutons reçoivent un événement de type MouseEvent et la fonction d'update du dessin un événement TimerEvent. N'oubliez pas avant d'écrire les fonctions et les variables d'importer les packages nécessaires.

Tout d'abord, voici les packages dont nous aurons besoin (code situé entre ouverture du package et ouverture class Main), leur utilité est détaillée dans les commentaires :

////////////////////
import flash.display.Sprite; //notre class en hérite
import flash.display.MovieClip; //pas mal de mc dans notre scene
import flash.utils.Timer; //timer pour actualiser le dessin
import flash.events.TimerEvent; //les event du timer qui declanche l'update
import flash.events.MouseEvent; //event pour le clique des boutons
import fl.motion.easing.Elastic; //fonction elactique pour ouvrir/fermer panneau outils :)
import fl.transitions.Tween; //tween qui animera notre panneau outils
import fl.transitions.TweenEvent; //event qui nous permettra de savoir la fin d'un tween
//////////////////

Maintenant voyons quelles variables nous pourrions avoir besoin ? Une pour savoir si l'utilisateur dessine ou non, 5 autres pour la taille du pinceau, la couleur (rouge, vert et bleu) et la transparence, un tween pour bouger notre panneau d'outil sur l'axe des abscisses, une pour savoir si le panneau d'affichage est ouvert ou fermé, une autre pour savoir si le panneau d'affichage est en cour de déplacement, et je crois que c'est tout ! Déclarons donc ces valeurs juste après l'ouverture de la class Main :

////////////////////
private var isDrawing:Boolean; //l'utilisateur dessine ?
private var size:Number; //taille du pinceau
private var redColor:Number; //composante rouge de la couleur
private var greenColor:Number; //composante vert de la couleur
private var blueColor:Number; //composante bleu de la couleur
private var alphaColor:Number; //transparence de la couleur
private var tween:Tween; //tween qui animera notre panneau d'outils
private var timer:Timer; //timer pour appelle la fonction update toute les x sec
private var toolBarOpened:Boolean; //true si ouvert, false sinon
private var toolBarMoving:Boolean; //true si bouge, false sinon
/////////////////

Maintenant, créons les fonctions juste après les déclarations des variables. Laissez ces fonctions vides, et lorsqu'elles sont censées renvoyer une valeur, renvoyez une valeur bidon, par exemple pour un Array faites : return(new Array()); pour passer à la compilation.

////////////////////
//met à jour le dessin, mais aussi toutes les autres informations tel que les reglages
private function updateDrawing(e:TimerEvent):void {
 
}
 
//ouvre ou ferme via un tween le panneau d'affichage
private function moveToolsBar(e:MouseEvent):void {
 
}
 
//analyse de quel slider vien l'evenement et change les valeurs necessaires
private function setSliderValue(e:MouseEvent):void {
 
}
 
//passe de true à false ou inverce la variable isDrawing en fonction de l'evenement
private function changeDrawingState(e:MouseEvent):void {
 
}
 
//changer la couleur clip couleur_mc
private function changeCoulor():void {
 
}
/////////////////////

Nous avons toutes nos fonctions. Avouez, il n'y en a pas beaucoup. Pour la plupart vous pouvez deviner à quoi elles vont ressembler en jetant un coup d'œil dans les commentaires. Mais je me dois de vous expliquer quelques détails : il n'y à qu'une fonction pour analyser la valeur de tous les sliders, en effet via la propriété target des événements MouseEvent nous pourrons retrouver quel slider à appelé la fonction. Dans le même genre il n'y à qu'une fonction pour changer l'état de la variable isDrawing, nous analyserons quel type d'événement il s'agit et agirons en conséquence. Voilà, maintenant il est temps de passer à l'implémentation du système événementielle de l'éditeur.

b) Gestion des événements

Nous allons rajouter des écouteurs à tous les éléments de la scène qui ont besoin de déclencher des événements, soit les boutons des sliders (ils déclenchent le même événement, mais ont tous besoin d'un écouteur chacun), la feuille ou l'on dessinera, le bouton ouvrir/fermer du panneau d'affichage, un Timer pour la fonction d'update, et c'est tout pour le moment (c'est pas mal déjà non ? :))

Nous allons donc mettre nos écouteurs et notre Timer dans le constructeur de la class Main (fonction Main). Attention, les nous devons écouter les sliders lors d'un clique (MOUSE_DOWN), un relâchement (MOUSE_UP) et une sortie de surface (ROLL_OUT).

////////////////////
//constructeur de la class Main :
public function Main() {
 
	//le timer qui appellera la function UpdateDrawing
	timer=new Timer(10); //raffraichissement tout les 1/100 se segonde
	timer.addEventListener(TimerEvent.TIMER, updateDrawing);
	timer.start();
 
	//gestion du clique sur bouton ouvrir/ferme panneau d'outils
	panneau.bougerPanneau_bnt.addEventListener(MouseEvent.CLICK, moveToolsBar);
 
	//gestion du clique, du relachement, et de la sortie de surface (rollOut) pour les sliders
	panneau.taille_slider.slider_bnt.addEventListener(MouseEvent.MOUSE_DOWN, setSliderValue);
	panneau.rouge_slider.slider_bnt.addEventListener(MouseEvent.MOUSE_DOWN, setSliderValue);
	panneau.vert_slider.slider_bnt.addEventListener(MouseEvent.MOUSE_DOWN, setSliderValue);
	panneau.bleu_slider.slider_bnt.addEventListener(MouseEvent.MOUSE_DOWN, setSliderValue);
	panneau.alpha_slider.slider_bnt.addEventListener(MouseEvent.MOUSE_DOWN, setSliderValue);
	panneau.taille_slider.slider_bnt.addEventListener(MouseEvent.MOUSE_UP, setSliderValue);
	panneau.rouge_slider.slider_bnt.addEventListener(MouseEvent.MOUSE_UP, setSliderValue);
	panneau.vert_slider.slider_bnt.addEventListener(MouseEvent.MOUSE_UP, setSliderValue);
	panneau.bleu_slider.slider_bnt.addEventListener(MouseEvent.MOUSE_UP, setSliderValue);
	panneau.alpha_slider.slider_bnt.addEventListener(MouseEvent.MOUSE_UP, setSliderValue);
	panneau.taille_slider.slider_bnt.addEventListener(MouseEvent.ROLL_OUT, setSliderValue);
	panneau.rouge_slider.slider_bnt.addEventListener(MouseEvent.ROLL_OUT, setSliderValue);
	panneau.vert_slider.slider_bnt.addEventListener(MouseEvent.ROLL_OUT, setSliderValue);
	panneau.bleu_slider.slider_bnt.addEventListener(MouseEvent.ROLL_OUT, setSliderValue);
	panneau.alpha_slider.slider_bnt.addEventListener(MouseEvent.ROLL_OUT, setSliderValue);
 
}
/////////////////

3) Implémenter le système de l'éditeur

Passons donc à quelque chose de plus sérieux, de plus costaud! Nous allons coder toutes les fonctions de notre éditeur, rien que ça! :-) Nous laissons la partie dessin pour la suite, ici nous allons en quelque sorte implémenter les “petites” fonctions.

Tout d'abord, nous allons commencer pas l'ouverture fermeture du panneau d'affichage. On utilisera les tweens. Dans la fonction moveToolsBar nous allons créer une tween en fonction de l'état du panneau (ouvert ou fermé). D'un autre côté, dans la fonction updateDrawing, nous allons centrer la feuille de dessin. Pour plus d'ergonomie, elle va se recentrer en fonction de la position du panneau d'outils. Avant toute chose on initialise les variables, dont nous allons nous servir, dans le constructeur, c'est-à-dire toolBarOpened et toolBarMoving. Il faudra aussi rajouter une fonction, tweenFinished, qui se déclenchera à la fin d'un Tween pour mettre toolBarMoving à false. Aller on code tout sa :) :

Note: dans la fonction update, je bouge aussi le clip nommé “board”, c'est un cadre bleu un petit peu plus grand que la feuille pour décorer l'éditeur, il faut aussi le faire bouger. Si vous avez pris les sources plus haut, convertissez le cadre bleu autour de la feuille en clip et donnez-lui comme nom d'occurrence “board”.

////////////////////
//dans le constructeur, on initialise la variable toolBarOpened
public function Main() {
 
	toolBarOpened=true; //au debut le panneua d'affichage est ouvert
	toolBarMoving=false; //fixe au debut
	//le reste du code de la fonction...
 
}
 
//la function moveToolBar permet d'ouvrir/fermer le panneau d'affichage
//avec un style bien simpa grâce à la fonction Elactique
private function moveToolsBar(e:MouseEvent):void {
 
	if(!toolBarMoving) { //si la panneau n'est pas deja en mouvement :
		toolBarMoving=true;
		if(toolBarOpened) { //si le panneau est ouvert on le ferme
			//on le ferme en 2s avec la fonction elastique à la fin du mouvement sur axe absices
			tween=new Tween(panneau, "x", Elastic.easeOut, panneau.x, stage.stageWidth-panneau.bougerPanneau_bnt.width, 2, true);
			//on rajoute un ecouteur
			tween.addEventListener(TweenEvent.MOTION_FINISH, tweenFinished);
			toolBarOpened=false;//on indique que le panneau sera ouvert
		}
		else { //sinon on ouvre le panneau
			//on l'ouvre en 2s avec la fonction elastique à la fin du mouvement sur les absices
			tween=new Tween(panneau, "x", Elastic.easeOut, panneau.x, stage.stageWidth-panneau.width-10, 2, true);
			//on rajoute un ecouteur
			tween.addEventListener(TweenEvent.MOTION_FINISH, tweenFinished);
			toolBarOpened=true;//on indique que le panneau sera fermé
		}			
	}
 
}
 
//dans la function d'update on place la feuille de dessin en fonction du panneau d'affichage
//en faite on la centre sur l'espace disponible :
private function updateDrawing(e:TimerEvent):void {
 
	//mise à jour de la position de la feuille par rapport au panneau d'outil :
	feuille.x=panneau.x/2-feuille.width/2;
	board.x=panneau.x/2-board.width/2;
 
 
}
//////////////////

Nous allons attaquer une des parties les plus difficiles de ce tutoriel, bien que ce ne soit pas la fonction principale de l'éditeur ^^. Nous aurions pu à la place des sliders mettre de simples champs de texte de saisie, mais cela aurait été bien moins joli qu'un beau slider :).
Que va donc faire la fonction setSliderValue ?
Tout d'abord si l'événement perçut est un clique, mettre un variable associé au slider cliqué sur true, lors d'un relâchement, une sortie de zone, alors la variable sera mise sur false. Dans la fonction d'update, en fonction de cette variable nous changerons les coordonnées du bouton du slider. Cette fonction doit aussi transmettre la position x et y à des variables lors du clique pour que nous déplacions correctement notre bouton de slider dans la fonction update (le bouton doit suivre le curseur de la sourie). Le rôle de la fonction est donc simplement de changer les valeurs de variables : cliqué ? Position curseur x.

Mais comment organiser ces variables puisqu'il y a 5 sliders ? Nous allons faire 1 tableau (Array) de 5 cases qui contiendront 5 objets dynamiques avec nos variables pour chaque slider : tableau[0] = objet pour le slider 1. Une variable lastSlider:Number contiendra le numéro du dernier slider sélectionné dans le tableau slidersData car on n'y a pas accès lors d'un événement ROLL_OUT via la propriété event.target contrairement aux autres events. On le récupère (numéro du dernier slider) lors d'un clique sur le slider en question.

Ok, allons-y, regardez bien les commentaires dans le code pour vous aider :)

////////////////////
//on ajoute lors de l'initialisation des variable un tableau.
//rappel : initialisation des variables = après ouverture class Main
private var slidersData:Array;
private var lastSlider:Number; //numero de la case du dernier slider utilisé
 
//dans le constructeur (fonction Main) on va initialiser se tableau de 5 cases :
public function Main() {
 
	lastSlider=0; //pour ne pas avoir d'erreur au debut si clique et relache sans rollOut
	slidersData=new Array();
 
	for(var i:Number=0;i<5;i++) {
		slidersData[i]=new Object(); //un Object est  dynamique, on peut lui rajouter ce qu'on veux
		slidersData[i].isCliked=false;
		slidersData[i].cursorX=0;
	}
 
	//le reste du code de la fonction Main...
 
}
 
//maintenant nous allons nous interesser à la fonction update :
 
//on parcours le tableau slidersData, et lorsque l'on trouve un isClicked=true on le traite
//en fonction du numero de la case du tableau on retrouve le nom du slider
//on bouge le bouton du slider sur le X du curseur (le y reste toujour le même)
//si la nouvelle position du bouton est trop loin, on la ramene à l'extremitée.
//pour finir on break le for car un seul slider peut etre modifier à la fois ;)
 
public function updateDrawing(e:TimerEvent):void {
 
	recherche: for(var i:Number=0; i<5; i++) {
 
		if(slidersData[i].isClicked) {//si on trouve un slider en cours d'utilisation :
 
			//on recupere le nom du slider en fonction du numero de la case du tableau (i):
			var sliderName:String;
			switch(i) {
				case 0:
				sliderName="taille_slider";
				break;
				case 1:
				sliderName="rouge_slider";
				break;
				case 2:
				sliderName="vert_slider";
				break;
				case 3:
				sliderName="bleu_slider";
				break;
				case 4:
				sliderName="alpha_slider";
				break;
			}//fin du switch
 
			//on met une variable temporaire à la position du curseur + decalage
			var newX:Number=panneau[sliderName].mouseX+slidersData[i].cursorX;
 
			//si cette position est trop grande ou trop petite ou la mettre au valeur corects
			if(newX>panneau[sliderName].width) {
				newX=panneau[sliderName].width;
			}
			if(newX<0) {
				newX=0;
			}
 
			//finalement on a la nouvelle valeur, et on break le for (label recherche)
			panneau[sliderName].slider_bnt.x=newX;
			break recherche;
 
		}//fin du if
 
	}//fin du for
 
	//le reste du code de la fonction updateDrawing...
 
}
 
//maintenant la function setSliderValue, va simplement changer les valeurs isClicked et
//cursorX lors d'un evenement de sourie sur les sliders.
 
private function setSliderValue(e:MouseEvent):void {
 
	//on definit quel case du tableau slidersData il va faloir modifier en identifiant
	//le bouton qui a appellé l'evenement à partie de la propriété target :
 
	var num:Number=-1; //au debut -1, si est resté à -1 après switch alors on ne fait rien
 
	if(e.type!=MouseEvent.ROLL_OUT) {//le rollout ne renvoit pas le bouton slider en target
 
		switch(e.target.parent.name) { //e.target=bouton slider, son parent est le slider
			case "taille_slider":      //la propriété name est le nom d'occurence
			num=0;
			break;
			case "rouge_slider":
			num=1;
			break;
			case "vert_slider":
			num=2;
			break;
			case "bleu_slider":
			num=3;
			break;
			case "alpha_slider":
			num=4;
			break;
		}//fin du switch
		lastSlider=num;//dernier slider utilisé est gardé en mémoire
	}
	else {
		num=lastSlider;//cas d'un rollOut : on sort du dernier slider utilisé
	}
 
	//maintenant en fonction de l'evenement on modifit ce qu'il faut dans slidersData[num] :
	switch(e.type) {
		case MouseEvent.MOUSE_DOWN: //si c'est un clique
		slidersData[num].isClicked=true;
		slidersData[num].cursorX=e.target.mouseX;
		break;
		case MouseEvent.MOUSE_UP://mouseUp et rollOut font la même chose :
		case MouseEvent.ROLL_OUT://on lache le bouton slider
			if(num>-1) {
				slidersData[num].isClicked=false;
			}
		break;
	}//fin du switch
 
}//fin function setSliderValue;
/////////////////////

Vous noterez que la gestion du RollOut nous oblige à déplacer les sliders boutons lentement sinon la sourie sort de la surface et le mouvement est arrêté. Nous verrons dans la partie des optimisation et améliorations possibles ce qui pourrait être fait pour éviter ça ;)

Vous avez suivi ? Tant mieux ! :)
Pour vous reposer un peu nous allons maintenant coder une des fonctions les plus simples, celle qui va faire changer de couleur le clip couleur_mc dans le panneau d'outils. Cette fonction est appelée par la fonction updateDrawing pour un affichage temps réel.

Les couleurs dans Flash fonctionnent en hexadécimal. Il va donc nous falloir transformer les valeurs RGB en hexadécimal pour flash. En gros, on rajoute la valeur du rouge, puis celle du vert et enfin celle du bleu à la suite sous forme de nombre hexadécimal. Pour cela, j'ai créé une petite class nommée colorUtils. Elle permet de retrouver la valeur hexadécimale depuis des valeurs RGB et vice versa. Téléchargez-la, et mettez-la dans le même répertoire que votre fichier Main.fla.

Telecharger le fichier ZIP de la class AS3 ColorUtils

Maintenant vous devez importer la class dans votre class Main avec import ColorUtils;
C'est bon, nous pouvons coder la fonction changeColor() ! Les fonctions de la class ColorUtils sont statiques, pas besoin de créer d'objets de type ColorUtils pour les utiliser. Pour modifier la couleur dynamiquement du clip couleur_mc, il nous faudra aussi la class ColorTransform. Importez-la donc avec import flash.geom.ColorTransform;.

////////////////////
//la fonction  qui va modifier la couleur de couleur_mc :
private function changeColor():void {
 
	//on recupere la valeur hexa depuis la class ColorUtils
	//on cast le resultat qui est un String vers un Number
	var hexaColor:Number=Number(ColorUtils.getHexaFromRGB(redColor, greenColor, blueColor));
 
	//on créé un nouveau objet de type ColorTransform pour colorer le clip couleur_mc
	var myColor=new ColorTransform();
 
	//on definit la couleur de ce objet colorTransform
	myColor.color=hexaColor;
 
	//on change la couleur de notre clip couleur_mc
	panneau.couleur_mc.transform.colorTransform=myColor;
 
}
 
//le constructeur de la class Main (fonction Main) :
 
public function Main() {
 
	//code de la fonction Main...
 
	changeColor(); //a appeler à la fin de la fonction, nous verrons pourquoi juste après ;)
 
}
/////////////////

Voilà, mais si vous testez, cela ne marchera pas. Pourquoi donc ? Et bien nous n'avons pas encore défini les composantes RGB de la couleur! Nous allons les calculer en fonction des sliders (encore eux :)) dans la fonction updateDrawing.

Sachant que lorsqu'un slider bouton est au bout du slider, la valeur est à 100%, et que tout à gauche, elle est a 0%, on peut facilement retrouver tous les réglages des sliders.
La formule à utiliser sera donc : pourcentage = position*100/80
Note : 80px = largeur d'un slider – largeur slider bouton.

On va profiter de ce moment pour mettre la valeur de chaque réglage dans les textes dynamiques prévus à cet effet juste après avoir défini les variables de réglage dans la class Main, c'est parti :) :

/////////////////////
//constructeur de la class Main (fonction Main) :
public function updateDrawing(e:TimerEvent) :void{
 
	//reste du code de la fonction update sauf l'instruction changeColor() à la fin...
 
	//recupération des valeurs des réglages :
	size=panneau.taille_slider.slider_bnt.x/80; // de la taille max soit 50 ?
	redColor=panneau.rouge_slider.slider_bnt.x/80; // de 255
	greenColor=panneau.vert_slider.slider_bnt.x/80; // de 255
	blueColor=panneau.bleu_slider.slider_bnt.x/80; // de 255
	alphaColor=panneau.alpha_slider.slider_bnt.x/80; // de 1
 
	size*=48; //la taille max sera de 50px
	size+=2; // la taille min est de 2px
 
	redColor*=255;
	greenColor*=255; //une composante s'etalone de 0 à 255 (=FF en hexa)
	blueColor*=255;
 
	//changement des textes dynamiques :
	panneau.taille_txt.text=String(Math.round(size))+"px";
	panneau.rouge_txt.text="R : "+String(Math.round(redColor));
	panneau.vert_txt.text="G : "+String(Math.round(greenColor));
	panneau.bleu_txt.text="B : "+String(Math.round(blueColor));
	panneau.transparence_txt.text=String(Math.round(alphaColor*100))+"%";
 
	//instruction que vous devriez deja avoir mis, a la fin du constructeur :
	changeColor();
 
}//fin fonction update
/////////////////

Au besoin, vous pouvez agrandir un peu vos textes dynamiques en largeur si tout le texte ne peut être affiché dans ceux-ci. Vous pouvez aussi changer votre taille max et min de pinceau.
Vous n'allez peut-être pas le croire, mais nous avons terminé cette grande partie :).

III. Le fonctionnement de l'éditeur

1) Implémenter la fonction de dessin

Nous allons donc maintenant programmer la partie dessin de cet éditeur. Quoi?! Seulement maintenant ? Eh oui, la création des fonctions “basiques” de l'éditeur a prit un peu de temps, mais sachez que c'est avec une bonne organisation que l'on peut créer plus rapidement et simplement de grands projets. Ainsi, cet éditeur de dessin, plus tard, pourra être repris et amélioré sans avoir à tout chambouler dans l'organisation du code ;)

Pour revenir à nos moutons, le principe est simple. On définit une variable isDrawing sur true ou false selon les actions de l'utilisateur. Lors d'un clique sur la feuille, ou un relâchement, lorsque le curseur quitte la surface de la feuille, on va modifier cette variable grâce à la fonction changeDrawingState. Ensuite il suffira de relier via l'API Graphics du MovieClip feuille la dernière position du curseur, avec la position actuelle, avec les paramètres de couleur, taille et opacité définis par l'utilisateur. Il nous faudra donc juste rajouter une seule variable au code, sinon nous utiliserons les fonctions et variables déjà présentes, la variable haveStarted:Boolean qui sera à false tant que l'on à pas commencé à traiter le dessin, et qui deviendra true des que l'on aura commencé, comme sa lorsque c'est false on fait un moveTo(x, y), sinon des lineTo(x, y). moveTo(x, y) déplace le curseur d'un objet de type Graphics en x, y tandis qu'un lineTo(x, y) fait une ligne depuis la dernière position du curseur à x, y. Commençons par gérer les événements :

////////////////////
//dans la déclaration des variables, on rajoute une variable :
private var haveStarted:Boolean; //as-t-on fait le premier moveTo ?
 
//constructeur de la class Main (fonction Main) :
public function Main() {
 
	//déclaration des écouteurs sur la feuille de dessin :
	feuille.addEventListener(MouseEvent.MOUSE_DOWN, changeDrawingState);
	feuille.addEventListener(MouseEvent.MOUSE_UP, changeDrawingState);
	feuille.addEventListener(MouseEvent.ROLL_OUT, changeDrawingState);
 
 
 
}//fin constructeur
 
//passe de true à false ou inverce la variable isDrawing en fonction de l'evenement
private function changeDrawingState(e:MouseEvent):void {
 
	//en fonction de l'evenement, on change la valeur de isDrawing :
	switch(e.type) {
		case MouseEvent.MOUSE_DOWN:
		isDrawing=true;
		haveStarted=false;
		break;
		case MouseEvent.MOUSE_UP:
		case MouseEvent.ROLL_OUT://mouseOut fait pareil que MouseUp
		isDrawing=false;
		break;
	}//fin du switch
 
}//fin de la fonction changeDrawingState
///////////////////

Maintenant il ne nous reste plus qu'à coder la partie qui affichera les lignes, dans la fonction updateDrawing. C'est maintenant que nous allons utiliser les lineTo et moveTo. Nous allons dessiner toutes les lignes dans un Sprite à part (pas directement sur le clip feuille). Nous dessinerons dans feuille.dessin. Il va donc falloir créer un Sprite dessin dans le clip feuille depuis le constructeur de la class Main. Si nous créons un nouveau Sprite, c'est pour voir ce que nous dessinons, en effet en dessinant directement sur le clip feuille, le dessin serait en dessous de la feuille de papier (pas très commode n'est ce pas ? ^^) :

////////////////////
//nous allons créer les lignes necessaires dans la function UpdateDrawing
//comme cette fonction est appellé par un timer toute les 1/100 de segonde
//on aura l'impression de dessiner des courbes, mais se sera une succetion de lignes :
 
//on rajoute notre nouveau Sprite dans la déclaration de variables :
var dessin:Sprite;
 
//on le créer dans le constructeur de la class Main (fonction Main) :
public function Main() {
 
	dessin=new Sprite();
	feuille.addChild(dessin);
 
	//le reste du code de la fonction...
 
}//fin fonction Main
 
 
private function updateDrawing(e:TimerEvent):void {
 
	//tout le code de la fonction...
 
	if(isDrawing) { //si l'utilisateur est en train de dessiner :
 
		if(!haveStarted) { //si c'est le premier traitement du trait qu'il fait :
			haveStarted=true;
			//on déplace le curseur graphique sur le curseur de la sourie
			dessin.graphics.moveTo(feuille.mouseX, feuille.mouseY);
			//on personnalise le style de la ligne
			dessin.lineStyle(size,Number(ColorUtils.getHexaFromRGB(redColor, greenColor, blueColor)),alphaColor,true);
		}
 
		else { //il suffit de tracer la une ligne vers le curseur de la sourie :
 
			dessin.lineTo(feuille.mouseX, feuille.mouseY);
 
		}
 
	}//fin if(isDrawing)
 
}//fin fonction updateDrawing
////////////////

Plutôt cool non ? Si vous ne voyez rien s'afficher lorsque vous dessinez, vérifiez que vous avez mis la transparence à 100% qui est par défaut a 0%. Nous allons rapidement mettre le slider bouton de la transparence à 100% par défaut, il suffit de le déplacer à l'initialisation de la class Main, dans le constructeur.

////////////////////
//constructeur de la class Main (fonction Main)
public function Main() {
 
	//on met le slider boutton par default à 100%
	panneau.alpha_slider.slider_bnt.x=80; //80 = abcisse max (largeur slider=88, largeur bouton=8)
 
	//le reste du code la la fonction Main...
 
}//fin fonction Main
////////////////

2) Optimiser la vitesse

Pour le moment, si vous dessinez, et que vous arrêtez votre sourie sans relâcher le bouton de la sourie, ou que vous dessinez lentement, vu la vitesse de rafraîchissement de la fonction updateDrawing (appelée tout les 1/100 de seconde), le même point de l'écran va être coloré plusieurs fois, cela est inutile et faire des lineTo aux mêmes coordonnées va ralentir votre PC. Nous allons donc créer un objet de type Object qui aura des propriétés x et y qui contiendront la dernière position du curseur. Si lors du traitement des lignes dans la fonction d'update, les coordonnées de la dernière position sont les même que la position actuelle du curseur, alors on ne va rien dessiner. On prendra un Object et non un Point (flash.geom.Point) pour la bonne et simple raison que nous avons besoin simplement des propriétés x et y et non de toutes les autres méthodes de la class Point.

////////////////////
//dans les déclaration de variables on rajoute notre Object :
private var lastPosition:Object;
 
//dans le constructeur on initialise cet Object :
public function Main() {
 
	lastPosition=new Object();
 
	//reste du code de la fonction...
 
}
 
//dans la fonction d'update on rajoute une condition, et on modifi à chaque tour
//les ancienne coordonnée par les actuelles pour le prochain appelle de la fonction :
 
private function updateDrawing(e:TimerEvent):void {
 
	//le debut du code de la fonction...
	//jusqu'à la gestion du dessin, on reprend ici le code précedent que l'on complete
	//via la condition avec lastPosition :
 
	//gestion du dessin :
	if(isDrawing) { //si l'utilisateur est en train de dessiner :
 
		if(!haveStarted) { //si c'est le premier traitement du trait qu'il fait :
			haveStarted=true;
			//on déplace le curseur graphique sur le curseur de la sourie
			dessin.graphics.moveTo(feuille.mouseX, feuille.mouseY);
			//on personnalise le style de la ligne
			dessin.graphics.lineStyle(size,Number(ColorUtils.getHexaFromRGB(redColor, greenColor, blueColor)),alphaColor,true);
		}
 
		else { //il suffit de tracer la une ligne vers le curseur de la sourie :
 
		//////////////////////////////////////
		////// nouveautée du code - debut - //
		//////////////////////////////////////
 
			//on vérifit si le point à dessiner est differant de l'ancienne position :
			if((dessin.mouseX!=lastPosition.x)&&(dessin.mouseY!=lastPosition.y)) {
 
				dessin.graphics.lineTo(dessin.mouseX, dessin.mouseY);
				//on change la dernière position pour l'actuelle :
				lastPosition.x=dessin.mouseX;
				lastPosition.y=dessin.mouseY;
 
			}//fon du if
 
		//////////////////////////////////////
		///// nouveautée du code - fin - /////
		//////////////////////////////////////
		}
 
	}//fin if(isDrawing)
}//fin function updateDrawing
////////////////

Et voilà, votre éditeur de dessin est terminé, et même un petit peu optimisé :-) . Mais ce serait dommage de s'arrêter en si bon chemin, il y a tant de choses que vous pourriez faire en plus pour cet éditeur. Nous allons voir dans la prochaine partie ce que vous pourriez améliorer dans cet éditeur. Ne faites pas cette tête, je vous promets que c'est possible ;)

IV. Améliorations possibles

1) Outils supplémentaires

Qui a dit qu'un éditeur de dessin devait simplement nous permettre de faire des traits ? Certes nous pouvons choisir la taille, la couleur et l'opacité des traits, mais cela ne permet pas de rivaliser avec notre ami Photoshop :). Voilà quelques idées que vous pourriez implémenter :

  • Permettre la création de formes simples (cercles, rectangles) Cela est facilité par rapport à l'AS2, car en AS3 vous avez des fonctions toutes prêtes pour réaliser cela : drawRect et drawCircle. Vous pouvez imaginer d'autres formes, des polygones réguliers par exemple…
  • Permettre de faire d'autres choses que de dessiner : créer du texte. Oui je vous assure, créer un outil texte ou l'on choisirait la police, taille des caractères et couleur du texte, pour cela utilisez la class TextField pour le texte et TextFormat pour sa mise en forme.
  • Outre les formes, permettez la réalisation d'actions simples : les lignes. Si vous le souhaitez, vous pouvez aussi proposer la création de courbes. Pour les lignes vous avez déjà la fonction lineTo, pour les courbes on utilise CurveTo.
  • Une optimisation qui sera présente dans les sources finales téléchargeables à la fin : au lieu d'arrêter de déplacer un slider bouton au rollOut, on l'arrête lors d'un événement MOUSE_UP sur le stage uniquement si il y a eu un ROLL_OUT avant. Ainsi, on peut plus facilement et rapidement bouger les sliders.
  • Utilisez les filtres de Flash pour faire de beaux effets. Présents dans le package flash.display.filters, vous pourrez proposer de rajouter du flou, des ombres portées, etc… bien sympa non ?
  • Une petite dernière amélioration graphique. Avez-vous remarqué que si vous prenez un gros pinceau (exemple : taille 50) et que vous dessinez près des bords, la peinture va sortir des bords, pouah c'est moche sa. Pour y remédier, utilisez un masque. Dans la version finale des sources téléchargeables, j'ai rajouté un masque ;) N'oubliez pas de prendre en compte le masque lorsque vous re-centrez la feuille via le code.

2) Sauvegarde des dessins

Pour pouvoir réutiliser vos dessins plus tard, rien de tel qu'un enregistrement, mais là, plusieurs questions peuvent se poser. La cause de ces questions : Comment allez-vous vous servir de vos dessins par la suite ? Nous allons voir trois façons différentes de les enregistrer et leur utilisation particulière.

a) Sous forme d'animation (vectorielle)

Cette méthode consiste à sauvegarder chaque action que l'utilisateur aura faite. C'est-à-dire que l'on stocke chaque appelle de la fonction moveTo, lineTo et lineStyle. On aura une sorte d'historique des actions de l'utilisateur.
Cela peut vous servir si vous souhaitez créer un lecteur de dessin vectoriel, il ira lire dans un fichier chaque commande que l'user à fait, puis les reproduira à l'écran, et progressivement! L'inconvénient de cette méthode est qu'elle est lourde, en effet pour un dessin d'environ 3000 lignes (environ 2 min de dessin) la taille des données pèse quelques méga-octets! Pour réduire la taille un maximum avant de mettre les données par exemple dans une base de données Mysql, on peut utiliser une notation courte : “a(x, y) b(x, y,) c(x, y)” avec a, b, et c des fonction au lieu de mettre de grands noms de fonction. Ensuite des librairies PHP existe pour compresser ces données :).

b) Sous forme d'image

Pour cela c'est très simple, il suffit de récupérer la valeur de couleur de chaque pixel du clip où l'on a dessiné et de les mettre dans un BitmapData. Il existe pour cela la fonction BitmapData.draw(IBitmapDrawable). On peut donc passer en paramètre un clip, sprite, shape… Ensuite il suffit soit directement de créer une image dans Flash et de la stocker dans un ByteArray puis de l'enregistrer, ou bien de faire passer directement les données à PHP qui se chargera via des librairies de créer et enregistrer cette image. Une image est fixe et pas animée, seul le résultat de votre dessin sera visible.
Cette méthode est utile lorsque l'on recherche une solution légère (faible poids des images) et plutôt pour enregistrer l'image sur son PC que pour la revoir dans un lecteur. (Exemple : Photophop express d'Adobe)

c) Sous forme de source AS3

La méthode la plus simple, dans une variable de type String vous stockez toutes les instructions que vous avez utilisées lors du dessin. Par exemple, vous faites :
lineTo(x,y);
maVariable+=”/nlineTo(”+x+”, ”+y+”);”;
Ce qui rajoutera une nouvelle ligne avec la commande dans votre variable. A la fin du dessin on sauvegarde le fichier en tant que “fichier.as”. La personne qui a ce fichier peut maintenant l'utiliser dans ses animations via un :
Include “fichier.as”
Le dessin se fera automatiquement. Vous pouvez aussi rajouter des effets sympathiques tels qu’une animation progressive du dessin avec la mise en place de Timers.

V. Conclusion

Et voilà, ce tutoriel touche à sa fin. J'espère qu'il vous aura été utile et que vous avez appris quelques petites choses. Je vous encourage vivement à faire, où essayer au moins, les améliorations citées plus haut, ou d'autre que vous pouvez imaginez bien sur. Je tien à souligner pour terminer, que nous avons ici réaliser un éditeur de dessin vectoriel, mais rien ne vous empêche de vous lancer dans un éditeur par pixel (Bitmap & BitmapData class), ainsi vous pourrez créer des brushs, et des filtres comme dans Photoshop! Ne sous estimez pas la documentation! Appuyez sur F1 avant de demander de l'aide à propos de ce tuto!

Téléchargements