Sie sind auf Seite 1von 14

Step 1

Set up a new Flash AS3 document and a blank ActionScript file. Well create the main class first; name it Gallery and save it as Gallery.as. Below is the code to set up the first class:
package { public class Gallery { public function Gallery() : void { } } }

In Flash we can now set this as the base class by entering the name in the input box under the Publish settings of the document. You can try a trace(hello world) in the Gallery function to test if its working.

Step 2
Before we continue with the main class, its best to start off with the slider. The code is fairly easy to understand and might shine some light on how things are done in AS3. Create two new movieclips: a slider and a handle for the slider. The slider or seeker doesnt need to be exported for ActionScript as we only need to apply some functions to the handle. Set the class for the handle as Handle. We can now create a new class called Handle and, if saved in the same directory, Flash will automatically use this class for the handle. Give both an instance name such as slider and theHandle.

Step 3
The following is the full code for the handle which I will explain in steps. It should be saved as Handle.as in the same directory as your .fla. This way, Flash will execute the code once an instance with a class name Handle is used.
package { import flash.display.MovieClip; import flash.events.MouseEvent; import flash.events.Event; public class Handle extends MovieClip { public var goToX : Number = x; private var slider : MovieClip = MovieClip(parent).slider; private var mousePos : Number = 0; public function Handle() : void { buttonMode = true; addEventListener( MouseEvent.MOUSE_DOWN, moveHandle ); stage.addEventListener( MouseEvent.MOUSE_UP, stopHandle ); } private function moveHandle( _e : MouseEvent ) : void { mousePos = mouseX; stage.addEventListener( MouseEvent.MOUSE_MOVE, followHandle ); } private function stopHandle( _e : MouseEvent ) : void {

stage.removeEventListener( MouseEvent.MOUSE_MOVE, followHandle ); } private function followHandle( _e : MouseEvent ) : void { var newPos : Number = stage.mouseX - mousePos; var orgX : Number = x; if ( newPos < slider.x ) goToX = slider.x; else if ( newPos > (slider.x + slider.width) width ) goToX = (slider.x + slider.width) width; else goToX = newPos; x = goToX; if( goToX != orgX ) dispatchEvent( new Event( "sliding", true ) ); } } }

