Forums Développement Multimédia

Les formations Mediabox
Les formations Mediabox

Scroller des MovieClip

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

Voici un visuel de ce que ce tutos mets en place :

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

Vous trouverez un framework plus poussé par ici !

Mise en place

Dans un contexte de scroll pour un MovieClip, il nous faut 3 choses :

  • un MovieClip avec du contenu
  • un masque (plus petit que le MovieClip dans le sens du scroll)
  • un scroller (fleche ou barre de scroll)

Notez que tout ceci est applicable aussi à un TextField puisque nous utiliserons les propriété _x/_y et _width/_heigth pour monter notre scroller. Le défaut d'utiliser uniquement un TextField est que nous ne pouvons pas placer n'importe quel contenu à l'intérieur. Il est préférable de faire un MovieClip ou l'on va mettre tous nos éléments (TextField/Images/MovieClip). Le but est d'avoir une classe qui puisse être adaptable à tout type de scroller.

Conception POO

Dans un contexte de programmation orienté objet, nous allons faire une classe qui sera dédiée aux calculs du scroll (VirtualScroller) et une autre qui s'occupera de fournir les fonctions pour les divers élément graphiques de notre scroller (GUIScroller).
Pourquoi séparer ces deux éléments ? Tout simplement car la classe VirtualScroller pourra ainsi être réutilisée pour d'autres fonctionnalité qui requiers des calculs de même sorte que le scroller ! Le fait de les séparer augemente ainsi la souplesse et la réutilisation de ces classes.

Classe VirtualScroller

Passons aux choses sérieuses ! Tout d'abord comment faire en sorte que cette classe soit complètement abstraite ? Elle ne doit avoir aucune relation avec le graphisme dans flash (donc pas de MovieClip, TextField ou autre). Nous devrons donc utiliser uniquement des nombres.

Données membres

Voici, à froid, les données membres (les variables) de la classe VirtualScroller :

class VirtualScroller
{
	//---------//
	//Variables//
	//---------//
	private var			_sizeContent:Number; //size content
	private var			_sizeScroller:Number; //size scroller
	private var			_sizeMask:Number; //size mask
	private var			_sizePlaceScroller:Number; //size place scroller	
	private var			_realSizeContent:Number; //calculated size
	private var			_realSizeScroller:Number; //calculated size
	private var			_currentPlaceContent:Number; //current place content
	private var			_currentPlaceScroller:Number; //current place scroller
	private var			_ratioContent:Number; //ratio of the content
 
	public var onScroll:Function;
}

Toutes les variables ne seront pas utilisées au cours de ce tutorial, mais elle le sont dans la classe VirtualScroller fournie dans les sources.

Constructeur

public function VirtualScroller(sizeContent:Number, sizeMask:Number, sizeScroller:Number, sizePlaceScroller:Number)
{
	if (sizeContent == null)
	{
		throw new Error(this+".<init> : sizeContent is ndefined");
	}
 
	if (sizeContent < 0)
	{
		throw new Error(this+".<init> : sizeContent is invalid "+sizeContent+")");
	}
 
	if (sizeMask == null)
	{
		throw new Error(this+".<init> : sizeMask is undefined");
	}
 
	if (sizeMask < 0)
	{
		throw new Error(this+".<init> : sizeMask is invalid "+sizeMask+")");
	}
 
	if (sizeScroller == null)
	{
		throw new Error(this+".<init> : sizeScroller is ndefined");
	}
 
	if (sizeScroller < 0)
	{
		throw new Error(this+".<init> : sizeScroller is invalid "+sizeScroller+")");
	}
 
	if (sizePlaceScroller == null || sizePlaceScroller < 0)
	{
		sizePlaceScroller = sizeMask;
	}
 
	//init
	_sizeScroller = sizeScroller;
	_sizeMask = sizeMask;
	_sizePlaceScroller = sizePlaceScroller;
	_sizeContent = sizeContent;
	_realSizeContent = sizeContent-sizeMask;
	_realSizeScroller = sizePlaceScroller-sizeScroller;
	_currentPlaceContent = 0;
	_currentPlaceScroller = 0;
	_ratioContent = 1;
	onScroll = null;
}

