Einen Container bei gedrückter Maustaste mit startDrag über die Bühne ziehen

In dem nachfolgenden swf-Beispiel kann man das Luftbild mit gedrückter Maustaste verschieben. Aber beim Erreichen des Bildrandes lässt sich das Bild nicht mehr weiter schieben.
 

 

In der Bibliothek liegt ein Movieclip, der als einziges Element das Luftbild der Altstadt von Linz enthält.
Eine Instanz des Movieclipcontainers liegt im ersten Frame des Mainmovies und hat den Instanznamen container_mc.

Das nachfolgende Skript wird im ersten Frame der Timeline des Mainmovies platziert (drag_movieclip.fla):
 

stop();
this.stage.addEventListener(MouseEvent.MOUSE_DOWN, containerZiehen);
function containerZiehen(e:MouseEvent):void {
container_mc.startDrag(false,
new Rectangle(stage.stageWidth-container_mc.width,stage.stageHeight-container_mc.height,
container_mc.width-stage.stageWidth,container_mc.height-stage.stageHeight));
}
this.stage.addEventListener(MouseEvent.MOUSE_UP, containerLoslassen);
function containerLoslassen(e:MouseEvent):void {
container_mc.stopDrag();
}


Schrittweise Erklärung des Skripts:
 

stop(); 

stop() hält den Abspielkopf an. Wenn nur das erste Frame verwendet wird, ist stop() nicht unbedingt notwendig.
 
 

this.stage.addEventListener(MouseEvent.MOUSE_DOWN, containerZiehen); 

Das Schlüsselwort this bezieht sich auf die Instanz der Dokumentklasse (Mainmovie). Könnte in diesem Fall auch weggelassen werden.
Über die Eigenschaft stage kann immer auf die Bühneninstanz zugegriffen werden.
Die Bühneninstanz abonniert das Mausereignis MOUSE_DOWN. Wenn das Ereignis eintritt, wird die Bühneninstanz (das Ereignisziel) davon verständigt und die dem Ereignis zugeordnerte EventListener-Funktion containerZiehen wird ausgeführt.
Näheres zum Ereigniskonzept siehe unter ActionScript3: Ereigniskonzept und Eventhandler - eine kurze Einführung
 
 

function containerZiehen(e:MouseEvent):void {
container_mc.startDrag(false,
new Rectangle(stage.stageWidth-container_mc.width,stage.stageHeight-container_mc.height,
container_mc.width-stage.stageWidth,container_mc.height-stage.stageHeight));
}

Die Defintion des EventListeners containerZiehen ist das Herzstück des Skripts. Dabei wird die Methode startDrag auf die Movieclipsinstanz container_mc angewandt.
Der Parameter false bewirkt, dass der ziehbare Movieclip an der Stelle beim Mauszeiger einrastet, an der man zum ersten Mal auf den Movieclip geklickt hat.

Wenn man nur den ersten Parameter von startDrag benutzt, kann der Moveiclip ohne Einschränkungen bewegt werden und die Ränder des Movieclips (des Luftbbildes) können in die Bühne hineingezogen und sichtbar werden, was aber nicht erwünscht ist.
Aus diesem Grund muss die Bewegung und Positionierung des Registrierungspunktes vom Movieclip auf ein Rechteck eingeschränkt werden, das sich wie folgt bestimmen lässt:

Zuerst definiert man die Extrempositionen, die der Movieclip einnehmen kann.

Daraus ergibt sich das orange Rechteck, in dem sich der Registrierungspunkt des Movieclips maximal bewegen kann. Für die Defintion des Rechtecks benötigt man die Koordinate des linken oberen Eckpunkts und die Breite sowie Höhe des Rechtecks.

Das orange Rechteck ist daher wie folgt definiert:
 

new Rectangle(stage.stageWidth-container_mc.width, stage.stageHeight-container_mc.height,
container_mc.width-stage.stageWidth, container_mc.height-stage.stageHeight)

Dieses mit new erzeugte Rechteck ist der zweite Parameter von startDrag.
 
 

this.stage.addEventListener(MouseEvent.MOUSE_UP, containerLoslassen);
function containerLoslassen(e:MouseEvent):void {
container_mc.stopDrag();
}

Die Bühneninstanz abonniert das Mausereignis MOUSE_UP. Beim Loslassen der Maustaste, wird die Bühneninstanz (das Ereignisziel) davon verständigt und die dem Ereignis zugeordnerte EventListener-Funktion containerLoslassen wird ausgeführt. Dabei wird mit der Methode stopDrag die Ziehverbindung zwischen Mauszeiger und Movieclip aufgehoben.
 

Alternative: Der Container und nicht die Bühne abonniert die Mausereignisse (drag_movieclip_alternativ.fla)

 

stop();
container_mc.addEventListener(MouseEvent.MOUSE_DOWN, containerZiehen);
function containerZiehen(e:MouseEvent):void {
e.target.startDrag(false,
new Rectangle(stage.stageWidth-e.target.width, stage.stageHeight-e.target.height,
e.target.width-stage.stageWidth, e.target.height-stage.stageHeight));
}
container_mc.addEventListener(MouseEvent.MOUSE_UP, containerLoslassen);
function containerLoslassen(e:MouseEvent):void {
e.target.stopDrag();
}

Da der Container das Ereignisziel ist, kann dieser in den Eventlistenern über e.target angesprochen werden.
 

Ändern des Mauszeigers in ein Handsymbol beim Ziehen (drag_movieclip_cursor.swf):

In der Bibliothek wird dazu ein Movieclip Handcursor mit dem Handsymbol abgelegt und "Export für ActionScript" muss aktiviert sein.
Im MOUSE_DOWN-Eventlistener containerZiehen wird dann der Mauszeiger ausgeblendet und das Handsymbol über einen ENTER_FRAME-Eventlistener an der Position des Mauszeigers eingefügt.
Beim MOUSE_UP-Eventlistener containerLoslassen wird der Mauszeiger wieder eingeblendet und das Handsymbol aus der Child-Liste der Bühne entfernt.

Details siehe unter ActionScript3: Cursor aus-/einblenden, ändern, animieren und Movieclips an die Maus binden
 

stop();
var handCursor:MovieClip = new Handcursor();
this.stage.addEventListener(MouseEvent.MOUSE_DOWN, containerZiehen);
function containerZiehen(e:MouseEvent):void {
container_mc.startDrag(false,
new Rectangle(stage.stageWidth-container_mc.width, stage.stageHeight-container_mc.height,
container_mc.width-stage.stageWidth, container_mc.height-stage.stageHeight));
Mouse.hide();
this.stage.addChild(handCursor);
handCursor.addEventListener(Event.ENTER_FRAME, updateCursor);

}
this.stage.addEventListener(MouseEvent.MOUSE_UP, containerLoslassen);
function containerLoslassen(e:MouseEvent):void {
container_mc.stopDrag();
Mouse.show();
this.stage.removeChild(handCursor);
handCursor.removeEventListener(Event.ENTER_FRAME, updateCursor);

}
function updateCursor(e:Event):void {
e.target.x=stage.mouseX;
e.target.y=stage.mouseY;
}

Scrollen eines Containers

In dem nachfolgenden swf-Beispiel kann das Luftbild mittels der vier Buttons an den Bühnenrändern nach links, rechts, oben und unten gescrollt werden. Beim Erreichen eines Bildrandes wird der Scrollvorgang beendet und der entsprechende Button wird ausgeblendet, da das Bild nicht mehr weitergescrollt werden kann. Betätigt man den gegenüberliegenden Button wird der ausgeblendete Button wieder sichtbar.
Es wurden dabei zwei Varianten realisert:

Bei dem linken und rechten Button wird der Scrollvorgang durch Anklicken der Buttons gestartet und durch Loslassen der Maustaste über den Buttons wieder gestoppt.
Bei dem oberen und unteren Button wird der Scrollvorgang durch Rollover gestartet und durch Rollout wieder gestoppt. Das ist für mich die elegantere Lösung.

Anmerkung: Bei der Gestaltung der Buttons ist man natürlich völlig frei. Es könnte z.B. auch entlang  jeder Bühnenseite ein schmales, unsichtbares Rechteck als Button platziert werden. Wenn man sich mit dem Mauszeiger einem Rand nähert, wird durch Rollover der Scrollvorgang gestartet und bei Rollout wieder gestoppt.
 

 

In der Bibliothek liegen ein Movieclip, der als einziges Element das Luftbild der Altstadt von Linz enthält und eine dreieckige Schaltfläche.
Eine Instanz des Movieclipcontainers liegt im ersten Frame des Mainmovies und hat den Instanznamen container_mc.
Vier Instanzen der Schaltfläche werden an den Rändern positioniert, entsprechend gedreht und erhalten die Instanznamen links_btn, rechts_btn, oben_btn und unten_btn.
Anmerkung: Nur für die Abbildung wurde die Deckkraft des Containers reduziert, um auch die Bühne zu zeigen.


 

1. Schritt: Scrollen des Containers OHNE Behandlung des Bildrandproblems

Das nachfolgende Skript wird im ersten Frame der Timeline des Mainmovies platziert (scroll_movieclip.fla):
 

var schrittweite:int=5;
 
links_btn.addEventListener(MouseEvent.MOUSE_DOWN, startScrollen);
rechts_btn.addEventListener(MouseEvent.MOUSE_DOWN, startScrollen);
oben_btn.addEventListener(MouseEvent.ROLL_OVER, startScrollen);
unten_btn.addEventListener(MouseEvent.ROLL_OVER, startScrollen);
 
