Créer une Scrollbar en ActionScript 3.0
Un tutoriel dont vous êtes le héros.
Ce matin, le secteur B de la base est sur le point de passer à l’ActionScript 3.0. Votre équipe s’est démenée six mois durant pour effectuer ce changement. Mais un problème subsiste encore, à 20 minutes de la réouverture du secteur. En effet, il semblerait que les écrans de contrôle du bâtiment principal ne puissent toujours pas afficher toutes les données.
Votre première décision : créer une classe vide du nom de « KillerScrollbar » et étendant la classe « Sprite ». Cette classe contiendra tous les éléments utiles à une barre de scroll, ce qui la rendra importable dans n’importe quelle animation Flash, et donc sur tous les écrans de contrôle.
package {
import flash.display.Sprite;
public class KillerScrollbar extends Sprite
{
public function KillerScrollbar()
{
};
}
}
Vous décidez de définir immédiatement la manière dont vous allez générer des barres de scroll à partir de cette classe. Vous imaginez 4 paramètres : d’abord sa position sur les axes X et Y, puis son clip cible scrollable, et enfin une hauteur maximum correspondant à la hauteur de la barre de scroll. Dans le but de prévenir les erreurs liées à l’utilisation de certains évènements, vous décidez également que la barre de scroll sera générée par une fonction appelée « init() » après son ajout sur la scène à l’aide de « addChild() ».
import KillerScrollbar;
var myScrollbar :KillerScrollbar = new KillerScrollbar();
myScrollbar.x = 670;
myScrollbar.y = 10;
myScrollbar.target = myTargetClip;
myScrollbar.maxHeight = 500;
addChild(myScrollbar);
myScrollbar.init();
Un ajustement de la classe s’impose : Deux nouvelles variables « target » et « maxHeight » et une fonction « init() » ont fait leur apparition, il faut les déclarer.
package
{
import flash.display.Sprite;
public class KillerScrollbar extends Sprite
{
public var target:Sprite;
public var maxHeight:int;
public function KillerScrollbar()
{
};
public function init():void
{
};
}
}
La fonction « init() » doit créer les éléments graphiques de la barre de scroll : Deux rectangles dont l’un sera nommé « BACKGROUND » et qui constituera l’élément statique de la barre de scroll, l’autre sera nommé « HANDLER » et servira de bouton pour scroller le clip cible.
private var BACKGROUND:Sprite();
private var HANDLER:Sprite();
public function init():void
{
BACKGROUND = new Sprite();
BACKGROUND.graphics.beginFill( 0x999999, 1 );
BACKGROUND.graphics.drawRect( 0, 0, 20, 20 );
BACKGROUND.graphics.endFill();
addChild( BACKGROUND );
HANDLER = new Sprite();
HANDLER.graphics.beginFill( 0x444444, 1 );
HANDLER.graphics.drawRect( 0, 0, 20, 20 );
HANDLER.graphics.endFill();
addChild( HANDLER );
};
Vous êtes alors en présence de deux rectangles de même largeur et de même hauteur ; Et cela n’a rien d’une barre de scroll !
D’abord et dans le but de définir les dimensions de ces éléments, vous élaborez le tableau de correspondance suivant :
| A l’échelle du clip cible
| A l’échelle de la « Scrollbar »
|
BACKGROUND.height
| target.height
| maxHeight
|
HANDLER.height
| maxHeight
| ---
|

Facile alors pour vous de redimensionner les deux éléments à la bonne échelle :
BACKGROUND.height = maxHeight;
HANDLER.height = maxHeight * maxHeight / target.height;
Puis vous-vous apprêtez alors à déclarer la fonction « render() ». Mais avant toute chose, vous avez la conscience d’anticiper sur les évènements car cette fonction doit aussi permettre de positionner l’élément « HANDLER » et le clip cible « target » sur l’axe Y. Initialement, les positions sont les suivantes :
HANDLER.y
| 0
|
target.y
| this.y (position de la « Scrollbar »)
|
Celles-ci vont évoluer dans le temps et pour permettre cette évolution, vous décidez d’ajouter un paramètre « delta » à la fonction « render() ». Ce paramètre contiendra une valeur positive ou négative correspondant aux changements de position sur l’axe Y. Ainsi « +4 » correspondra à un scroll vers le bas et « -10 » à un scroll vers le haut; « 0 » n’opérant aucun changement. Vous élaborez également un moyen pour éviter que l’élément « HANDLER » ne sorte de la barre de scroll.
public function render( delta:int ):void
{
BACKGROUND.height = maxHeight;
HANDLER.height = maxHeight * maxHeight / target.height;
if( (HANDLER.y + delta) < 0 ) {
delta = -HANDLER.y;
}
if( (HANDLER.y + delta) > (maxHeight - HANDLER.height - HANDLER.y) ) {
delta = (maxHeight - HANDLER.height - HANDLER.y) - HANDLER.y;
}
HANDLER.y = HANDLER.y + delta;
target.y = this.y -((HANDLER.y * (target.height -maxHeight) / (maxHeight -HANDLER.height)));
};
La barre de scroll est prête pour jouer son rôle, encore faut-il lui dire quand elle doit le jouer.
Votre objectif à présent est de gérer le « Glisser déposer » de l’élément « HANDLER ». Vous savez que des fonctions comme « onReleaseOutside » n’ont pas survécu au passage à l’ActionScript 3.0. Il vous sera alors nécessaire d’user de votre imagination pour combler ces lacunes. Vous décidez alors de réaliser un petit schéma pour comprendre tous les évènements qui entrent en jeu.

