Unlimited Plugins, WordPress themes, videos & courses! Unlimited asset downloads! From \$16.50/m

Zoom Into Your Own Canvas of Content

Difficulty:IntermediateLength:LongLanguages:

Some paintings require you take a closer look to see all the details. Now instead of a painting, imagine a blank canvas that can be filled with images and text and dynamic descriptions.

We found this awesome author thanks to FlashGameLicense.com, the place to buy and sell Flash games.

Note: This tutorial has nothing to do with the HTML5 canvas element!

The demo is a viewer application that loads its data from XML. Content is placed on the "canvas" and the user can zoom into the canvas or click on any of the images to center it on the screen. Settings such as page size, background color/texture and other settings are loaded dynamically. As a bonus the user can create multiple pages.

In this tutorial we will be looking at the basic concepts of creating an application like this. It is advisable to keep the source code next to you while reading the tutorial because not every part is explained. Source code is available for use with FlashDevelop or with Flash CS3 Professional and higher.

Note: Opening the Application Locally

Please be aware that before trying to open the application from your hard disk, you will need to have it added to the "trusted locations" list specified in the Global Security Settings panel shown below. Otherwise, no access is granted to grab the images from your hard disk. This is not needed when uploaded to a website as long as the images are located on the same web server.

Step 1: Setting up the XML file

In case you are unfamiliar with XML, check out Dru Kepple's AS3 101: XML. Shown below is the XML data corresponding to one page filled with one image and one text field. Settings specified here apply to all pages. As you can see, creating multiple pages and adding content to them is made easy. To images you can add a title and description which is seen when hovered over with the mouse. Multi-line text be added and size and style can be customized. Every text field uses a custom font found in the .fla file.

Step 2: The Document and Canvas Class

Use the following code to set up our application. It is almost completely generated by FlashDevelop using the "AS3 Project" template. Main is the document class - which is the class first loaded when starting flash - and inside Canvas we are building our application.

And a simplistic version of Canvas. Note that we use a display object container as a parameter to add that extra bit of control to add/remove the Canvas.

In this application we are using the so called BulkLoader: an AS3 library that aims to make loading and managing complex loading requirements easier and faster. When first loading up, we use it to grab the XML file and a background image. The following code shows how to boot up the BulkLoader and add two items to the loading queue. The Complete function is called when all items are loaded.

Step 4: Saving the Loaded Assets

We save the loaded assets inside the class for later use. The BulkLoader provides an easy way to grab the items we need. Just call the function corresponding to the type of object and use the object's ID string. Static variables are used to make the XML data globally available (e.g. accessing a static variable using Canvas.viewerWidth). The path variable specifies where the externally loaded images will be located. Images can only be loaded from the own webserver due to security restrictions.

Step 5: Introducing the Virtual Camera

As you may or may not have guessed, the application uses a virtual camera to extract a portion of the canvas and show to the user. The camera applies scaling and translation to a displayobject container such that only the area under the camera is seen on the screen. The following demonstration gives you a better idea of how it works.

The source code of this demonstration is also included in the archive. Let's explain this example by starting with configurating the settings. We would like the viewer region to be 305 x 230, and the content region - which are the dimensions of the flower image - should try to keep the aspect ratio of the viewer region so it does not deform too much. Our flower is 1000 x 750, which is close enough. In order to show the entire flower in the viewer region, the camera must be the same size as the flower, which is 1000 x 750.

The following code sets the dimension settings. In this example they are hardcoded, but otherwise it is taken from XML.

Next up we create a viewer container.The option scrollRect is used to clip off anything outside a specified rectangle. This is very useful as only a portion of the canvas must be shown when zoomed in. It is a neater solution than for example putting a black border around the application.

To the viewer container we add another Sprite called viewerScroller. The function of viewerScroller is simply to act as a container for anything that must serve as content for the camera. It makes it easier to add multiple items to the content region.

Add content to the viewerScroller. In this case the flower image.

Now add the Camera class. Although we have yet to make the Camera class, its constructor will take the viewer dimensions as parameter, and the function SetTarget has the content Sprite as parameter. We position the Camera in the middle of the content and give it a first update.