In the first few lines we create a few variables to hold data we can use in every function in this class. The slider variable holds a reference to the instance called slider on the parent. We need it to correctly calculate the x position to which the handle should be moved. In the constructor we set buttonMode to true so a hand cursor shows up when hovering over the handle. Additionally, we add two eventlisteners to listen for mouse events.
public class Handle extends MovieClip { public var goToX : Number = x; private var slider : MovieClip = MovieClip(parent).slider; private var mousePos : Number = 0; public function Handle() : void { buttonMode = true; addEventListener( MouseEvent.MOUSE_DOWN, moveHandle ); stage.addEventListener( MouseEvent.MOUSE_UP, stopHandle ); }

Once a mouse down event occurs, an extra listener is added. This listener stays active as long as the drag movement isnt stopped and calls the followHandle function. Its removed again when the mouse click is over.
private function moveHandle( _e : MouseEvent ) : void { mousePos = mouseX; stage.addEventListener( MouseEvent.MOUSE_MOVE, followHandle ); } private function stopHandle( _e : MouseEvent ) : void {

stage.removeEventListener( MouseEvent.MOUSE_MOVE, followHandle ); }

This last function actually moves the handle around. The variable newPos stores the new position to which the handle should move. If, however, this position is further than the far left or right of the slider, the position should be set to the maximum possible value. If the handle is moved, we dispatch a new custom event called sliding, which we can later use to move around the images.
private function followHandle( _e : MouseEvent ) : void { var newPos : Number = stage.mouseX - mousePos; var orgX : Number = x; if ( newPos < slider.x ) goToX = slider.x; else if ( newPos > (slider.x + slider.width) - width ) goToX = (slider.x + slider.width) - width; else goToX = newPos; x = goToX; if( goToX != orgX ) dispatchEvent( new Event( "sliding", true ) ); }

Step 4
If everythings gone well until now, you should have a nice functional slider like the one below. Add a dynamic textfield underneath it which will hold the image number. Give it an instance name like countTxt so we can address it later in ActionScript. Because theres nothing to display yet I filled it with the text Loading which will also display while the script loads the first image.

Step 5
Next well create the php backend script. Flash cant read the contents of a local directory, so we need to pass the information from php to Flash. Well use XML since its easy to output with php and even easier to read again in AS3. The following is the php backend code, save it as backend.php. The code loops through the directory img and writes a line of XML for each file in there. Before printing we need to filter out the . and .. directories. As the directory only contains images theres no further checking necessary.
<xml> <?php $od = opendir('img'); while ( $filename = readdir($od) ) if( $filename != "." && $filename != ".." ) echo "<img>" . $filename . "</img>\n";

?> </xml>

This will output for example:


<xml> <img>file1.jpg</img> <img>file2.jpg</img> <img>file3.jpg</img> </xml>

Step 6
Before we load this into Flash, lets create a class to hold our images individually. In the same way we created the Handle class we can now create an Img class. Start off with creating a movieclip about the size you want your images to be displayed. Give it a margin of a few pixels and save some space on the bottom for the description text. Add a dynamic text field for the description and give it an instance name of descr. Make sure to set the registration point of the movieclip to the center so we can easily scale it later. Export it for ActionScript under the class name Img. Now delete the instance from the stage as well call it directly from the library.

Step 7
Next well load the information the php file returns into our Flash project using ActionScript. Open up the Gallery class again. The following code adds two

functionalities to our project. First off, it creates imagesClip, an empty movieclip in which well store the actual images later. Using addChild here adds the movieclip to the stage. Anything added to imagesClip later will also appear on the stage. To actually load the xml data, we create a URLLoader. This class can fetch the results and run a function when the results are in.
package { import import import import flash.display.MovieClip; flash.events.Event; flash.net.URLLoader; flash.net.URLRequest;

public class Gallery extends MovieClip { private var backend : String = 'http://localhost/.../backend.php'; private var xmlLoader : URLLoader = new URLLoader; private var xdata : XML; public var images : Array = new Array(); public var imagesClip : MovieClip = new MovieClip; public function Gallery() : void { imagesClip.y = 180; addChild( imagesClip ); xmlLoader.load( new URLRequest( backend + "?" + new Date().valueOf() ) ); xmlLoader.addEventListener( Event.COMPLETE, loadImages ); } private function loadImages( _e : Event ) : void { xdata = new XML( _e.target.data ); var i : Number = 0; for each( var img:XML in xdata.img ) { images[i] = new Img(); i++; imagesClip.addChild( images[i] ); } } } }

Here we use the load function of the URLLoader class. To prevent caching of the results we can add a simple date to the end of the url. The eventlistener checks when the URLLoader is finished and then runs the loadImages function.
xmlLoader.load( new URLRequest( backend + "?" + new Date().valueOf() ) ); xmlLoader.addEventListener( Event.COMPLETE, loadImages );

This next function loops through all the <img> instances in the xml. For each of these it creates a new instance of the Img class. Next we add it to the imagesClip (this is just for testing as later on we only want the active images to be displayed).
private function loadImages( _e : Event ) : void { xdata = new XML( _e.target.data ); var i : Number = 0; for each( var img:XML in xdata.img ) { images[i] = new Img(); i++; imagesClip.addChild( images[i] ); } }

Step 8
To give our Img instances more functionality, create an Img class and save it as Img.as. In a similar way to loading the XML we can load the image itself and display it inside the Img movieclip. The loading should not occur in the constructor or all images would try to load at the same time; well create a separate function for doing this.
public class Img extends MovieClip { public var id : Number; private var src : String; private var imageLoader:Loader = new Loader(); private var main : Gallery; private var orgWidth:Number = 0; private var orgHeight:Number = 0;

In the constructor, we set a reference to the main Gallery class so we can later access elements on the stage or public variables and functions of the main class. The string load will contain the path to the image which php returned, well save it to a variable so we can access it later.
public function Img( load : String, m : Gallery ) : void { orgWidth = width; orgHeight = height; main = m; src = load; }

The loadImage function loads the image and when finished runs the displayImage function.
public function loadImage() : void { imageLoader.load( new URLRequest( "img/" + src ) ); imageLoader.contentLoaderInfo.addEventListener( Event.COMPLETE, displayImage );

The displayImage function checks the images array we created and loads the next image. It sets the smoothing to true on the Loader (by default, smoothing is set to false on dynamically loaded images). Once you start scaling or rotating an image its important to set the smoothing so the image remains its quality. As the registration point of the Img movieclip is in the center we need to calculate the x and y position of where to place the image itself. In my example Ive used a directory of images with the same width and height. If the width and height of the loaded image are variable this is the place to resize it on the fly. Just before adding it as a child we set the description text to src, which holds the name of the image.
private function displayImage( _e : Event ) : void { if ( main.images[id + 1] != null && !main.images[id + 1].parent ) main.images[id + 1].loadImage(); Bitmap( imageLoader.content ).smoothing = true; imageLoader.x = main.spaceBetween/2 - ( orgWidth /2 ); imageLoader.y = main.spaceBetween/2 - ( orgHeight /2 ); descr.text = src; addChild( imageLoader ); } }

Step 9
After the changes we made to the Img class we need to update the way the instances are called in the loadImages function of the Gallery class. We now need to pass two arguments when calling new Img(). The first is the path name of the image that needs to be loaded, we get this from the xml. The second is a reference to the main Gallery class; we can use this which points to the class were currently working in. Instead of adding the images with addChild to the imagesClip container well create a new function goTo. This function will work out which images to place on the screen. The argument we need to pass is the id number of the image, the same number as the index key in the images array. When the images are loaded for the first time, well set the focus on the first image, of which the id number is zero.
private function loadImages( _e : Event ) : void { xdata = new XML( _e.target.data ); var i : Number = 0; for each( var img:XML in xdata.img ) { images[i] = new Img( img, this ); images[i].x = 200 * i; images[i].id = i; i++; } goTo( 0 ); }

Step 10
To use the goTo function we need to declare a variable imagesToShow first. This will set the amount of images we want to load at once on screen. To determine the direction in which the user is scrolling, we simply check if the image were going to had a higher or lower id number than the last one.
private function goTo( imgId : Number ) : void { var direction : Number; if ( orgImgId != imgId ) { if ( imgId > orgImgId ) direction = 1; else direction = -1;

The next for loop loops all images needed on screen. For example: if we set imagesToShow to 5, it will loop from -2 to 2. This means that if we pass the value of i to the Img class we can determine where on the screen it should be positioned (-2 being far left, 0 center and 2 the far right). Therefore, we can scale the images larger the more centrally they are positioned. Theres an additional check included so we dont activate non-existing images (it stops at the first and the last one). For each of the active images well run the makeActive function, which well create later on.
for ( var i : Number = - Math.floor(imagesToShow/2); i <= Math.floor(imagesToShow/2); i++ ) { if( imgId + i < images.length && imgId + i >= 0 ) images[imgId + i].makeActive( i, direction ); }

Right after placing the images we need on screen, well check which ones shouldnt be there any more and get those off the stage. Since all images are added to the imagesClip container, we can easily loop through all children of that movieclip. If their id is not within those that should be active, we run the deActive.
for( var j : Number = 0; j < imagesClip.numChildren; j++ ) { var tile : Img = imagesClip.getChildAt(j) as Img; if ( tile.id < imgId Math.floor(imagesToShow/2) || tile.id > imgId + Math.floor(imagesToShow/2) ) tile.deActive( direction ); }

The next line updates the text of the dynamic textfield we created earlier. Since the ids of the images start their count at 0 we add + 1 to the imgId so the first image is actually number 1 etc. We can get the total number of images from accessing the length of the images array.
countTxt.text = imgId + 1 + "/" + images.length; }

Finally, well set orgImgId so the next time the function is run, the direction being scrolled can be determined.
orgImgId = imgId; }

Step 11
We now need the makeActive and deActive functions in the Img class. These will either add the image to the stage, or take it off. For now well just add them and place them correctly. Later on well also tween them to their correct place. The makeActive function first checks if it is added to the imagesClip already. If there is no parent found, it adds itself to to imagesClip container. The parent is then the imagesClip. Next we set the visible property to true. When deactivating, we set it to false so its only normal we want our image to show again when its made active.
public function makeActive( position : Number, direction : Number ) : void { if ( parent == null ) { main.imagesClip.addChild( this ); } visible = true;

Theres a chance that the image itself hasnt yet loaded. To check this, I check if the amount of children is smaller than 3. This number can depend on the amount of shapes or other movieclips in your Img. If you feel uncertain about this method, a safer option would be to declare a boolean at the start and set it to true in the displayImage function.
if ( numChildren < 3 ) loadImage();

Theres no depth in AS3, but once we start scaling and rotating our images we need to make sure the image in the center is on top of the others. Because we passed the position as an argument in the goTo function we can now use it to set the index of the image in the imagesClip. The index of a child can be compared to a depth, but there wont be any issues when changing it since the other movieclips will move an index up or down. This step is unnecessary if you dont plan to overlap the images.
parent.setChildIndex( this, ( parent.numChildren-1 ) Math.abs( position ) );

Lastly, we determine the position of the image. The extra variable is used here to find out by how much the current image is away from the center. DefaultWidth and spaceBetween are public variables set in the main Gallery class so we can access them from anywhere. Since all the images in my directory have the same width, I set defaultWidth at 195 and spaceBetween to 20. To actually move the image to the new position we set the x property to the newly found x value.

var extra : Number = Math.round( position * ( main.defaultWidth + main.spaceBetween ) ); var newX : Number = Math.round( ( main.stageWidth / 2 ) ) + extra; x = newX; }

Step 12
The deActive function is pretty straight forward, it changes the visibility to false. The direction is already set as an argument since well need it later to know in which direction to send the image when taking it off the stage.
public function deActive( direction : Number ) : void { visible = false; }

Step 13
By now, the first few images should appear on stage. Theres only one functionality still missing. The slider is not connected to the goTo function yet. However, since we already dispatch a custom event once the handle is dragged, its not very hard to connect the two. Add the following line to the Gallery constructor function. This eventlistener will run the slide function every time the sliding event is called by the handle.
theHandle.addEventListener( "sliding", slide );

All we need the slide function to do is to calculate which image should be shown in the center depending on where the handle is. In the function slider and theHandle are the instance names we set earlier on the movieclips on the stage. To find out which image to go to we first determine the percentage of the handles position over the sliders length. Then, multiplying that by the total of images brings us to the right image id.
private function slide( _e : Event ) : void { var percent : Number = ( theHandle.goToX - slider.x ) / ( slider.width - theHandle.width ); var imgId : Number = Math.round( percent * ( images.length - 1 ) ); goTo( imgId ); }

Step 14

If youve managed to follow this far and kept track of which classes to import and which variables to declare (or followed the source files) you should by now have a working example.

Step 15
To finish this tutorial well add tweening to the images using TweenLite, a free and lightweight tweening engine. The standard tweening classes provided by Adobe do not perform well when theres a lot going on. When trying those out they used to crash or freeze a lot, while I have yet to discover such problems with TweenLite. The syntax of TweenLite is very easy. Ill demonstrate this by comparing it to a normal Flash tween; tweening an object from its current x to 100 and changing the alpha to 0:
new Tween( object, 'x', Linear.easeOut, object.x, 100, 2, true ); new Tween( object, 'alpha', Linear.easeOut, object.alpha, 0, 2, true );

Now heres the TweenLite alternative syntax:


TweenLite.to( object, 2, { x:100, alpha:0, easing:Linear.easeOut } );

Step 16
Lets create a new function in which we can place all of the tweening actions. Name it flyTo and place it in the Img class.
private function flyTo( newX : Number, removeAfter : Boolean, scale : Number = 1 ) : void { var tweeningOptions : Object = new Object; tweeningOptions.x = newX; if ( removeAfter ) { tweeningOptions.ease = Linear.easeIn; tweeningOptions.alpha = 0; tweeningOptions.scaleX = tweeningOptions.scaleY = 0.3; tweeningOptions.visible = false; } else { tweeningOptions.ease = Back.easeOut; tweeningOptions.alpha = 1; tweeningOptions.scaleX = tweeningOptions.scaleY = scale; tweeningOptions.rotation = (Math.random() * 20) - 10; } TweenLite.to ( this, 0.4, tweeningOptions ); }

There are 3 arguments needed for this function: the x value we calculated earlier, whether it should be removed after the tween and the scale. The removedAfter parameter will be used in the deActive function, so the visibility of the image can be set to false once its reached the end of the stage. The scaling parameter is only used for the center image; well display it slightly larger than the rest. Lets check out the tweening options for when the image is removed from the stage. First we choose an easing option, in this case Linear.easeIn which gives normal, linear movement. Secondly, we fade the alpha value to zero so the image fades away. Then we scale it to only a small percentage of its width and height so it becomes smaller as it reaches the end. Lastly, when the tween is completed, we set the visibility to false.
tweeningOptions.ease = Linear.easeIn; tweeningOptions.alpha = 0; tweeningOptions.scaleX = tweeningOptions.scaleY = 0.3; tweeningOptions.visible = false;

When an image is made active, the tweening options are different. We set the easing to Back.easeOut so the images seem to fly slightly too far and then bounce back; a very subtle effect. Next we change the alpha back to 1 and scale the image to the scale value we passed to the function. Lastly, we set a random rotation of -10 to 10 degrees. If your description text fails to load after this action you need to make sure the font is embedded.
tweeningOptions.ease = Back.easeOut; tweeningOptions.alpha = 1; tweeningOptions.scaleX = tweeningOptions.scaleY = scale; tweeningOptions.rotation = Math.round( (Math.random() * 20) - 10 );

Step 17
Now we need to update the makeActive and deActive functions so they make use of the new flyTo function. In the makeActive function we need to add an x value to the image so it can be tweened from that original value. The defaultWidth needs to be set in the main Gallery class and contains the width of the stage.
public function makeActive( position : Number, direction : Number ) : void { deactivating = false; if ( parent == null ) { x = ( direction == 1 ? main.stageWidth + main.defaultWidth * 2 : - main.defaultWidth * 2 ); alpha = 0; main.imagesClip.addChild( this ); } visible = true; if ( numChildren < 3 ) loadImage(); parent.setChildIndex(this, ( parent.numChildren-1 ) - Math.abs( position ) );

var extra : Number = Math.round( position * ( main.defaultWidth + main.spaceBetween ) ); var newX : Number = ( Math.round( ( main.stageWidth / 2 ) /* ( main.defaultWidth / 2 )*/ ) + extra ); flyTo( newX, false, ( position == 0 ? 1.2 : 1 ) ); }

In the deActive function all we need to add is a moveTo value which contains the x value to which the image should tween. If we locate this to outside the stage width, the image will disappear just outside the stage.
public function deActive( direction : Number ) : void { if ( ! deactivating ) { active = false; var moveTo : Number = ( direction != 1 ? main.stageWidth + main.defaultWidth * 2 : parent.x - main.defaultWidth * 2 ); flyTo( moveTo, true ); deactivating = true; } }

Step 18
After updating everything, we now have a functional gallery. To see the movieclips disappearing from the stage check out the full version.

Conclusion
The final version is still low on functionality. The loaded images all need to be the same size and do not have a larger preview. You could add a resizing function and an option to view a larger preview of the image when clicked. Furthermore, an option to browse left or right with buttons or with a keyboard event could be added. Once you understand the concept of using classes to separate and structure your code it will help in development and make things easier in the long run. I hope you enjoyed following along!

Das könnte Ihnen auch gefallen