function startScrollen(e:MouseEvent):void {
switch (e.target) {
case links_btn :
container_mc.addEventListener(Event.ENTER_FRAME, nachRechts);
break;
case rechts_btn :
container_mc.addEventListener(Event.ENTER_FRAME, nachLinks);
break;
case oben_btn :
container_mc.addEventListener(Event.ENTER_FRAME, nachUnten);
break;
case unten_btn :
container_mc.addEventListener(Event.ENTER_FRAME, nachOben);
break;
}
}
 
links_btn.addEventListener(MouseEvent.MOUSE_UP, stopScrollen);
rechts_btn.addEventListener(MouseEvent.MOUSE_UP, stopScrollen);
oben_btn.addEventListener(MouseEvent.ROLL_OUT, stopScrollen);
unten_btn.addEventListener(MouseEvent.ROLL_OUT, stopScrollen);
 
function stopScrollen(e:MouseEvent):void {
switch (e.target) {
case links_btn :
container_mc.removeEventListener(Event.ENTER_FRAME, nachRechts);
break;
case rechts_btn :
container_mc.removeEventListener(Event.ENTER_FRAME, nachLinks);
break;
case oben_btn :
container_mc.removeEventListener(Event.ENTER_FRAME, nachUnten);
break;
case unten_btn :
container_mc.removeEventListener(Event.ENTER_FRAME, nachOben);
break;
}
}
 
function nachLinks(e:Event) {
e.target.x-=schrittweite;
}
function nachRechts(e:Event) {
e.target.x+=schrittweite;
}
function nachOben(e:Event) {
e.target.y-=schrittweite;
}
function nachUnten(e:Event) {
e.target.y+=schrittweite;
}


Schrittweise Erklärung des Skripts:

Grundidee: Die vier Scrollbuttons abonnieren das Ereignis MOUSE_DOWN bzw. ROLL_OVER mit dem Eventhandler startScrollen.
In Abhängigkeit davon, welcher Button aktiviert wurde, wird dem Container ein ENTER_FRAME-Eventhandler zugeordnet, der bei jedem ENTER_FRAME-Ereignis den Container um die vordefinierte Schrittweite weiterschiebt.
Beim Loslassen der Maustaste bzw. bei Rollout wird der ENTER_FRAME-Eventhandler wieder vom Container entfernt.

 

var schrittweite:int=5;

In der ganzzahligen Variablen schrittweite wird festgelegt, um wieviele Pixel der Container bei jedem ENTER_FRAME-Ereignis weitergeschoben wird.

 

links_btn.addEventListener(MouseEvent.MOUSE_DOWN, startScrollen);
rechts_btn.addEventListener(MouseEvent.MOUSE_DOWN, startScrollen);
oben_btn.addEventListener(MouseEvent.ROLL_OVER, startScrollen);
unten_btn.addEventListener(MouseEvent.ROLL_OVER, startScrollen);

Die vier Buttoninstanzen abonnieren das Ereignis MOUSE_DOWN bzw. ROLL_OVER mit dem Eventhandler startScrollen.

 

function startScrollen(e:MouseEvent):void {
switch (e.target) {
case links_btn :
container_mc.addEventListener(Event.ENTER_FRAME, nachRechts);
break;
case rechts_btn :
container_mc.addEventListener(Event.ENTER_FRAME, nachLinks);
break;
case oben_btn :
container_mc.addEventListener(Event.ENTER_FRAME, nachUnten);
break;
case unten_btn :
container_mc.addEventListener(Event.ENTER_FRAME, nachOben);
break;
}
}

e.target liefert die Buttoninstanz, die aktiviert wurde. Im switch-Statement wird jede einzelne Buttoninstanz abgefragt und in Abhängigkeit davon abonniert mit addEventListener der Container für das ENTER_FRAME-Ereignis einen anderen Eventhandler.
Wenn der linke Button aktiviert wurde, wird der Container nach rechts geschoben, beim rechten Button nach links, beim oberen Button nach unten und beim unteren Button nach oben.

 

function nachLinks(e:Event) {
e.target.x-=schrittweite;
}
function nachRechts(e:Event) {
e.target.x+=schrittweite;
}
function nachOben(e:Event) {
e.target.y-=schrittweite;
}
function nachUnten(e:Event) {
e.target.y+=schrittweite;
}

e.target liefert den Container. Die vier ENTER_FRAME-Eventhandler (für jeden Button einen) erhöhen bzw. reduzieren je nach Richtung den x- bzw. y-Wert des Containers.
Z.B. wenn der Container nach oben bewegt wird, wird die Schrittweite vom y-Wert abgezogen.

 

links_btn.addEventListener(MouseEvent.MOUSE_UP, stopScrollen);
rechts_btn.addEventListener(MouseEvent.MOUSE_UP, stopScrollen);
oben_btn.addEventListener(MouseEvent.ROLL_OUT, stopScrollen);
unten_btn.addEventListener(MouseEvent.ROLL_OUT, stopScrollen);

Die vier Buttoninstanzen abonnieren auch das Ereignis MOUSE_UP bzw. ROLL_OUT mit dem Eventhandler stopScrollen.

 

function stopScrollen(e:MouseEvent):void {
switch (e.target) {
case links_btn :
container_mc.removeEventListener(Event.ENTER_FRAME, nachRechts);
break;
case rechts_btn :
container_mc.removeEventListener(Event.ENTER_FRAME, nachLinks);
break;
case oben_btn :
container_mc.removeEventListener(Event.ENTER_FRAME, nachUnten);
break;
case unten_btn :
container_mc.removeEventListener(Event.ENTER_FRAME, nachOben);
break;
}
}

e.target liefert wieder die entsprechende Buttoninstanz. Im switch-Statement wird jede einzelne Buttoninstanz abgefragt und in Abhängigkeit davon wird der dazugehörige ENTER_FRAME-Eventhandler mit removeEventListener vom Container entfernt.

 

2. Schritt: Scrollen des Containers MIT Behandlung des Bildrandproblems

Das nachfolgende Skript wird im ersten Frame der Timeline des Mainmovies platziert (scroll_movieclip_limits.fla):

Die Ergänzungen sind hervorgehoben.

var schrittweite:int=5;
 
links_btn.addEventListener(MouseEvent.MOUSE_DOWN, startScrollen);
rechts_btn.addEventListener(MouseEvent.MOUSE_DOWN, startScrollen);
oben_btn.addEventListener(MouseEvent.ROLL_OVER, startScrollen);
unten_btn.addEventListener(MouseEvent.ROLL_OVER, startScrollen);
 
function startScrollen(e:MouseEvent):void {
switch (e.target) {
case links_btn :
rechts_btn.visible=true;
container_mc.addEventListener(Event.ENTER_FRAME, nachRechts);
break;
case rechts_btn :
links_btn.visible=true;
container_mc.addEventListener(Event.ENTER_FRAME, nachLinks);
break;
case oben_btn :
unten_btn.visible=true;
container_mc.addEventListener(Event.ENTER_FRAME, nachUnten);
break;
case unten_btn :
oben_btn.visible=true;
container_mc.addEventListener(Event.ENTER_FRAME, nachOben);
break;
}
}
 
links_btn.addEventListener(MouseEvent.MOUSE_UP, stopScrollen);
rechts_btn.addEventListener(MouseEvent.MOUSE_UP, stopScrollen);
oben_btn.addEventListener(MouseEvent.ROLL_OUT, stopScrollen);
unten_btn.addEventListener(MouseEvent.ROLL_OUT, stopScrollen);
 
function stopScrollen(e:MouseEvent):void {
switch (e.target) {
case links_btn :
container_mc.removeEventListener(Event.ENTER_FRAME, nachRechts);
break;
case rechts_btn :
container_mc.removeEventListener(Event.ENTER_FRAME, nachLinks);
break;
case oben_btn :
container_mc.removeEventListener(Event.ENTER_FRAME, nachUnten);
break;
case unten_btn :
container_mc.removeEventListener(Event.ENTER_FRAME, nachOben);
break;
}
}
 
function nachLinks(e:Event) {
container_mc.x-=schrittweite;
if (container_mc.x < stage.stageWidth-container_mc.width) {
container_mc.x=stage.stageWidth-container_mc.width;
rechts_btn.visible=false;
container_mc.removeEventListener(Event.ENTER_FRAME, nachLinks);
}
}
function nachRechts(e:Event) {
container_mc.x+=schrittweite;
if (container_mc.x > 0) {
container_mc.x=0;
links_btn.visible=false;
container_mc.removeEventListener(Event.ENTER_FRAME, nachRechts);
}
}
function nachOben(e:Event) {
container_mc.y-=schrittweite;
if (container_mc.y < stage.stageHeight-container_mc.height) {
container_mc.y=stage.stageHeight-container_mc.height;
unten_btn.visible=false;
container_mc.removeEventListener(Event.ENTER_FRAME, nachOben);
}
}
function nachUnten(e:Event) {
container_mc.y+=schrittweite;
if (container_mc.y > 0) {
container_mc.y=0;
oben_btn.visible=false;
container_mc.removeEventListener(Event.ENTER_FRAME, nachUnten);
}
}


Schrittweise Erklärung der Erweiterungen:

Es werden hier nur mehr die Abschnitte besprochen, die sich gegenüber dem ersten Schritt geändert haben.

function startScrollen(e:MouseEvent):void {
switch (e.target) {
case links_btn :
rechts_btn.visible=true;
container_mc.addEventListener(Event.ENTER_FRAME, nachRechts);
break;
case rechts_btn :
links_btn.visible=true;
container_mc.addEventListener(Event.ENTER_FRAME, nachLinks);
break;
case oben_btn :
unten_btn.visible=true;
container_mc.addEventListener(Event.ENTER_FRAME, nachUnten);
break;
case unten_btn :
oben_btn.visible=true;
container_mc.addEventListener(Event.ENTER_FRAME, nachOben);
break;
}
}