Instant
| Description
| Évènements sur HANDLER
| Évènements sur stage
|
t0
| Le curseur s’approche au dessus du HANDLER
| MOUSE_MOVE
| MOUSE_MOVE
|
t1
| Le curseur démarre un « glisser déposer » en cliquant
| MOUSE_DOWN
| MOUSE_MOVE
|
t2
| Le curseur se déplace au dessus du HANDLER
| MOUSE_MOVE
| MOUSE_MOVE
|
t3
| Le curseur sort du HANDLER
| ROLL_OUT
| MOUSE_MOVE
|
t4
| Le curseur se déplace en dehors du HANDLER
| ?
| MOUSE_MOVE
|
t5
| Le curseur sort de la Scène
| ?
| MOUSE_LEAVE
|
t(n)
| Le curseur arrête le « glisser déposer » en décliquant
| ?
| MOUSE_UP
|
D’une, pour restituer un vrai « glisser déposer », vous remarquez qu’il est impossible de se focaliser uniquement sur l’élément « HANDLER » car à partir d’un certain moment le curseur n’est plus suivi ; Il faut au contraire suivre le curseur sur la globalité de la Scène, et même au delà quand le curseur quitte la Scène. Vous décidez donc d’appliquer les principaux évènements à la Scène « stage » plutôt qu’au « HANDLER », sauf pour l’événement initial « MOUSE_DOWN ».
De deux, vous remarquez que les positions du curseur aux instants « t(n) » et « t(n-1) » peuvent être utilisées pour calculer la distance parcourue entre deux instants (en faisant leur différence), cette distance pouvant être envoyée en paramètre à la fonction « render() » afin d’effectuer un scroll. Pour connaître la position du curseur à l’instant « t(n-1) », il vous suffit de l’avoir enregistré au préalable dans une variable. Vous décidez d’appeler cette variable « VAR_eStageY » car elle correspond à la propriété « stageY » de l’évènement en cours. Ainsi la distance parcourue sera la différence suivante : e.stageY MOINS VAR_eStageY ; Soit « position à t(n) » MOINS « position à t(n-1) ».
Vous décidez de déclarer ces événements de manière chronologique : Les événements de déplacement « MOUSE_MOVE » et de relâchement « MOUSE_UP » et « MOUSE_LEAVE » sont directement appelés par l’événement initial « MOUSE_DOWN ».

