21 Feb 2010 - Posted in ActionScript

Build an interactive Polaroid Gallery with Actionscript 3 and the Flickr API

preview
In this tutorial I explain how to create a Polaroid style Flickr gallery using the Flickr API and then how to apply user actions such as dragging and resizing and to those images.

Get SourceSee Demo

Step 1: Getting Started

To begin with, open Flash and create a new Actionscript 3.0 Flash file.

pic1

Set the stage to 800×500 and then create a new Actionscript file.

Step 2: The Libraries

In this tutorial we’ll be using the as3flickrlib library to interface with the Flickr API. You can download it here and you’ll also need the as3corelib library as as3flickrlib is dependent on that. You can download that here

Step 3: Setting Up

Once you’ve downloaded the libraries, you’ll need to make them accessible to Flash. To do this, go into preferences and then click on the Actionscript category and then Actionscript 3.0 settings. Here you’ll be able to add a new source path which you’ll need to set the location of the libraries you’ve just downloaded.

Step 4: Begin Coding

Start a package and begin by importing the following classes.

package
{
  import flash.display.*;
  import flash.events.*;
  import flash.net.URLRequest;
  import flash.filters.DropShadowFilter;
  import flash.geom.Point;
  import flash.system.Security;
  import com.adobe.webapis.flickr.*;
  import com.adobe.webapis.flickr.events.*;

Step 5: The Class

Start a class that extends Sprite and name it something like FlickrGallery, make sure to give the Actionscript file the same name when you save it.

public class FlickrGallery extends Sprite
{

Step 6: The Variables

Some of these variables can be changed at your discretion such as the minScale and maxScale variables, the maxWidth and maxHeight variables.

var xPosition:int = 0; //The X position of an image
	var yPosition:int = 0; //The Y position of an image
	var prevHeight:int; //Store the height of the last image to be loaded
	var imageCounter:int = 0; //Iterate each time an image is added
	var minScale:Number = 0.3; //The minimum scale boundary for images
	var maxScale:Number = 2.5; //The maximum scale boundary for images
	var totalResults:int = 15; //The number of results you want to be pulled from Flickr
	var api_key:String = ""; //your Flickr API key
	var searchQuery:String = "paris"; //the search criteria, change this to what you want
	var user:String = ""; //optional, the NSID of a particular Flickr user
	var photoCollection:PagedPhotoList; //the collected images
	var imageIndex:int = 0; //the image index for the array
	var displayPic:Bitmap = null; //the bitmap that the images will be assigned to
	var dShadow:DropShadowFilter = new DropShadowFilter(); //the shadow that will be applied to the images
	var targetImage:DisplayObject; //the target image for scaling
	var maxWidth:int = 150; //the maximum width that the images will be initialy displayed at
	var maxHeight:int = 200; //the maximum height that the images will be initially displayed at
	var xyPoints:Point; //the x and y points of an image when scaling
	var xyDistance:Number; //the distant between the x and y points and the mouse
	var imagesReturned:int = 0; // the number of images returned

Step 7: Policy Files

You’ll need to load Flickr’s cross domain policy files to enable you to load the images from Flickr in your own domain.

	Security.loadPolicyFile("http://farm1.static.flickr.com/crossdomain.xml");
   	Security.loadPolicyFile("http://farm2.static.flickr.com/crossdomain.xml");
   	Security.loadPolicyFile("http://farm3.static.flickr.com/crossdomain.xml");
   	Security.loadPolicyFile("http://farm4.static.flickr.com/crossdomain.xml");
	Security.loadPolicyFile("http://farm5.static.flickr.com/crossdomain.xml");

Step 8: The Constructor

Here we’ll declare the constructor; it will need to be named the same as the class. In this case FlickrGallery.

	public function FlickrGallery() {
	  dShadow.distance = 5;
	  dShadow.angle = 90;
	  dShadow.alpha = 0.5;
	  var service:FlickrService = new FlickrService(api_key);
      	  service.addEventListener(FlickrResultEvent.PHOTOS_SEARCH, handleResults);
          service.photos.search(user, searchQuery, "any", "", null, null, null, null, -1, "", totalResults, 1);
    }

It starts by setting the distance, angle and alpha of the drop shadow that will be applied to the images. After that a new instance of the FlickrService class is created and then an event listener is attached to the Flickr search which will call the handleResults function when Flickr returns the search results.

Step 9: Handle the Results

Following on from the Flickr search in the constructor, the handleResults function starts by using an if statement to check if the search was successful. If it was, then the returned photos are placed in the photoCollection array, the number of images is assigned to the imagesReturned variable and the loadImages function is called.

	private function handleResults(event:FlickrResultEvent):void {
		if(event.success) {
			photoCollection = event.data.photos;
			imagesReturned = photoCollection.photos.length;
			loadImages();
	  } else {
		  trace("Failed to load images.");
	  }
    }

Step 10: Load the Images

The loadImages function starts by forming the URL of image to be loaded and then loading it using the URLReequest object and a Loader object. An event listener is then attached to the Loader and is set to call the displayImages function when it completes.

	private function loadImages() {
		var picURL:String = "http://static.flickr.com/" + photoCollection.photos[imageIndex].server + "/"
+ photoCollection.photos[imageIndex].id + "_" + photoCollection.photos[imageIndex].secret + ".jpg";
		imageIndex++;
		var imageRequest:URLRequest = new URLRequest(picURL);
		var loader:Loader = new Loader();
		loader.contentLoaderInfo.addEventListener(Event.COMPLETE,displayImages);
		loader.load(imageRequest);
	}

Step 11: Display the Images

The displayImages function is large and will be broken up across then next few steps. It starts by assigning the content of the event target, i.e. the loaded image, to the displayPic variable which we declared earlier as a Bitmap.

	private function displayImages(event:Event):void {
		displayPic = event.currentTarget.content
		if(displayPic.width>maxWidth) {
			displayPic.height = (displayPic.height/100)*((maxWidth/displayPic.width) * 100);
			displayPic.width = maxWidth;
		}
		if(displayPic.height>maxHeight) {
			displayPic.width = (displayPic.width/100)*((maxHeight/displayPic.height) * 100);
			displayPic.height = maxHeight;
		}

After that the width and height of the image is checked against the maxHeight and maxWidth variables to see if the image size is within the required boundaries. If it’s not, then scaling is performed on the image by assigning the maxWidth or maxHeight to it and then working out the change as a percentage and applying that to the dimension that wasn’t changed.

Step 12: Create the Image Holder

We now need to create the holder for the image which in this case will be a Sprite. First, we create a new Sprite object and apply a colour fill of white to it. Then we draw it as a rectangle with its width and height slightly larger than the image it will be holding.

		var holder:Sprite = new Sprite();
		holder.graphics.beginFill(0xFFFFFF);
		holder.graphics.drawRect(0,0,displayPic.width+10,displayPic.height+30);
		holder.graphics.endFill();
		holder.rotation = (Math.round(Math.random() * (1+4-1)) + 1)*-1;
		holder.name = "im" + imageIndex;
		holder.filters = [dShadow];

Next, a slight rotation is applied to the holder to give the gallery a kind of rough scattered style a Polaroid gallery needs. Here, its minimum rotation is 1 and its maximum is 4 but you can alter these if you like. The holder is then named according to the imageIndex and then a drop shadow filter is applied to it which we set up in the constructor at the beginning.

Step 13: Create the Scale Tab

In this section we create the little tab that goes in the bottom-right corner of the holder, this is what the user drags to resize the image.

		var scaler:Sprite = new Sprite();
		scaler.graphics.beginFill(0xBBBBBB);
		scaler.graphics.drawRect(0,0,10,10);
		scaler.graphics.endFill();
		scaler.buttonMode = true;
		scaler.useHandCursor = true;
		scaler.x = displayPic.width - 2;
		scaler.y = displayPic.height + 17;

Once again we create a new Sprite object and this time fill it with a light grey colour, but you can use whichever colour you like. We then draw it as a rectangle and set its width and height to 10, we apply buttonMode and useHandCursor so that the cursor turns to a hand when a user hovers over it. We then set its x and y positions based on the width and height of the image associated with it. For the purpose of this tutorial I’ve kept this part simple and just used a grey square to represent a resize tab but you could use a more meaningful symbol or graphic.

Step 14: Position the images

Still working within the displayImages function, we now need to set the position that the current image will appear on the stage at. The first image is positioned at 0,0 or the top left corner of the screen, after that the next image is given the same x coordinate but the y coordinate is calculated based on the previous images height and generated randomly somewhere between 90% and 100% of the previous images height. This further adds to the random scattered style of the gallery. Also, the images are currently organized into columns of 3 but you can alter this if you desire. Just change the number where it says if(imageCounter==3).

		if(imageIndex<2) {
			xPosition = 0;
			yPosition = 0;
		} else {
			yPosition = Math.round(Math.random() *
(1+(prevHeight+yPosition)-((prevHeight+yPosition)*0.9)) + ((prevHeight+yPosition)*0.9));
			if(imageCounter==3) {
				xPosition = displayPic.width + xPosition;
				yPosition = 0;
				imageCounter = 0;
			}
		}
		imageCounter++;
		prevHeight = displayPic.height;
		displayPic.x = 5;
		displayPic.y = 5;
		holder.x = xPosition;
		holder.y = yPosition;

After the image positions have been calculated we store the image height in the prevHeight variable for use with the next image and then set the x and y coordinates of the image within the holder, in this case 5,5.

Step 15: Add to Stage

In this section we add the displayPic and scaler to the holder and add the holder to the stage using the addChild method. Event listeners then need to be added to the holder and scaler, targeting the startImageDrag, stopImageDrag and scaleImage functions.

		holder.addChild(displayPic);
		holder.addChild(scaler);
		stage.addChild(holder);
		holder.addEventListener(MouseEvent.MOUSE_DOWN, startImageDrag);
		holder.addEventListener(MouseEvent.MOUSE_UP, stopImageDrag);
		scaler.addEventListener(MouseEvent.MOUSE_DOWN, scaleImage);
		if(imageIndex<imagesReturned) {
			loadImages();
		}
	}

And finally ending the displayImages function with a call to the loadImages function to load the next image, if the imageIndex is less than the imagesReturned.

Step 16: Dragg…

The startImageDrag function is called when a mouse down event is detected on the image holder sprite. It brings the target to the forefront of the display list using the setChildIndex method and then invokes the built-in startDrag method on the target.

	private function startImageDrag(evt:MouseEvent) {
		stage.setChildIndex(stage.getChildByName(evt.target.name),stage.numChildren-1);
		evt.target.startDrag();
	}

Step 17: And Drop

The simplest function of all, stopImageDrag is triggered when a mouse up event is detected on the image holder and invokes the stopDrag method on the target.

	private function stopImageDrag(evt:MouseEvent) {
		evt.target.stopDrag();
	}

Step 18: Prepare for Scaling

The scaleImage function is called when a mouse down event is detected on the scaler sprite. It assigns the target parent, i.e. the image holder, to the targetImage variable and then removes the mouse down event listener from it.

	private function scaleImage(evt:MouseEvent) {
		targetImage = evt.target.parent;
		targetImage.removeEventListener(MouseEvent.MOUSE_DOWN, startImageDrag);
		stage.addEventListener(MouseEvent.MOUSE_MOVE,startImageScale);
		stage.addEventListener(MouseEvent.MOUSE_UP, stopImageScale);
		xyPoints = new Point(targetImage.x,targetImage.y);
		xyDistance = (Point.distance(new Point(mouseX, mouseY),xyPoints))/targetImage.scaleX;
	}

Event listeners are then added to the stage for detecting the starting and stopping of the image resize, these call the startImageScal and stopImageScale functions. After that, the coordinates of the image holder’s position is assigned to the xyPoints point object and the distance between that and the x and y position of the mouse is calculated and assigned to the xyDistance variable. This will be used in the subsequent functions for calculating the resizing of the image.

Step 19: Scale Image

The startImageScale is called when the mouse moves, it calculates a new distance between the mouse x and y positions and the x and y position of the image holder. It then scales the image holder, and thus the image, according to the change in distance calculated by dividing the newDistance variable by the initial xyDistance variable. Checks are then made to see if the scaleX or scaleY of the target are greater than the maxScale or less than the MinScale variables and if they are then those variables are assign to the scaleX and scaleY to stop the image from being expanded or shrunk past the boundaries.

	private function startImageScale(evt:MouseEvent) {
		var newDistance = Point.distance(new Point(mouseX, mouseY), xyPoints);
		targetImage.scaleX = targetImage.scaleY = newDistance/xyDistance;
		if(targetImage.scaleX>maxScale) {
			targetImage.scaleX = targetImage.scaleY = maxScale;
		} else if(targetImage.scaleX<minScale) {
			targetImage.scaleX = targetImage.scaleY = minScale;
		}
	}

Step 20: Stop Scaling

The final function of this project is stopImageScale, it’s called when a mouse up event is triggered from the stage and starts by removing the event listeners on the stage that relate to scaling. It then adds the mouse down event back to the image holder that has just been resized.

	private function stopImageScale(evt:MouseEvent) {
		stage.removeEventListener(MouseEvent.MOUSE_MOVE,startImageScale);
		stage.removeEventListener(MouseEvent.MOUSE_UP,stopImageScale);
		targetImage.addEventListener(MouseEvent.MOUSE_DOWN, startImageDrag);
	}
  }
}

The closing brackets for the class and package also need to be added here.

Conclusion

You can customize this however much you like; there are many things here that can be changed such as the search query, user NSID or the sizes of the images etc.

I hope you found this tutorial useful and thanks for reading.

Post a Comment