Bei Aktivierung eines Buttons wird auf jeden Fall der gegenüberliegende Button sichtbar gemacht.
Denn es kann der Fall eintreten, dass ein Button unsichtbar ist, weil der Rand erreicht wurde. Wenn nun der gegenüberliegende Button aktiviert wird und sich der Container in Gegenrichtung vom Rand weg bewegt, muss der unsichtbare Button wieder sichtbar gemacht werden.
Falls der Container keine Randlage einnimmt, ist der Button ohnehin sichtbar und die Anweisung xxx_btn.visible = true verändert nichts.

 

function nachRechts(e:Event) {
container_mc.x+=schrittweite;
if (container_mc.x > 0) {
container_mc.x=0;
links_btn.visible=false;
container_mc.removeEventListener(Event.ENTER_FRAME, nachRechts);
}
}
function nachLinks(e:Event) {
container_mc.x-=schrittweite;
if (container_mc.x < stage.stageWidth-container_mc.width) {
container_mc.x=stage.stageWidth-container_mc.width;
rechts_btn.visible=false;
container_mc.removeEventListener(Event.ENTER_FRAME, nachLinks);
}
}

Stellvertretend werden die Eventhandler nachRechts und nachLinks besprochen:
Zuerst wird der Container um die Schrittweite bewegt. Dabei kann es passieren, dass der Bildrand bereits innerhalb der Bühne liegt.
Daher wird in der darauf folgenden if-Anweisung geprüft, ob das der Fall ist:
In der Funktion nachRechts ist der linke Bildrand innerhalb der Bühne, wenn container_mc.x>0 gilt. Wenn das der Fall ist, wird der x-Wert des Containers auf 0 gesetzt (d.h. der linke Bildrand ist gleich dem linken Bühnenrand), der linke Button wird unsichtbar gesetzt und der ENTER_FRAME-Eventhandler nachtRechts wird vom Container entfernt, da keine Rechtbewegung mehr gemacht werden darf.
In der Funktion nachLinks ist der rechte Bildrand innerhalb der Bühne, wenn container_mc.x gilt. Wenn das der Fall ist, wird der x-Wert des Containers auf stage.stageWidth-container_mc.width gesetzt (d.h. der rechte Bildrand ist gleich dem rechten Bühnenrand), der rechte Button wird unsichtbar gesetzt und der ENTER_FRAME-Eventhandler nachtLinks wird vom Container entfernt, da keine Linksbewegung mehr gemacht werden darf.

Analoge Überlegungen gelten für die Eventhandler nachUnten und nachOben.

Angeklickter Punkt des Containers wird in die Bühnenmitte verschoben (Tween)

 

 

Skript (scroll_tween.fla):

// Durch Anklicken eines Punktes wird dieser ins Zentrum gerückt.
import fl.transitions.Tween;
import fl.transitions.TweenEvent;
import fl.transitions.easing.*;

var tweenX:Tween;
var tweenY:Tween;

this.stage.addEventListener(MouseEvent.CLICK, scrollContainer);

function scrollContainer(e:MouseEvent):void {
var deltaX:int=stage.stageWidth/2-e.stageX;
var deltaY:int=stage.stageHeight/2-e.stageY;
tweenX=new Tween(container_mc,"x",Regular.easeInOut,container_mc.x,container_mc.x+deltaX,1,true);
tweenY=new Tween(container_mc,"y",Regular.easeInOut,container_mc.y,container_mc.y+deltaY,1,true);
tweenX.addEventListener(TweenEvent.MOTION_CHANGE, checkLimitsX);
tweenY.addEventListener(TweenEvent.MOTION_CHANGE, checkLimitsY);
tweenX.start();
tweenY.start();
}
function checkLimitsX(e:TweenEvent):void {
if (e.position > 0 || e.position < stage.stageWidth-container_mc.width) {
e.target.stop();
if (e.position>0) {
container_mc.x=0;
} else {
container_mc.x=stage.stageWidth-container_mc.width;
}
}
}
function checkLimitsY(e:TweenEvent):void {
if (e.position > 0 || e.position < stage.stageHeight-container_mc.height) {
e.target.stop();
if (e.position>0) {
container_mc.y=0;
} else {
container_mc.y=stage.stageHeight-container_mc.height;
}
}
}

 

Schrittweise Erklärung des Skripts

WICHTIG: Bei der Verwendung von Tweens müssen zuerst die entsprechenden Klassen bzw. Pakete importiert werden.

import fl.transitions.Tween;
import fl.transitions.TweenEvent;
import fl.transitions.easing.*;

 
Die Tween-Variablen sollen am besten immer außerhalb von Funktionen definiert werden. Denn sonst kann es passieren, dass eine Tween-Animation noch läuft, aber die Funktion bereits abgearbeitet ist und vom Garbagecollector aus dem Speicher gelöscht wird. Damit aber auch die Tween-Variablen, die in der Funktion deklariert wurden und  nur dort Gültigkeit haben. Dadurch blockeriert die Animation und das Programm steht. Wir haben der laufenden Animation die Tween-Variablen "unter den Füßen weggezogen".

var tweenX:Tween;
var tweenY:Tween;

 
Durch Klick auf die Bühne soll der Scrollvorgang gestartet werden. Daher abonniert die Bühne das Mausereignis CLICK mit dem Eventhandler scrollContainer.

this.stage.addEventListener(MouseEvent.CLICK, scrollContainer);

 
e.stageX und e-stageY liefern die Mausposition des Klickereignisses e relativ zur Bühne. Damit wird der Abstand der Mausposition zur Bühnenmitte berechnet und in deltaX bzw. deltaY abgespeichert.

function scrollContainer(e:MouseEvent):void {
var deltaX:int=stage.stageWidth/2-e.stageX;
var deltaY:int=stage.stageHeight/2-e.stageY;

 
Anschließend werden für die Eigenschaften x und y des Containers container_mc zwei Tweens erzeugt. Ausgangspunkt ist die momentane Position des Containers. Zielpunkt ist die Containerposition verschoben um die Werte deltaX bzw. deltaY.
Damit die Bewegung sanft startet und stoppt wird Regular.easeInOut verwendet

	tweenX = new Tween(container_mc,"x",Regular.easeInOut,container_mc.x,container_mc.x+deltaX,1,true);
tweenY = new Tween(container_mc,"y",Regular.easeInOut,container_mc.y,container_mc.y+deltaY,1,true);

 
Da man während der Bewegung laufend prüfen muss, ob nicht bezüglich der x- bzw. y-Richtung der Rand bereits erreicht wurde, abonnieren die Tweens das TweenEvent-Ereignis MOTION_CHANGE, das immer eintritt, wenn sich die Animationswerte ändern.

	tweenX.addEventListener(TweenEvent.MOTION_CHANGE, checkLimitsX);
tweenY.addEventListener(TweenEvent.MOTION_CHANGE, checkLimitsY);

 Abschließend werden die Tweens gestartet.

	tweenX.start();
tweenY.start();
}

 
Im Eventhandler checkLimitsX wird bei jedem MOTION_CHANGE-Ereignis zuerst geprüft, ob der linke oder rechte Rand des Containers in der Bühne liegt. Über e.position wird der momentane Wert des animierten Parameters, in unserem Fall die x-Position des Containers, ermittelt.
Wenn e.position >0 ist, dann ist der linke Containerrand bereits in die Bühne gerutscht.
Wenn e.position<Bühnebreite-Containerbreite ist, dann ist der rechte Containerrand schon innerhalb der Bühne.
Anmerkung: Diese Überlegungen gelten nur, wenn der Ursprung des Containers in der linken oberen Ecke liegt.

function checkLimitsX(e:TweenEvent):void {
if (e.position > 0 || e.position < stage.stageWidth-container_mc.width) {

 
Wenn das der Fall ist, wird die Tweenanimation gestoppt und die Position des Container nachkorrigiert. Je nachdem, ob der linke oder rechte Containerrand bereits innerhalb der Bühne liegt, wird die x-Position exakt am linken bzw. rechten Bühnenrand ausgerichtet.

		e.target.stop();
if (e.position>0) {
container_mc.x=0;
} else {
container_mc.x=stage.stageWidth-container_mc.width;
}
}
}

Analoges gilt für die Funktion checkLimitsY.

Kombination der 3 Methoden

Die in den ersten drei Kapiteln erklärten Methoden lassen sich natürlich kombinieren.
Man muss nur aufpassen, dass sich die Eventhandler nicht gegenseitig stören.
In unserem Beispiel reagieren sowohl das Ziehen mit der Maus (Kapitel 1) als auch das Anklicken und automatische Verschieben des Klickpunkts in die Bühnenmitte (Kapitel 3) auf das MOUSE_DOWN-Ereignis.

In der nachstehenden swf-Datei muss man daher beim Klick zusätzlich die Strg-Taste gedrückt halten, damit der Klickpunkt in die Bühnemitte verschoben wird.

 

 

Das kombinierte Skript (kombination.fla):

Die Veränderungen und Ergänzungen sind hervorgehoben.
Vor allem muss man auch berücksichtigen, dass nicht nur beim Scrollen mit den Buttons die Ränder erreicht werden können. Daher müssen auch bei den anderen beiden Methoden die Buttons aus- und eingeblendet werden.

Deshalb wurde die Frage der Sichtbarkeit der Scrollbuttons in einen ENTER_FRAME-Eventhandler ausgelagert, der laufend prüft, ob ein Button gezeigt werden soll oder nicht.

 