Soit :
public var VAR_eStageY:int;
public function EVT_mouseDown( e:MouseEvent ):void {
VAR_eStageY = e.stageY;
e.target.stage.addEventListener( MouseEvent.MOUSE_MOVE, EVT_mouseMove, false, 0, true );
e.target.stage.addEventListener( MouseEvent.MOUSE_UP, EVT_mouseUp, false, 0, true );
e.target.stage.addEventListener( Event.MOUSE_LEAVE, EVT_mouseLeave, false, 0, true );
};
public function EVT_mouseUp( e:MouseEvent ):void
{
e.target.stage.removeEventListener( MouseEvent.MOUSE_UP, EVT_mouseUp );
e.target.stage.removeEventListener( MouseEvent.MOUSE_MOVE, EVT_mouseMove );
e.target.stage.removeEventListener( Event.MOUSE_LEAVE, EVT_mouseLeave );
};
public function EVT_mouseLeave( e:Event ):void
{
e.target.stage.removeEventListener( MouseEvent.MOUSE_UP, EVT_mouseUp );
e.target.stage.removeEventListener( MouseEvent.MOUSE_MOVE, EVT_mouseMove );
e.target.stage.removeEventListener( Event.MOUSE_LEAVE, EVT_mouseLeave );
};
public function EVT_mouseMove( e:MouseEvent ):void
{
var eStageY = e.stageY;
render( eStageY - VAR_eStageY );
VAR_eStageY = eStageY;
};
Ainsi, une seule ligne de code suffit à l’élément « HANDLER » pour accepter le « glisser déposer », ligne que vous insérez à la fin de la fonction « init() » afin d’être opérationnel dès le début.
HANDLER.addEventListener( MouseEvent.MOUSE_DOWN, EVT_mouseDown );
Un dernier événement reste à prendre en compte : L’événement MOUSE_WHEEL, ou la mollette.
En deux trois mouvements, vous concoctez sa déclaration :
public function EVT_mouseWheel( e:MouseEvent ):void
{
render( e.delta * (-6) );
};
La propriété « delta » de l’événement correspond à la valeur du scroll (entier positif ou négatif). Vous décidez de multiplier cette valeur « delta » par « -6 » afin de ne pas paraître trop lent et surtout de ne pas déclencher un scroll dans le mauvais sens !
Enfin, vous insérez à nouveau une ligne de code à la fonction « init() » pour initialiser la mollette.
this.stage.addEventListener( MouseEvent.MOUSE_WHEEL, EVT_mouseWheel );
Et pour finir, vous songez à appeler la fonction « render() » au moins une fois au début dans le but de la dimensionner correctement. Vous ajoutez donc une dernière ligne de code à « init() » :
render(0);
Ce qui vous donne alors en intégralité :
package
{
import flash.display.Sprite;
import flash.events.Event;
import flash.events.MouseEvent;
public class KillerScrollbar extends Sprite
{
public var maxHeight:int;
public var target:Sprite;
private var VAR_eStageY:int;
private var BACKGROUND:Sprite;
private var HANDLER:Sprite;
public function KillerScrollbar ()
{
};
public function init():void
{
BACKGROUND = new Sprite();
BACKGROUND.graphics.beginFill( 0x999999, 1 );
BACKGROUND.graphics.drawRect( 0, 0, 20, 20 );
BACKGROUND.graphics.endFill();
addChild( BACKGROUND );
HANDLER = new Sprite();
HANDLER.graphics.beginFill( 0x444444, 1 );
HANDLER.graphics.drawRect( 0, 0, 20, 20 );
HANDLER.graphics.endFill();
addChild( HANDLER );
BACKGROUND.height = maxHeight;
HANDLER.height = maxHeight * maxHeight / target.height;
HANDLER.addEventListener( MouseEvent.MOUSE_DOWN, EVT_mouseDown );
this.stage.addEventListener( MouseEvent.MOUSE_WHEEL, EVT_mouseWheel );
render(0);
};
public function EVT_mouseDown( e:MouseEvent ):void {
VAR_eStageY = e.stageY;
e.target.stage.addEventListener( MouseEvent.MOUSE_MOVE, EVT_mouseMove, false, 0, true );
e.target.stage.addEventListener( MouseEvent.MOUSE_UP, EVT_mouseUp, false, 0, true );
e.target.stage.addEventListener( Event.MOUSE_LEAVE, EVT_mouseLeave, false, 0, true );
};
public function EVT_mouseUp( e:MouseEvent ):void
{
e.target.stage.removeEventListener( MouseEvent.MOUSE_UP, EVT_mouseUp );
e.target.stage.removeEventListener( MouseEvent.MOUSE_MOVE, EVT_mouseMove );
e.target.stage.removeEventListener( Event.MOUSE_LEAVE, EVT_mouseLeave );
};
public function EVT_mouseLeave( e:Event ):void
{
e.target.stage.removeEventListener( MouseEvent.MOUSE_UP, EVT_mouseUp );
e.target.stage.removeEventListener( MouseEvent.MOUSE_MOVE, EVT_mouseMove );
e.target.stage.removeEventListener( Event.MOUSE_LEAVE, EVT_mouseLeave );
};
public function EVT_mouseMove( e:MouseEvent ):void
{
var eStageY = e.stageY;
render( eStageY - VAR_eStageY );
VAR_eStageY = eStageY;
};
public function EVT_mouseWheel( e:MouseEvent ):void
{
render( e.delta * (-6) );
};
public function render( delta:int ):void
{
BACKGROUND.height = maxHeight;
HANDLER.height = maxHeight * maxHeight / target.height;
if( (HANDLER.y + delta) < 0 )
{
delta = -HANDLER.y;
}
if( (HANDLER.y + delta) > (maxHeight - HANDLER.height) )
{
delta = (maxHeight - HANDLER.height) - HANDLER.y;
}
HANDLER.y = HANDLER.y + delta;
target.y = this.y - ( ( HANDLER.y * ( target.height - maxHeight ) / ( maxHeight - HANDLER.height ) ) );
};
}
}
Et sur la « Timeline » ou dans une classe de document (ou plutôt sur les écrans de contrôle) :
« myTargetClip » étant le clip cible à scroller.
import KillerScrollbar;
var myScrollbar :KillerScrollbar = new KillerScrollbar();
myScrollbar.x = 670;
myScrollbar.y = 10;
myScrollbar.target = myTargetClip;
myScrollbar.maxHeight = 500;
addChild(myScrollbar);
myScrollbar.init();