With all this code in place we are able to render and manipulate the camera. Inside a Event.ENTER_FRAME loop you could run cam.Update() to refresh the camera view. It is however more efficient to only update upon a change. By changing cam.x and cam.y you can move the camera around, and by changing cam.scaleX and cam.scaleY you can change the scale. This is exactly what the keyboard keys in the example do.

Step 6: Camera Class A Closer Look

Here we take a look at the internals of the Camera. In the constructor function we add some graphics which are needed for the manipulation of scale of scale of the camera. We also store the viewer content dimensions locally inside the class.

Next up, attach the camera to an object. The camera becomes the same scale as the object.

This is the most interesting part and the engine of the camera. Scaling and translation is applied to the object attached to the camera by giving it a new transform matrix. Check out the Matrix class in the AS3 documentation for more information on the use of matrices.

Step 7: Setting up the Viewer

We return to our application. In this step we lay down the foundation for so called Page objects that contain the canvas with pictures and text. First, create a background for the whole application. The color is specified from XML.

Like in the example with the Camera and the flower, we are using pageContainer and pageScroller and Camera. This should look pretty familar.

Next up is adding all the Page objects required as specified in the XML. In our example we have only created one page. Note that the Page class does not exist yet as we are creating this one the next step. pages is an array that will hold all Page references for later use.

Parameters used by Page are

• pageScroller: the Sprite to which the Page will be added to
• p: an XML data object containing all information within a certain page (all images and text)
• g_background: the background texture

Step 8: Creating a Page

Displayed below is the constructor of Page. We simply save all the parameters locally in the class for later use.

Now a bit more interesting stuff. The Load function of Page starts off by taking the background texture we loaded before and wrapping it over the canvas. To do this we need to know the size of the background image we are looping and the size of the entire canvas. Create a Sprite called g_background which functions as a graphics container. Add a solid background color to prevent lines between the bitmap images to show.

Wrapping the background texture uses two loops, only vertically and one horizontally. It keeps on looping until the edge is reached. Every every tile at an even position - like (2,2), (4,2), (4,6) et cetera - is flipped around using scaleX or scaleY. This makes the texture flow over to the next one seamlessly. Checking if a number is even is done using the modulo operator (%). If the remainder of a number after deviding by 2 is zero then that number must be even. Around the edges we clip off any texture that goes outside the content dimensions as specified in trueWidth and trueHeight. It is important that the Page object stays this size as making it larger will change the aspect ratio and cause the screen to deform.

The result of the repeating background should be something like this. Dark borders are added for clarity.

Let's start our page-filling journey by adding text. Go through all XML entries labelled "text" and pass their data to the AddText function.

The AddText function looks like this. The first portion of the code validates the XML data. In case some fields aren't filled in it will append a standard value. QuickText is a class used to create a textfield with certain options. Finally, the text must be excluded from mouse interaction by using mouseEnabled = false and mouseChildren = false.

The following image shows all the options of the QuickText class:

And the result of the new page including text:

The first step here is to create Picture objects that will hold the XML data, the bitmap and some descriptive text. Listeners are directly applied for mouse interaction. All these Picture objects are then stored in an array for easy access later.

And here is the basic version of the Picture class, just holding the XML data. Picture extends Sprite, we can already position it somewhere. The scale setting is verified before being used, because if the user omits it from the XML it will return 0. The standard value for scale is 1.

Create a new BulkLoader just like the first time, but now to load batches of images. We are going to be loading 5 images at a time and displaying those when done. It will keep fetching new images until all are done. The function Complete is called at the completion of each batch.

In this step we're putting the loaded data inside Picture objects. The items property of the loader holds all objects that have been loaded in. Loop over all those objects and check whether they are an image. Each Bitmap is given to the corresponding Picture object and the Picture is added to the canvas. We also delete the loaded image from the loader items list to prevent conflicts with a later batch of loaded images.

And a closer look at the SetImage function of Picture.

And adding a picture to the canvas is as easy as calling addChild.

The result now becomes:

Adding something to the camera view is done by adding a child to the viewerScroller container. We've added the viewerScroller to each Page object as a parameter so that we can add it as a child by calling the Show() function of Page.

Return to the Canvas class and call the Load() and Show() functions when we'd like to a show the user a page. In case the Page is already loaded, Load() will return directly so no unnecessary actions are done. The current Page we are showing is saved in the class as page. This reference will be important for page interaction.