// Übernommen aus Kapitel 1: Container mit bei gedrückter Maustaste ziehen
var handCursor:MovieClip = new Handcursor();
this.stage.addEventListener(MouseEvent.MOUSE_DOWN, containerZiehen);
function containerZiehen(e:MouseEvent):void {
if (! e.ctrlKey) {
container_mc.startDrag(false,new Rectangle(stage.stageWidth-container_mc.width,stage.stageHeight-container_mc.height,container_mc.width-stage.stageWidth,container_mc.height-stage.stageHeight));
Mouse.hide();
this.stage.addChild(handCursor);
handCursor.addEventListener(Event.ENTER_FRAME, updateCursor);
}
}
this.stage.addEventListener(MouseEvent.MOUSE_UP, containerLoslassen);
function containerLoslassen(e:MouseEvent):void {
if (! e.ctrlKey) {
container_mc.stopDrag();
Mouse.show();
this.stage.removeChild(handCursor);
handCursor.removeEventListener(Event.ENTER_FRAME, updateCursor);
}
}
function updateCursor(e:Event):void {
e.target.x=stage.mouseX;
e.target.y=stage.mouseY;
}
 
// Übernommen aus Kapitel 2: Scrollen über Buttons
var schrittweite:int=5;
links_btn.addEventListener(MouseEvent.ROLL_OVER, startScrollen);
rechts_btn.addEventListener(MouseEvent.ROLL_OVER, startScrollen);
oben_btn.addEventListener(MouseEvent.ROLL_OVER, startScrollen);
unten_btn.addEventListener(MouseEvent.ROLL_OVER, startScrollen);
function startScrollen(e:MouseEvent):void {
switch (e.target) {
case links_btn :
container_mc.addEventListener(Event.ENTER_FRAME, nachRechts);
break;
case rechts_btn :
container_mc.addEventListener(Event.ENTER_FRAME, nachLinks);
break;
case oben_btn :
container_mc.addEventListener(Event.ENTER_FRAME, nachUnten);
break;
case unten_btn :
container_mc.addEventListener(Event.ENTER_FRAME, nachOben);
break;
}
}
links_btn.addEventListener(MouseEvent.ROLL_OUT, stopScrollen);
rechts_btn.addEventListener(MouseEvent.ROLL_OUT, stopScrollen);
oben_btn.addEventListener(MouseEvent.ROLL_OUT, stopScrollen);
unten_btn.addEventListener(MouseEvent.ROLL_OUT, stopScrollen);
function stopScrollen(e:MouseEvent):void {
switch (e.target) {
case links_btn :
container_mc.removeEventListener(Event.ENTER_FRAME, nachRechts);
break;
case rechts_btn :
container_mc.removeEventListener(Event.ENTER_FRAME, nachLinks);
break;
case oben_btn :
container_mc.removeEventListener(Event.ENTER_FRAME, nachUnten);
break;
case unten_btn :
container_mc.removeEventListener(Event.ENTER_FRAME, nachOben);
break;
}
}
function nachLinks(e:Event) {
e.target.x-=schrittweite;
if (container_mc.x
container_mc.x=stage.stageWidth-container_mc.width;
container_mc.removeEventListener(Event.ENTER_FRAME, nachLinks);
}
}
function nachRechts(e:Event) {
e.target.x+=schrittweite;
if (container_mc.x>0) {
container_mc.x=0;
container_mc.removeEventListener(Event.ENTER_FRAME, nachRechts);
}
}
function nachOben(e:Event) {
container_mc.y-=schrittweite;
if (container_mc.y
container_mc.y=stage.stageHeight-container_mc.height;
container_mc.removeEventListener(Event.ENTER_FRAME, nachOben);
}
}
function nachUnten(e:Event) {
container_mc.y+=schrittweite;
if (container_mc.y>0) {
container_mc.y=0;
container_mc.removeEventListener(Event.ENTER_FRAME, nachUnten);
}
}
 
// Übernommen aus Kapitel 3: Container anklicken. Klickpunkt wird in die Bühnenmitte verschoben.
import fl.transitions.Tween;
import fl.transitions.TweenEvent;
import fl.transitions.easing.*;

var tweenX:Tween;
var tweenY:Tween;

this.stage.addEventListener(MouseEvent.CLICK, scrollContainer);

function scrollContainer(e:MouseEvent):void {
var deltaX:int=stage.stageWidth/2-e.stageX;
var deltaY:int=stage.stageHeight/2-e.stageY;
tweenX=new Tween(container_mc,"x",Regular.easeInOut,container_mc.x,container_mc.x+deltaX,1,true);
tweenY=new Tween(container_mc,"y",Regular.easeInOut,container_mc.y,container_mc.y+deltaY,1,true);
tweenX.addEventListener(TweenEvent.MOTION_CHANGE, checkLimitsX);
tweenY.addEventListener(TweenEvent.MOTION_CHANGE, checkLimitsY);
tweenX.start();
tweenY.start();
}
function checkLimitsX(e:TweenEvent):void {
if (e.position>0||e.position
e.target.stop();
if (e.position>0) {
container_mc.x=0;
} else {
container_mc.x=stage.stageWidth-container_mc.width;
}
}
}
function checkLimitsY(e:TweenEvent):void {
if (e.position>0||e.position
e.target.stop();
if (e.position>0) {
container_mc.y=0;
} else {
container_mc.y=stage.stageHeight-container_mc.height;
}
}
}
 
// NEU: Sichtbarkeit der Scrollbuttons prüfen
this.stage.addEventListener(Event.ENTER_FRAME, checkButtons);
function checkButtons(e:Event):void {
links_btn.visible=true;
rechts_btn.visible=true;
oben_btn.visible=true;
unten_btn.visible=true;
if (container_mc.x>=0) {
container_mc.x=0;
links_btn.visible=false;
}
if (container_mc.x<=stage.stageWidth-container_mc.width) {
container_mc.x=stage.stageWidth-container_mc.width;
rechts_btn.visible=false;
}
if (container_mc.y>=0) {
container_mc.y=0;
oben_btn.visible=false;
}
if (container_mc.y<=stage.stageHeight-container_mc.height) {
container_mc.y=stage.stageHeight-container_mc.height;
unten_btn.visible=false;
}
}

 

Durch  !e.ctrlKey wird geprüft, ob NICHT beim Drücken der Maustaste zusätzlich die Strg-Taste gedrückt wurde.

In der Funktion checkButtons werden immer zuerst alle Scrollbuttons sichtbar gesetzt und dann wird für jeden Button geprüft, ob dieser nicht doch auf unsichtbar gesetzt werden muss.

Scrollen eines Containers innerhalb eines maskierten Rechtecks, das kleiner als die Bühne ist

In der nachstehenden swf-Datei lässt sich der Container innerhalb eines Rechtecks, das kleiner als die gelbe Bühne ist, scrollen.

 

 

Um einen Container, der größer als die Bühne ist, innerhalb eines Rechtecks, das kleiner als die Bühne ist, zu scrollen, benötigt der Container  eine Maske in der Größe des Rechtecks.

In der obigen Abbildung ist das grüne Rechteck eine Instanz maske_mc des Movieclips Maske aus der Bibliothek und das orange Rechteck ist die Bühne.
Anmerkung: Nur für die Abbildung wurde die Deckkraft des Containers mit dem Luftbild reduziert, um auch die Bühne zu zeigen.

Die vier Scrollbuttons wurden an den Rändern des grünen Rechtecks platziert.
In der Zeitleiste sieht man, dass das grüne Rechteck in der Ebene mask eine Maske für die Ebene container ist.

Das grüne Rechteck wurde in einen Movieclip verpackt, um über den Instanznamen maske_mc die Position und die Größe des Rechtecks für die  Berechnungen der Scrollgrenzen ermitteln zu können.

 
Geändertes Skript (scroll_movieclip_mask.fla):

Gegenüber dem Beispiel aus dem letzten Kapitel müssen die Funktionen nachLinks, nachRechts, nachOben und nachUnten adaptiert werden. Die hervorgehobenen Änderungen beziehen sich auf die Berechnung der Scollgrenzen relativ zum Maskenrechteck.

 

function nachLinks(e:Event) {
container_mc.x-=schrittweite;
if (container_mc.x<(maske_mc.x+maske_mc.width)-container_mc.width) {
container_mc.x=(maske_mc.x+maske_mc.width)-container_mc.width;
rechts_btn.visible=false;
container_mc.removeEventListener(Event.ENTER_FRAME, nachLinks);
}
}
function nachRechts(e:Event) {
container_mc.x+=schrittweite;
if (container_mc.x>maske_mc.x) {
container_mc.x=maske_mc.x;
links_btn.visible=false;
container_mc.removeEventListener(Event.ENTER_FRAME, nachRechts);
}
}
function nachOben(e:Event) {
container_mc.y-=schrittweite;
if (container_mc.y<(maske_mc.y+maske_mc.height)-container_mc.height) {
container_mc.y=(maske_mc.y+maske_mc.height)-container_mc.height;
unten_btn.visible=false;
container_mc.removeEventListener(Event.ENTER_FRAME, nachOben);
}
}
function nachUnten(e:Event) {
container_mc.y+=schrittweite;
if (container_mc.y>maske_mc.y) {
container_mc.y=maske_mc.y;
oben_btn.visible=false;
container_mc.removeEventListener(Event.ENTER_FRAME, nachUnten);
}
}
 

Die Abbildung zeigt wie für die Funktion nachLinks die Grenze berechnet wird, über die der Container nicht weiter nach links verschoben werden darf.

 

Import eines scrollbaren Containers als swf-Datei in ein anderes Flash-Movie (swf-import.fla)

Wenn man eine swf-Datei, die einen scrollbaren Container enthält, mit einem Loader und der Methode load in ein anderes Flash-Movie importiert, dann ist der Container auch über die Bühnengröße der importierten swf-Datei hinaus sichtbar.
Siehe rote Fläche in der nachstehenden Abbildung.

Die Größe des Loaders entspricht der Größe des gesamten Containers und nicht der Bühnengröße der importierten swf-Datei.

In obigem Beispiel hatte die Bühne der swf-Datei scroll_movieclip_limits.swf eine Größe von 600x450px und wurde in ein Flash-Movie mit einer Bühnengröße von 800x600px importiert. Der Container mit dem Luftbild ist über die ursprünglichen Grenzen hinaus sichtbar.