Je vous fait gâce des détails concernant la vérification des variables. Notez que le dernier argument (sizePlaceScroller) est optionnel et s'il est indéfini, nous prendrons la taille du masque.
Quoi d'intéressant la-dedans ? Notez juste l'initialisation des deux variables suivantes :

_realSizeContent = sizeContent-sizeMask;
_realSizeScroller = sizePlaceScroller-sizeScroller;

On soustrait la taille du masque (qui est sensée être plus petite que la taille du contenu) pour obtenir une taille sur laquelle on va pouvoir par la suite effectuer les calculs pour obtenir les valeurs de scroll (cf ci-dessous).

Méthodes publiques

isScrollable

public function isScrollable(Void):Boolean
{
	return (_sizeMask < _sizeContent && _sizeScroller < _sizePlaceScroller);
}

Cette méthode nous permet de savoir si les valeurs que nous avons passé en paramètres dans le constructeur font qu'il y ait un scroll ou pas. Je traduit :

  • Si la taille du masque est plus grande que le contenu, cela signifie que le contenu est entièrement affiché donc il n'y a pas de scroll.
  • Si la taille du scroller est plus grande que la place qu'il a à disposition pour bouger, cela veut dire qu'on ne peut pas bouger le scroller.

Cette méthode est à adapter suivant les divers types de scroller que vous voulez faire.

getMaxMoveContent & getMaxMoveScroller

public function getMaxMoveContent(Void):Number
{
	return _realSizeContent;
}
 
public function getMaxMoveScroller(Void):Number
{
	return _realSizeScroller;
}

rien à redire sur ces getter de base…

moveContentTo

public function moveContentTo(target:Number):Void
{
	//check
	if (target < 0 || target > getMaxMoveContent())
	{
		throw new Error(this+".moveContentTo : invalid target ("+target+")");
	}
 
	var ratio:Number = target / getMaxMoveContent();
	_currentPlaceContent = target;
	_currentPlaceScroller = getMaxMoveScroller()*ratio;
 
	//scroll event
	onScroll(_currentPlaceContent*_ratioContent, _currentPlaceScroller);
}

voila LA fonction principale de cette classe ! Elle calcul les positions du scroller et du contenu en fonction du mouvement (absolu) que vous lui donnez. Ci-dessous le détail :

var ratio:Number = target / getMaxMoveContent();

on calcul le ratio de mouvement. Cela correspont au pourcentage de la nouvelle position par rapport à la position maximale. Typiquement, si je peux scroller entre 0 et 500, si je passe 250 en paramètre, le ratio sera égal à 0.5 (ou 50%).

_currentPlaceContent = target;
_currentPlaceScroller = getMaxMoveScroller()*ratio;

nous stockons les places (qui sont virtuelles !) du contenu et du scroller. Comment trouves-t-on celle-ci ? il suffit d'appliquer notre ratio sur la place maximale qu'a le scroller pour bouger. Automatiquement la position du scroller et du contenu sont synchronisées !

onScroll(_currentPlaceContent*_ratioContent, _currentPlaceScroller);

on appelle l'évènement qui va nous permettre d'ajuster les positions ! Vous trouvez les explications concernant le _ratioContent dans la source.

appendContent

public function appendContent(append:Number):Void
{
	var np:Number = _currentPlaceContent+append;
 
	if (np > getMaxMoveContent())
	{
		np = getMaxMoveContent();
	}
	else if (np < 0)
	{
		np = 0;
	}
 
	moveContentTo(np);
}

cette méthode permet de modifier relativement la position courante. Par exemple si le contenu est à la position 27 / 100, si je passe 3 en paramètre, le contenu va se retrouver à la position 30 (au lieu de 3 pour la méthode moveContentTo).

Bilan

Vous trouvez d'autres méthodes dans la classe VirtualScroller fournie dans les sources qui ne sont guères difficile à comprendre si vous avez bien saisi le fonctionnement des méthodes ci-dessus.

Classe GUIScroller

Cette classe va gérer les fonctions à mettre sur les MovieClip qui vont gérer le scroller.

Données membres