Step 13: Zoom Modes

Now that we've created our page with images and text, it's necessary to scale it so it will fit inside our viewer region. We are using the public static variables zoom and magnifyStep for this purpose. magnifyStep is an array holding all the different scale values of the camera and zoom is the current position of magnifyStep the camera is scaled to. To know which scaling value is needed to fit the content inside the viewer we need to know the ratio between the content and viewer regions. To account for wrong aspect ratios we take the minimal ratio between width and heights as follows:

We'd like to zoom in when clicking on the canvas. Add a mouse event to the hitfield of Page. The hitfield is basically just the graphics in the background of Page and because it is a Sprite we can put mouse interaction on it.

Upon a mouse click we would like the camera to scale down to the position zoom in the magnifyStepList and move to the point on the canvas that we clicked. Remember from the example that as the camera becomes smaller (scales down), the zoom on the canvas becomes larger. This is why we decrease the integer zoom by the value one. Getting the mouse position we clicked on the canvas is easy by using page.mouseX and page.mouseY. Flash automatically converts the numbers so that it appears local - meaning that if the page is for example 2000 px scaled down by 50% and you click halfway, it returns 1000 px, even though the mouse position on scale coordinates is a lot smaller.

Step 14: Correcting Camera Position

To keep the area of the camera inside the canvas we will need to correct the position within bounds of the camera. Looking at the camera example again for a demonstration of this. The camera is centered around itself so the position horizontally for example will need to stay within 0 + camera half-width and contentWidth - camera half-width. A reliable way to calculate the width of the camera when zoomed in is contentWidth/2 * magnifyStepList[zoom], because the camera in its initial unzoomed condition has the size contentWidth (same size as the canvas).

Shown in the image below is the camera and the canvas, zoomed in one time. The red lines show the borders that the camera can not cross and must stay within.

Step 15: Making the Zoom Work

Zooming is performed by adding scale to the camera. We are using the Tweener class and the "easyOutQuint" transition to make this happen in a smooth way. bZooming is a variable used to see if the camera is already zooming or not. You cannot zoom on the page again while it is still busy scaling up or down. At each update of the camera the function Update is called, which performs the scaling on the content.

Step 16: A Closer Look at Pictures

Remember that we added MouseEvent listeners to all the pictures inside the page. What we'd like to do is zoom in on a picture when one is clicked on and make sure it fits within the viewer region well. Images smaller than the actual viewer region must not be scaled beyond their resolution.

The basic concept here is that the aspect ratio of an image must first be determined. If the width of an image is higher than the height then imgRatio < 1 will hold true and vice-versa if the height is larger than the width. We will always scale to the largest part of an image, meaning that if the image is for example 200x400px, we will treat the image like a 400x400 square. Another addition here is that we scale the image with 1.05 first, meaning the image becomes 5% larger. This way the image does not touch the sides when zoomed in. To calculate the scale of an image in comparison to the content size we divide it by the height or width of the content.

Call the Zoom function of the camera and move to the middle of the picture we're focussing on and apply the new scale that we've calculated.

Here is the image zooming process shown in action. Notice how the camera is kept within the bounds of the page, and how the entire image including description fits inside the screen perfectly.

Step 17: Scrolling the Page

If you hadn't noticed, when zoomed in on a page you can move the mouse cursor to the edges of the screen to scroll around and view more of the page. The code shown below may look a little bizarre to you; it is something I've written a while ago for a RTS style game engine and have been re-using it ever since for anything that needs scrolling. Basic principles here is that you check where the mouse position is, and in case it moves without a certain range around the sizes (mouse_scroll_areax_reduced and mouse_scroll_areay_reduced) then it will start moving to one side at a rate proportionate to how far you are inside that range. When the mouse cursor is not inside the range it will put a drag on the scrolling to slow it down eventually.

Shown below is the area where mouse interaction for scrolling occurs. Remember that it can only happen when zoomed in.

Conclusion

I believe this sums up all the learning goals I set out for this tutorial. Unfortunately, not everything shown in the application could be discussed because of the scope of the tutorial, but I hope you managed to learn the basics about XML data, page filling and camera manipulation. Thanks for reading!