Lösung:

Wenn man also einen scrollbaren Container in ein anderes Flash-Movie importieren möchte, muss man den Container auf jeden Fall mit einer Rechtecksmaske abdecken.

Scrollen und Ziehen mit der Komponente ScrollPane

 

Die Komponente ScrollPane ermöglicht das Scrollen und Ziehen eines Bildes oder einer swf-Datei, die von extern in den ScrollPane-Container geladen werden. Darin unterscheidet sich diese von den vorangegangenen Methoden, bei denen ein Movieclip oder Sprite gescrollt oder gezogen wird.
 

Die Fenster für die Komponenten und des Komponenten-Inspektors lassen sich im Menü über Fenster>Komponenten (Strg+F7) bzw. Fenster>Komponenten-Inspektor (Umschalt+F7) öffnen.

Im nachfolgenden Beispielvideo liegen die fla-Datei (scrollpane.fla) und das zu ladende Bild in demselben lokalen Ordner. Daher genügte es in diesem Fall bei der Quelle nur den Dateinamen anzugeben. Bei einer Internet-Anwendung muss die absolute URL der Datei angegeben werden.

In dem Video wird gezeigt, wie man
  • eine Instanz der Komponente ScrollPane erzeugt,
  • ein extenes Bild über Scrollbalken scrollen und
  • zusätzlich auch bei gedrückter Maustaste ziehen kann,
  • wie man die Scrollbalken ausblendet und
  • das Erscheinungsbild der Komponente (Skin) verändern kann.

 

Im Videobeispiel wurden bis jetzt kein ActionScript-Code geschrieben. Allerdings können im Komponenten-Inspektor nur wenige Parameter eingestellt werden. Wenn man man andere Parameter verändern möchte, muss man dafür ein selbst ein Skript schreiben.

Die Komponente ScrollPane ist eine Unterklasse der Komponente BaseScrollPane, in der wichtige Eigenschaften zu finden sind. Beide sind im Paket fl.containers enthalten.
 

Ändern der Startposition des geladenen Bildes bzw. der swf-Datei (scrollpane_zentriert.fla)

Standardmäßig wird die geladene Datei in die linke ober Ecke des ScollPane-Containers gelegt.
Über die BaseScrollPane-Eigenschaften horizontalScrollPosition und verticalScrollPosition lässt sich die Datei positionieren, wobei über die BaseScrollPane-Eigenschaften maxHorizontalScrollPosition und maxVerticalScrollPositiondie maximale Bildlaufposition ermittelt werden kann.

Das nachfolgende Skript, das ergänzend zum Beispiel aus dem Video im ersten Frame der Zeitleiste platziert wird, zentriert die geladene Datei auf die Mitte des ScrollPane-Containers.
Wichtig ist dabei, dass die Instanz der Komponente ScrollPane benannt wird. In unserem Fall mit scrollPane.

// Positionieren des Bildes
scrollPane.addEventListener(Event.COMPLETE, completeHandler);
function completeHandler(event:Event):void {
scrollPane.horizontalScrollPosition=scrollPane.maxHorizontalScrollPosition/2;
scrollPane.verticalScrollPosition=scrollPane.maxVerticalScrollPosition/2;
}

Die Positionierung der Datei kann erst erfolgen, wenn die Datei vollständig geladen wurde. Daher wird die Zentrierung in einem COMPLETE-Eventhandler vorgenommen.
Zuerst werden die maximalen Positionen ermittelt, diese anschließend halbiert und mit diesen Werten wird die Datei positioniert.

In der nachfolgenden swf-Datei sind die Slider der Scrollbalken in der Mitte der Laufleisten.
 

 

Eine Instanz der Komponente in ActionScript erzeugen und verwenden (scrollpane_as.fla)

Damit man eine Instanz der Komponente über ActionScript erzeugen kann, muss die Komponente einmal aus dem Komponentenfenster in die Bibliothek gezogen werden. Oder: Vom Komponentenfenster auf die Bühne ziehen. Damit liegt die Komponente und ihre Assets auch in der Bibliothek und dann anschließend die Instanz auf der Bühne wieder löschen.

Nachfolgend das Skript, das im Ergebnis dem vorangegangenen Beispiel scrollpane_zentriert.fla entspricht.
 

import fl.containers.*;
import fl.controls.*;

// Neuen ScrollPane-Container erzeugen
var scrollPane:ScrollPane = new ScrollPane();

// Positionieren des ScrollPane-Containers
scrollPane.width=stage.stageWidth-40;
scrollPane.height=stage.stageHeight-40;
scrollPane.x=20;
scrollPane.y=20;
this.addChild(scrollPane);

// Externen Inhalt des ScrollPane-Containers laden
scrollPane.source="http://www.dma.ufg.ac.at/assets/23033/intern/linz_altstadt.jpg";

// Dragging erlauben
scrollPane.scrollDrag=true;

// Einblenden der Scrollbars
scrollPane.horizontalScrollPolicy=ScrollPolicy.ON;
scrollPane.verticalScrollPolicy=ScrollPolicy.ON;

// Zentrieren des Bildes auf die Containermitte
scrollPane.addEventListener(Event.COMPLETE, completeHandler);
function completeHandler(event:Event):void {
scrollPane.horizontalScrollPosition=scrollPane.maxHorizontalScrollPosition/2;
scrollPane.verticalScrollPosition=scrollPane.maxVerticalScrollPosition/2;
}

Man sieht, dass alle Eigenschaften, die sich im Komponenten-Inspektor einstellen lassen, auch direkt in Actionskript verändert werden können.
Der Import des fl.controls-Pakets ist notwendig wegen der ScrollPolicy-Klasse.
 

Selbst gestaltete Scrollbuttons zusammen mit der Komponente ScrollPane verwenden (scrollpane_buttons.fla)

Analog zu Kapitel 2 kann man selbst gestaltete Buttons zum Scrollen der importierten Datei verwenden.
Dabei werden die Buttons in eine eigene Ebene über der Ebene mit der ScrollPane-Instanz gelegt. Im nachfolgenden Beispiel zwei Dreieck-Buttons für das Scrollen nach links und rechts.
Die Buttoninstanzen haben die Namen links_btn und rechts_btn. Bei Rollover über die Buttons wird das Bild gescrollt. Die Scrollbalken der Komponenteninstanz scrollPane wurden im Komponenten-Inspektor ausgeblendet, und die Ziehfunktion wurde aktiviert.

 

 

Das Skript für die Funktionalität der Buttons entspricht im Großen und Ganzen dem Skript aus Kapitel 2.
Der Vorteil der Verwendung der ScrollPane-Komponente besteht darin, dass man das Randproblem nicht extra behandeln muss. Wenn das Bild die Containerkante erreicht hat, lässt es sich automatisch nicht mehr weiter bewegen.

Im nachfolgenden Skript sind die wichtigen Stellen hervorgehoben:
 

// Zentrieren des Bildes auf die Bühnenmitte
scrollPane.addEventListener(Event.COMPLETE, completeHandler);
function completeHandler(event:Event):void {
//trace(scrollPane.maxHorizontalScrollPosition);
//trace(scrollPane.maxVerticalScrollPosition);
scrollPane.horizontalScrollPosition=scrollPane.maxHorizontalScrollPosition/2;
scrollPane.verticalScrollPosition=scrollPane.maxVerticalScrollPosition/2;
}

// Der nachfolgende Abschnitt kommt für die Buttons dazu:
// Scrollbuttons: entspricht fast zur Gänze dem Skript aus Kapitel 2
var schrittweite:int=5;
links_btn.addEventListener(MouseEvent.ROLL_OVER, startScrollen);
rechts_btn.addEventListener(MouseEvent.ROLL_OVER, startScrollen);
function startScrollen(e:MouseEvent):void {
switch (e.target) {
case links_btn :
this.addEventListener(Event.ENTER_FRAME, nachRechts);
break;
case rechts_btn :
this.addEventListener(Event.ENTER_FRAME, nachLinks);
break;
}
}
links_btn.addEventListener(MouseEvent.ROLL_OUT, stopScrollen);
rechts_btn.addEventListener(MouseEvent.ROLL_OUT, stopScrollen);
function stopScrollen(e:MouseEvent):void {
switch (e.target) {
case links_btn :
this.removeEventListener(Event.ENTER_FRAME, nachRechts);
break;
case rechts_btn :
this.removeEventListener(Event.ENTER_FRAME, nachLinks);
break;
}
}
function nachLinks(e:Event) {
scrollPane.horizontalScrollPosition -= schrittweite;
}
function nachRechts(e:Event) {
scrollPane.horizontalScrollPosition += schrittweite;
}

 

Die eigentliche Positionsveränderung erfolgt durch die Eventhandler nachLinks und nachRechts, in denen die x-Position des Bildes über die scrollPane-Eigenschaft horizontalScrollPosition um die Schrittweit verkleinert bzw. vergrößert wird.

Mehrere geschichtete Container scrollen

Von Zugfahrten kennt man, dass Objekte im Vordergrund schneller am Fenster vorbeiziehen als Objekte, die weiter hinten liegen. Diesen Tiefeneffekt kann man nutzen und mehrere Container überlagern. Z.B. je einen Container für den Vorder-, Mittel- und Hintergrund.
Siehe z.B. http://www.stylewars.com/index2.html

Diese Container  müssen beim Scrollen unterschiedlich schnell bewegt werden damit der Effekt wirksam wird.
In der nachfolgenden swf-Dateilassen sich drei Container über die Dreieckbuttons am linken und rechten Bühnenrand scrollen.

 

 

Das Grundprinzip des Scrollen von Containern aus Kaptiel 2 bleibt gleich. Es muss aber gleichzeitig auf mehrere Container angewandt werden.