class GUIScroller
{
	//---------//
	//Variables//
	//---------//
	private var		_virtualScroller:VirtualScroller; //the scroller
	private var		_content:MovieClip; //the content
	private var		_contentStart:Number; //start position of the content
	private var		_mask:MovieClip; //the mask
	private var		_arrowUP:MovieClip; //arrow up
	private var		_arrowDown:MovieClip; //arrow down
	private var		_scroller:MovieClip; //the scroller
	private var		_scrollerStart:Number; //start position of the scroller
}

Notez que l'on doit stocker la position du début du contenu (_contentStart) et celle du scroller (_scrollerStart).

Constructeur

public function GUIScroller(content:MovieClip, mask:MovieClip,	arrowUP:MovieClip, arrowDown:MovieClip,	scroller:MovieClip, sizePlaceScroller:Number)
{
	_content = content;
	_contentStart = content._y;
	_mask = mask;
	_arrowUP = arrowUP;
	_arrowDown = arrowDown;
	_scroller = scroller;
	_scrollerStart = _scroller._y;
	_virtualScroller = new VirtualScroller(content._height, mask._height, scroller._height, sizePlaceScroller);
 
	init();
}

Nous allons créer un scroll vertical pour cette classe (le scroller virtuel est initialisé avec les _height). C'est dans la méthode init() que les fonctions sont attribuées.

Méthode privée init()

private function init(Void):Void
{
	var me:GUIScroller = this;
 
	//on scroll
	_virtualScroller.setRatio(-1);
	_virtualScroller.onScroll = function(posContent:Number, posScroller:Number):Void
	{
		me._content._y = posContent+me._contentStart;
		me._scroller._y = posScroller+me._scrollerStart;
	}
 
	_arrowUP.onRelease = function(Void):Void
	{
		me._virtualScroller.appendContent(-4);
	}
 
	_arrowDown.onRelease = function(Void):Void
	{
		me._virtualScroller.appendContent(4);
	}
 
	_scroller.onPress = function(Void):Void
	{
		this.startDrag(false, this._x,
			       me._scrollerStart,
			       this._x,
		               me._virtualScroller.getMaxMoveScroller()+me._scrollerStart);
		this.onEnterFrame = function(Void):Void
		{
			me._virtualScroller.moveScrollerTo(this._y-me._scrollerStart);
		}
	}
 
	_scroller.onRelease = function(Void):Void
	{
		this.stopDrag();
		this.onEnterFrame = null;
	}
}

Intéressons nous à ceci :

_virtualScroller.setRatio(-1);
_virtualScroller.onScroll = function(posContent:Number, posScroller:Number):Void
{
	me._content._y = posContent+me._contentStart;
	me._scroller._y = posScroller+me._scrollerStart;
}

La classe VirtualScroller ne connais pas les positions de départ des clips et nous renvoie des nombre relatif en partant de 0 ! Il nous faut donc rajouter à ces positions les valeurs de départ du scroller et du contenu pour que les positions soient respectées !

_scroller.onPress = function(Void):Void
{
	this.startDrag(false, this._x,
	  	       me._scrollerStart,
		       this._x,
		       me._virtualScroller.getMaxMoveScroller()+me._scrollerStart);
	this.onEnterFrame = function(Void):Void
	{
		me._virtualScroller.moveScrollerTo(this._y-me._scrollerStart);
	}
}
 
_scroller.onRelease = function(Void):Void
{
	this.stopDrag();
	this.onEnterFrame = null;
}

Lorque l'on presse sur le scroller, on commence à le dragger. Il suffit de jouer avec les positions pour délimiter la place à scroller. En même temps on initialise un onEnterFrame qui va se charger de faire la mise à jour de la position du contenu (et du scroller aussi). Une vois de plus, la classe VirtualScroller ne prenant en compte que des positions partant de 0, nous devont soustraire la position de départ du scroller à sa position actuelle pour passer la bonne valeur au VirtualScroller.

Conclusion

Vous savez à présent comment fonctionne un scroller à la base. Vous avez dans les sources, les fichiers créés au cours de ce tutorial (et même plus :)) que vous pouvez sans autre utiliser.
Vous pouvez télécharger les sources ici.
Vous pouvez en discuter sur le forum !