Man muss nur überlegen, in welcher Geschwindiglkeit die drei Container bewegt werden müssen.
 

Berechnung der Scrollgeschwindigkeit

Der Container im Vordergrund ist der längste, da er sich am schnellsten bewegen muss. Die maximale Scrolldistanz ist a. Nach hinten hin werden die Container immer kürzer und somit auch die maximalen Scrolldistanzen b und c.

Wenn man nun eine Schrittweite vorgibt, muss für jeden Container ein Faktor errechnet werden, mit dem die Schrittweite multipliziert wird. Der Bezugscontainer ist der vorderste mit dem Faktor 1.

Die Faktoren b/a bzw. c/a reduzieren für den Mittel- bzw. Hintergrund die Schrittweite, sodass die drei Container immer synchron laufen und gleichzeitig am linken und rechten Bühnenrand anstehen.

Skript (scroll_schichten.fla):

Das Skript wird wieder im ersten Frame des Mainmovies platziert.

Es gibt 3 Instanzen von Movieclipcontainern:
Vordergrund ... container1_mc,
Mittelgrund ... container1_mc,
Hintergrund ... container1_mc.

In den Variablen speedFactor1 und speedFactor2 werden die Faktoren zur Schrittweitenreduzierung für die Container container2_mc und container3_mc gespeichert.

Da zu Beginn die drei Container auf die linke Bühnenkante ausgerichtet sind, wird der linke Scrollbutton zuerst ausgeblendet.

In den Funktionen startScrollen und stopScrollen werden die Eventhandler nachLinks und nachRechts für alle drei Container aktiviert bzw. deaktiviert.

In den Funktionen nachLinks und nachRechts wird je nachdem für welchen Container die Funktionen aufgerufen wurden, die Schrittweite mit dem entsprechenden Reduzierungsfaktor multipliziert.

var schrittweite:int=10;
var speedFactor1:Number=(container2_mc.width-stage.stageWidth)/(container1_mc.width-stage.stageWidth);
var speedFactor2:Number=(container3_mc.width-stage.stageWidth)/(container1_mc.width-stage.stageWidth);

 
links_btn.visible=false;

links_btn.addEventListener(MouseEvent.ROLL_OVER, startScrollen);
rechts_btn.addEventListener(MouseEvent.ROLL_OVER, startScrollen);
function startScrollen(e:MouseEvent):void {
switch (e.target) {
case links_btn :
rechts_btn.visible=true;
container1_mc.addEventListener(Event.ENTER_FRAME, nachRechts);
container2_mc.addEventListener(Event.ENTER_FRAME, nachRechts);
container3_mc.addEventListener(Event.ENTER_FRAME, nachRechts);
break;
case rechts_btn :
links_btn.visible=true;
container1_mc.addEventListener(Event.ENTER_FRAME, nachLinks);
container2_mc.addEventListener(Event.ENTER_FRAME, nachLinks);
container3_mc.addEventListener(Event.ENTER_FRAME, nachLinks);
break;
}
}
links_btn.addEventListener(MouseEvent.ROLL_OUT, stopScrollen);
rechts_btn.addEventListener(MouseEvent.ROLL_OUT, stopScrollen);
function stopScrollen(e:MouseEvent):void {
switch (e.target) {
case links_btn :
container1_mc.removeEventListener(Event.ENTER_FRAME, nachRechts);
container2_mc.removeEventListener(Event.ENTER_FRAME, nachRechts);
container3_mc.removeEventListener(Event.ENTER_FRAME, nachRechts);
break;
case rechts_btn :
container1_mc.removeEventListener(Event.ENTER_FRAME, nachLinks);
container2_mc.removeEventListener(Event.ENTER_FRAME, nachLinks);
container3_mc.removeEventListener(Event.ENTER_FRAME, nachLinks);
break;
}
}
function nachLinks(e:Event) {
switch (e.target) {
case container3_mc :
e.target.x -= schrittweite * speedFactor2;
break;
case container2_mc :
e.target.x -= schrittweite * speedFactor1;
break;
case container1_mc :
e.target.x -= schrittweite;
break;
}
if (e.target.x < stage.stageWidth-e.target.width) {
// alle Container werden wieder exakt auf den rechten Rand ausgerichtet
container1_mc.x=stage.stageWidth-container1_mc.width;
container2_mc.x=stage.stageWidth-container2_mc.width;
container3_mc.x=stage.stageWidth-container3_mc.width;
rechts_btn.visible=false;
e.target.removeEventListener(Event.ENTER_FRAME, nachLinks);
}
}
function nachRechts(e:Event) {
switch (e.target) {
case container3_mc :
e.target.x += schrittweite * speedFactor2;
break;
case container2_mc :
e.target.x += schrittweite * speedFactor1;
break;
case container1_mc :
e.target.x += schrittweite;
break;
}
if (e.target.x > 0) {
// alle Container werden wieder exakt auf den linken Rand ausgerichtet
container1_mc.x=0;
container2_mc.x=0;
container3_mc.x=0;
links_btn.visible=false;
e.target.removeEventListener(Event.ENTER_FRAME, nachRechts);
}
}

Endloses Scrollen von 360°-Panoramen

Oft steht man vor der Aufgabe, ein 360°-Panoramabild in eine swf-Datei einzubinden, in dem man sich in beide Richtungen endlos im Kreis drehen kann.
Wie man professionell Panoramabilder aufnimmt, zeigt folgendes Video aus www.foto-podcast.de

In der nachstehenden swf-Datei kann man durch Rollover über die Dreiecke an den seitlichen Rändern das Panorama endlos nach links oder rechts verschieben.
 

 

Das Panoramabild (hier der Hauptplatz von Linz im Winter) wird in einem Bildbearbeitungsprogramm so vorbereitet, dass die linke und rechte Bildkante identisch sind.

Abschließend wird in der Größe der Bühne das linke Ende des Panoramas noch einmal an die rechte Panoramakante angehängt.
Das ist notwendig, um das endlose Rotieren zu ermöglichen.
Im nachstehenden Bild sind die beiden blau umrandeten Rechtecke identisch und haben die Größe der Bühne.
Dieses Bild wird der Inhalt des zu rotierenden Containers.

Die beiden Scrollbuttons liegen in einer eigenen Ebene und sind auf die seitlichen Ränder ausgerichtet.
Bei Rollover wird nun die Movieclipinstanz analog zu Kapitel 2: Scrollen eines Containers nach links bzw. rechts bewegt.

Skript (panorama360_scroll.fla):

Wir übernehmen dafür aus dem zweiten Kapitel Scrollen eines Containers das Skript aus dem 1. Schritt Scrollen des Containers OHNE Behandlung des Bildrandproblems. Die Ergänzungen für das endlose Rotieren sind hervorgehoben und werden anschließend erklärt.
 

var schrittweite:int=15;
links_btn.addEventListener(MouseEvent.ROLL_OVER, startScrollen);
rechts_btn.addEventListener(MouseEvent.ROLL_OVER, startScrollen);
function startScrollen(e:MouseEvent):void {
switch (e.target) {
case links_btn :
panorama_mc.addEventListener(Event.ENTER_FRAME, nachRechts);
break;
case rechts_btn :
panorama_mc.addEventListener(Event.ENTER_FRAME, nachLinks);
break;
}
}
links_btn.addEventListener(MouseEvent.ROLL_OUT, stopScrollen);
rechts_btn.addEventListener(MouseEvent.ROLL_OUT, stopScrollen);
function stopScrollen(e:MouseEvent):void {
switch (e.target) {
case links_btn :
panorama_mc.removeEventListener(Event.ENTER_FRAME, nachRechts);
break;
case rechts_btn :
panorama_mc.removeEventListener(Event.ENTER_FRAME, nachLinks);
break;
}
}
function nachLinks(e:Event) {
e.target.x -= schrittweite;
if (e.target.x + e.target.width < stage.stageWidth) {
e.target.x += e.target.width - stage.stageWidth;
}

}
function nachRechts(e:Event) {
e.target.x += schrittweite;
if (e.target.x > 0) {
e.target.x -= e.target.width - stage.stageWidth;
}
}

In der if-Anweisung der Funktionen nachLinks wird geprüft, ob nach Abzug der Schrittweite die rechte Containerkante bereits die rechte Bühnenkante erreicht hat. Wenn das der Fall ist, wird der ganze Container um (Containerbreite - Bühnebreite) nach rechts versetzt. Da die beiden Enden des Containers identisch sind, ändert sich das gezeigte Bild nicht, aber der Container kann jetzt weiterhin nach links verschoben werden.

In der if-Anweisung der Funktionen nachRechts wird geprüft, ob nach Hinzufügen der Schrittweite die linke Containerkante bereits die linke Bühnenkante erreicht hat. Wenn das der Fall ist, wird der ganze Container um (Containerbreite - Bühnebreite) nach links versetzt. Da die beiden Enden des Containers identisch sind, ändert sich das gezeigte Bild nicht, aber der Container kann jetzt weiterhin nach rechts verschoben werden.


Wichtige Anmerkung:

Bei dieser Methode wird die exakte Bühnengröße schon im Voraus in das Panoramabild eingearbeitet. Daher sollte man verhindern, dass die BenutzerInnen das Player-Fenster skalieren können ohne dass der Inhalt angepasst wird. 

Man kann das verhindern, wenn man den ScaleMode der Bühne auf

stage.scaleMode = StageScaleMode.EXACT_FIT; (Proportionen bleiben nicht erhalten)

oder

stage.scaleMode = StageScaleMode.NO_BORDER; (Proportionen bleiben erhalten)

setzt.

 

2. Möglichkeit: Zwei Container verwenden (panorama360_scroll_2Teile.fla)

Wenn man im Panoramabild nicht das linke Ende noch einmal am rechten Ende wiederholen möchte, gibt es die Möglichkeit, das Bild exakt in der Hälfte zu teilen und jede Hälfte in einen eigenen Container zu legen.
Dabei wird ein Container, sobald er in Scrollrichtung aus der Bühne hinaus bewegt wurde, hinter dem anderen Container positioniert.

Das Skript zum Selbststudium ohne weitere Erklärung

var schrittweite:int=15;
 
links_btn.addEventListener(MouseEvent.ROLL_OVER, startScrollen);
rechts_btn.addEventListener(MouseEvent.ROLL_OVER, startScrollen);
 
function startScrollen(e:MouseEvent):void {
switch (e.target) {
case links_btn :
panorama1_mc.addEventListener(Event.ENTER_FRAME, nachRechts);
panorama2_mc.addEventListener(Event.ENTER_FRAME, nachRechts);
break;
case rechts_btn :
panorama1_mc.addEventListener(Event.ENTER_FRAME, nachLinks);
panorama2_mc.addEventListener(Event.ENTER_FRAME, nachLinks);
break;
}
}
 
links_btn.addEventListener(MouseEvent.ROLL_OUT, stopScrollen);
rechts_btn.addEventListener(MouseEvent.ROLL_OUT, stopScrollen);
function stopScrollen(e:MouseEvent):void {
switch (e.target) {
case links_btn :
panorama1_mc.removeEventListener(Event.ENTER_FRAME, nachRechts);
panorama2_mc.removeEventListener(Event.ENTER_FRAME, nachRechts);
break;
case rechts_btn :
panorama1_mc.removeEventListener(Event.ENTER_FRAME, nachLinks);
panorama2_mc.removeEventListener(Event.ENTER_FRAME, nachLinks);
break;
}
}
 
function nachLinks(e:Event) {
e.target.x-=schrittweite;
// Neupositionierung: Der Container wird um seine doppelte Länge nach rechts verschoben.
if (e.target.x+e.target.width < 0) {
e.target.x = e.target.x + 2 * e.target.width;
}

}
function nachRechts(e:Event) {
e.target.x+=schrittweite;
// Neupositionierung: Der Container wird um seine doppelte Länge nach links verschoben.
if (e.target.x > stage.stageWidth) {
e.target.x = e.target.x - 2 * e.target.width;
}
}

Endloses Draggen von 360°-Panoramen

In der nachfolgenden swf-Datei kann man bei gedrückter Maustaste das Panorama nach links oder rechts ziehen ohne an eine Grenze zu stoßen.
 

 

Skript (panorama360_drag.fla):

Das Skript wird wieder im ersten Frame des Mainmovies platziert.
Das Draggen der Movieclipinstanz panorama_mc wird wie in Kapitel 1 mit startDrag realisiert, das bei  MOUSE_DOWN aktiviert und bei MOUSE_UP mit stopDrag deaktiviert wird.
Neu hinzu gefügt wurde eine ENTER_FRAME-Eventhandler rotierePanorama, der wie im vorhergehenden Kapitel, die Neupositionierung des Panoramas übernimmt.

this.stage.addEventListener(MouseEvent.MOUSE_DOWN, containerZiehen);
 
function containerZiehen(e:MouseEvent):void {
panorama_mc.startDrag(false, new Rectangle (int.MIN_VALUE/4,0,int.MAX_VALUE,0));
}
 
this.stage.addEventListener(MouseEvent.MOUSE_UP, containerLoslassen);
 
function containerLoslassen(e:MouseEvent):void {
panorama_mc.stopDrag();
}
 
panorama_mc.addEventListener(Event.ENTER_FRAME, rotierePanorama);
 
function rotierePanorama(e:Event) {
if (e.target.x+e.target.width < stage.stageWidth) {
e.target.x+=e.target.width-stage.stageWidth;
} else if (e.target.x > 0) {
e.target.x-=e.target.width-stage.stageWidth;
}
}

 

Erklärungen zum Skript

In diesem Fall muss man den Container beliebig weit nach links bzw. rechts ziehen können. Nach oben und unten darf er aber nicht verschoben werden. D.h. der Container darf sich nur entlang einer "unbegrenzt" langen horizontalen Linie bewegen.
Diese Linie können wir durch ein Rechteck mit der Höhe 0 definieren, das seinen Ursprung "ganz weit" links hat. Die kleineste mögliche ganze Zahl ist für ActionScript durch int.MIN_VALUE festgelegt. Das Rechteck soll "sehr lang" sein. Die größte mögliche ganze Zahl ist für ActionScript durch int.MAX_VALUE festgelegt.
Wenn man den Ursprungs des Rechtecks auf (int.MIN_VALUE|0), die Länge mit int.MAX_VALUE und die Höhe mit 0 festlegt, funktioniert es noch nicht richtig. Erst bei int.MIN_VALUE/4 erreicht man den gewünschten Dragvorgang. Der Faktor 1/4 wurde durch Probieren ermittelt.

panorama_mc.startDrag(false, new Rectangle (int.MIN_VALUE/4,0,int.MAX_VALUE,0));

 
Das Panorama abonniert das ENTER_FRAME-Ereignis mit dem rotierePanorama-Eventhandler.
Es wird wieder, wie in den Funktionen nachRechts und nachLinks aus dem Vorkapitel, geprüft, ob durch den Dragvorgang einer der beiden Panoramakanten in der Bühne liegt. Wenn das der Fall ist, wird das Panorama um (Panoramabreite - Bühnenbreite) verschoben.

panorama_mc.addEventListener(Event.ENTER_FRAME, rotierePanorama);
function rotierePanorama(e:Event) {
if (e.target.x+e.target.width < stage.stageWidth) {
e.target.x+=e.target.width-stage.stageWidth;
} else if (e.target.x > 0) {
e.target.x-=e.target.width-stage.stageWidth;
}
}

Endloses Scrollen von 360°-Panoramen mit variabler Geschwindigkeit

In diesem Beispiel kann man irgendwo die Bühne anklicken und den Mauszeiger bei gedrückter Maustaste nach links und rechts bewegen. In Abhängigkeit von der Entfernung des Mauszeigers vom ursprünglichen Anklickpunkt, rotiert das Panorama in unterschiedlicher Geschwindigkeit: je weiter weg vom Anklickpunkt desto schneller.

 

Skript (panorama360_speedChange.fla):

var nullPunkt:int;
var schrittweite:int;

this.stage.addEventListener(MouseEvent.MOUSE_DOWN,startScrollen);
function startScrollen(e:MouseEvent):void {
nullPunkt=e.stageX;
schrittweite=0;
this.stage.addEventListener(MouseEvent.MOUSE_MOVE, scrollen);
panorama_mc.addEventListener(Event.ENTER_FRAME, rotierePanorama);
}
function scrollen(e:MouseEvent):void {
schrittweite=e.stageX-nullPunkt;
}
function rotierePanorama(e:Event) {
e.target.x+=schrittweite;
if (e.target.x+e.target.width < stage.stageWidth) {
e.target.x+=e.target.width-stage.stageWidth;
} else if (e.target.x > 0) {
e.target.x-=e.target.width-stage.stageWidth;
}
}

this.stage.addEventListener(MouseEvent.MOUSE_UP, stopScrollen);
function stopScrollen(e:MouseEvent):void {
this.stage.removeEventListener(MouseEvent.MOUSE_MOVE, scrollen);
panorama_mc.removeEventListener(Event.ENTER_FRAME, rotierePanorama);
}

 
Erläuterungen zum Skript

In der Variablen nullPunkt wird die x-Koordinate des Anklickpunktes gespeichert. Damit kann man immer die aktuelle Distanz des Mauszeigers vom Anklickpunkt ermitteln. Daraus wird die aktuelle Scrollgeschwindigkeit berechnet, die in der Variablen schrittweite gespeichert wird.

var nullPunkt:int;
var schrittweite:int;


Beim MOUSE_DOWN-Ereignis auf der Bühne wird der Eventhandler startScrollen aufgerufen.
In diesem wird durch nullPunkt=e.stageX; die x-Koordinate des Mauszeigers relativ zur Bühne als Bezugspunkt gespeichert und die Schrittweit wird auf 0 gesetzt. D.h. nur durch einen Klick wird noch keine Bewegung ausgelöst.

this.stage.addEventListener(MouseEvent.MOUSE_DOWN,startScrollen);
function startScrollen(e:MouseEvent):void {
nullPunkt=e.stageX;
schrittweite=0;

 
Abschließend wird im Eventhandler startScrollen der Eventhandler scrollen für das Bewegen der Maus aktiviert und der ENTER_FRAME-Eventhandler rotierePanorama, der wieder, wie im vorhergehenden Kapitel, für die Rotation des Panoramas zuständig ist.

	this.stage.addEventListener(MouseEvent.MOUSE_MOVE, scrollen);
panorama_mc.addEventListener(Event.ENTER_FRAME, rotierePanorama);
}
 

Im MOUSE_MOVE-Eventhandler scrollen wird nur der x-Abstand zwischen Mauszeiger und Anklickpunkt berechnet und als Schrittweite abgespeichert.
Anmerkung: Hier könnte man ausgehend vom Abstand für die Schrittweite auch komplexere Formeln verwenden, die nicht 1:1 den Pixelabstand in die Schrittweite überführen.

function scrollen(e:MouseEvent):void {
schrittweite=e.stageX-nullPunkt;
}
 

Im ENTER_FRAME-Eventhandler rotierePanorama wird das Panorama um die Schrittweite verschoben und anschließend geprüft, ob einer der Panoramaränder bereits in der Bühne liegt.
Wenn das der Fall ist, wird das Panorama um (Panoramabreite - Bühnenbreite) verschoben.

function rotierePanorama(e:Event) {
e.target.x+=schrittweite;
if (e.target.x+e.target.width < stage.stageWidth) {
e.target.x+=e.target.width-stage.stageWidth;
} else if (e.target.x > 0) {
e.target.x-=e.target.width-stage.stageWidth;
}

 
Beim MOUSE_UP-Ereignis werden die Eventhandler scrollen und rotierePanorama wieder deaktiviert.

this.stage.addEventListener(MouseEvent.MOUSE_UP, stopScrollen);
function stopScrollen(e:MouseEvent):void {
this.stage.removeEventListener(MouseEvent.MOUSE_MOVE, scrollen);
panorama_mc.removeEventListener(Event.ENTER_FRAME, rotierePanorama);
}

Scrollen und Ziehen mit einem Navigator

 

Da in dem vorgegebenen Container immer nur ein Ausschnitt des Bildes gezeigt wird, ist es sinnvoll, in einer kleinen Darstellung daneben, das ganze Bild darzustellen und den gerade sichtbaren Ausschnitt extra hervorzuheben. Dadurch erhält man eine bessere Vorstellung von dem Detail in Relation zum Ganzen. Aus dem Photoshop und anderen Grafikprogrammen kennt man den Navigator, der auch zum Wählen des Ausschnitts verwendet werden kann.

Im nachfolgenden Beispiel kann über das kleine Rechteck im Navigator der Ausschnitt im ScrollPane-Container verschoben werden. Wenn man im Container den Inhalt scrollt oder zieht wird umgekehrt im Navigator das Ausschnittsrechteck entsprechend verschoben.
 

Skript (scrollpane_navigator.fla)

Das nachstehende Skript wurde der Adobe-Hilfe für ScrollPane entnommen und überarbeitet.
Die Grenzen werden exakter berechnet und es wird an Stelle von startDrag ein MOUSE_MOVE-Eventhandler verwendet, der unmittelbarer auf die Mausbewegungen reagiert.

Die Erklärungen sind direkt als Kommentar zum Skript hinzugefügt.

import flash.events.*;
import flash.geom.*;
import fl.containers.ScrollPane;
import fl.events.ScrollEvent;

// globale Variablen festlegen
var sampleImagePath:String="http://www.dma.ufg.ac.at/assets/23033/intern/linz_altstadt.jpg"; // URL des Bildes
var sp:ScrollPane; // ScrollPane-Container für das Bild
var previewWindow:Sprite=new Sprite(); // Navigatorfenster
var previewWindowSize:Number=100; // Breite des Navigatorfensters
var previewPositioner:Sprite; // Ausschnittsrechteck im Navigator
var deltaX:Number; // speichert den x-Versatz zur x-Mausposition des Ausschnittsrechtsecks beim Ziehen
var deltaY:Number; // speichert den y-Versatz zur y-Mausposition des Ausschnittsrechtsecks beim Ziehen
var margin:Number=10; // Randbreite zur Bühne

// Scrollpane-Container erzeugen
sp=new ScrollPane();
sp.move(margin,margin);
// festlegen der Größe mit den Rändern
sp.setSize(stage.stageWidth-previewWindowSize-3*margin,stage.stageHeight-2*margin);
sp.source=sampleImagePath;
// nach dem Laden des Inhalts wird der Navigator erzeugt
sp.addEventListener(Event.COMPLETE,createPreviewWindow);
// Eventhandler, der laufend beim Scrollen und Ziehen des Inhalts aufgerufen wird
sp.addEventListener(ScrollEvent.SCROLL,repositionPreview);
sp.scrollDrag=true;// das Ziehen wird ermöglicht
addChild(sp);

// erstellen des Navigators
function createPreviewWindow(e:Event):void {
previewWindow=new Sprite();
// positionieren
previewWindow.x=sp.width+2*margin;
previewWindow.y=margin;
// weiße Füllung, schwarze Kontur
previewWindow.graphics.beginFill(0xFFFFFF);
previewWindow.graphics.lineStyle(1,0,1);
// die Breite ist vorgegeben, die Höhe errechnet sich aus der Proportion des geladenen Bildes
previewWindow.graphics.drawRect
(0,0,previewWindowSize,Math.ceil(previewWindowSize*sp.content.height/sp.content.width));
addChild(previewWindow);

// Bitmap für die verkleinerte Gesamtansicht des Bildes
var bitmapData:BitmapData;
bitmapData=new BitmapData(sp.content.width,sp.content.height); // Größe wird festgelegt
bitmapData.draw(sp.content); // der Inhalt des Containers wird in die bitmapData gezeichnet
var bitmap:Bitmap=new Bitmap(bitmapData); // mit der bitmapData wird eine Bitmap angelegt
// festlegen der Bitmapgröße in der Größe des Navigators
bitmap.width=previewWindow.width;
bitmap.height=previewWindow.height;
bitmap.alpha=0.5; // Deckkraft reduzieren
previewWindow.addChild(bitmap); // die Bitmap ist ein Teil des Navigators

// Ausschnittsrechteck im Navigator erstellen
previewPositioner=new Sprite();
previewPositioner.graphics.beginFill(0xFFFFFF,0.5);
previewPositioner.graphics.lineStyle(1,0,0.5);
// Größe des Ausschnittrechtsecks berechnen
// getHorizontalAspect() und getVerticalAspect() liefern das Verhältnis
// von ScrollPane-Containerbreite/höhe und der Breite/Höhe des Inhalts
previewPositioner.graphics.drawRect
(0,0,getHorizontalAspect()*previewWindow.width,getVerticalAspect()*previewWindow.height);
// bei MOUSE_DOWN soll das Ausschnittsrechteck verschiebbar werden
previewPositioner.addEventListener(MouseEvent.MOUSE_DOWN,dragPreviewPositioner);
// bei MOUSE_UP wird die Verschiebemöglichkeit beeendet
previewPositioner.addEventListener(MouseEvent.MOUSE_UP,dropPreviewPositioner);
// positionieren in der linken oberen Ecke des Navigators
previewPositioner.x=previewWindow.x;
previewPositioner.y=previewWindow.y;
// zur Displaylist des Mainmovies hinzufügen
addChild(previewPositioner);
}

// ziehen des Ausschnittrechtecks ermöglichen
function dragPreviewPositioner(e:MouseEvent):void {
// speichern des Versatzes zw. Mausposition beim Klick und der linken, oberen Ecke des Ausschnittrechtecks
deltaX=e.stageX-previewPositioner.x;
deltaY=e.stageY-previewPositioner.y;
// Eventhandler aktivieren,
// der bei MOUSE_MOVE das Ausschnittsrechteck und den Inhalt des ScrollPane-Containers aktualisiert

previewPositioner.addEventListener(MouseEvent.MOUSE_MOVE,repositionScrollPane);
}

// ziehen des Ausschnittrechtecks beenden
function dropPreviewPositioner(e:MouseEvent):void {
previewPositioner.removeEventListener(MouseEvent.MOUSE_MOVE,repositionScrollPane);
}

// Aktualisierung von Ausschnittsrechteck und Inhalt des ScrollPane-Containers
function repositionScrollPane(e:MouseEvent):void {
// neue Position des Ausschnitts durch die aktuelle Mausposition und den gespeicherten Versatz festlegen
previewPositioner.x=e.stageX-deltaX;
previewPositioner.y=e.stageY-deltaY;
// prüfen, ob das Ausschnittsrechteck die Navigatorränder überschreitet
// wenn ja, Auuschnittsrechteck an dem entsprechenden Navigatorrand ausrichten
// zuerst für die x-Richtung ...

if (previewPositioner.x < previewWindow.x) {
previewPositioner.x=previewWindow.x;
} else if (previewPositioner.x > previewWindow.x+previewWindow.width-previewPositioner.width) {
previewPositioner.x=previewWindow.x+previewWindow.width-previewPositioner.width;
}
// ... anschließend für die y-Richtung
if (previewPositioner.y < previewWindow.y) {
previewPositioner.y=previewWindow.y;
} else if (previewPositioner.y>previewWindow.y+previewWindow.height-previewPositioner.height) {
previewPositioner.y=previewWindow.y+previewWindow.height-previewPositioner.height;
}
// Inhalt im ScrollPane-Container aktualisieren
// die horzontale Scrollposition ist ein Bruchteil der maximalen horizontalen Scrollposition

sp.horizontalScrollPosition = (previewPositioner.x-previewWindow.x)/
(previewWindow.width-previewPositioner.width)*sp.maxHorizontalScrollPosition;
// die vertikale Scrollposition ist ein Bruchteil der maximalen vertikalen Scrollposition
sp.verticalScrollPosition = (previewPositioner.y-previewWindow.y)/
(previewWindow.height-previewPositioner.height)*sp.maxVerticalScrollPosition;
e.updateAfterEvent(); // Bühne aktualisieren
}

// Aktualisierung des Ausschnittrechtecks im Navigator, wenn der Inhalt des ScrollPane-Containers verschoben wird
function repositionPreview(e:ScrollEvent):void {
previewPositioner.x = previewWindow.x + (previewWindow.width-previewPositioner.width)*
(sp.horizontalScrollPosition/sp.maxHorizontalScrollPosition);
previewPositioner.y = previewWindow.y + (previewWindow.height-previewPositioner.height)*
(sp.verticalScrollPosition/sp.maxVerticalScrollPosition);
}

// Hilfsfunktionen für die Berechnung der Größe des Ausschnittrechtecks im Navigator

// getHorizontalAspect() liefert das Verhältnis von ScrollPane-Containerbreite und der Breite des Inhalts
function getHorizontalAspect():Number {
return sp.width/sp.content.width;
}
// getVerticalAspect() liefert das Verhältnis von ScrollPane-Containerhöhe und der Höhe des Inhalts
function getVerticalAspect():Number {
return sp.height/sp.content.height;
}