Loader class with content scaling

Some time ago I needed a loader object that would allow scaling the content. So I simply created a class that extends the Loader class itself and adds that extra feature to it. The scaling occurs write after the content has been loaded and during resizing. I’ve named the class SimpleLoader and it has a Boolean property that allows the scaling when set to true, otherwise it just behaves like the standard Loader class.

I thought I should share it with you, so here it is. The class is documented so you should have no problem understanding it. Feel free to use it as you like.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
package {
 
	import flash.display.Loader;
	import flash.events.Event;
 
 
	/**
	 * class SimpleLoader
	 * 
	 * @author negush
	 */
	public class SimpleLoader extends Loader {
 
		/**
		 * The width and height setters have effect only after the content has been loaded.
		 */
 
		private var _scale:Boolean = false;
		private var _width:Number = 0;
		private var _height:Number = 0;
 
		private var scaleRatio:Number;
 
 
		/**
		 * Constructor.
		 */
		public function SimpleLoader() {
			super();
			this.contentLoaderInfo.addEventListener(Event.COMPLETE, completeHandler);
		}
 
 
		/**
		 * Handler function for the Event.COMPLETE event. Initializes the width and height of the
		 * content and calculates the scale ratio of the image, used when the loader is resized
		 * with the content being scaled. Catches the Loader's event and dispatches it as SimpleLoader.
		 */
		private function completeHandler(evt:Event):void {
			// The initial size of the content.
			this._width = this.contentLoaderInfo.width;
			this._height = this.contentLoaderInfo.height;
			// The scale ratio content_width / content_height.
			this.scaleRatio = this.contentLoaderInfo.width / this.contentLoaderInfo.height;
			if (this.scale) scaleToWidth();
			var newEvent:Event = new Event(Event.COMPLETE);
			dispatchEvent(evt);
		}
 
 
		/**
		 * Resizes the loader keeping the aspect ratio according to the new width. In this case the
		 * new height is calculated so that the content keeps its aspect ratio according to the
		 * loader's new width.
		 */
		private function scaleToWidth():void {
			this._height = Math.round(this.width / this.scaleRatio);
			super.width = this._width;
			super.height = this._height;
		}
 
 
		/**
		 * Resizes the loader keeping the aspect ratio according to the new height. In this case the
		 * new width is calculated so that the content keeps its aspect ratio according to the
		 * loader's new height.
		 */
		private function scaleToHeight():void {
			this._width = Math.round(this.height * this.scaleRatio);
			super.width = this._width;
			super.height = this._height;
		}
 
 
		//***********************************************************************
		//
		//	getters and setters
		//
		//***********************************************************************
 
		/**
		 * Setter/getter for the _scale property. Activates or deactivates the scaling for the
		 * current loader instance. If the scale is set to true, the content's height will be
		 * recalculated so that the content keeps the current width but will have the original
		 * aspect ratio.
		 * 
		 * true = resize keeping content aspect ratio
		 * false = resize without keeping content aspect ratio
		 * 
		 */
		public function set scale(value:Boolean):void {
			if (this._scale == value) return;
			this._scale = value;
			if (value) scaleToWidth();
		}
 
		public function get scale():Boolean {
			return this._scale;
		}
 
 
		/**
		 * Overrides the Loader's width setter and getter. The setter resizes the content
		 * according to the scale property's value.
		 */
		override public function set width(value:Number):void {
			if (this._width == value) return;
			this._width = value;
			if (this.scale) scaleToWidth();
			else super.width = value;
		}
 
		override public function get width():Number {
			return this._width;
		}
 
 
		/**
		 * Overrides the Loader's height setter and getter. The setter resizes the content
		 * according to the scale property's value.
		 */
		override public function set height(value:Number):void {
			if (this._height == value) return;
			this._height = value;
			if (this.scale) scaleToHeight();
			else super.height = value;
		}
 
		override public function get height():Number {
			return this._height;
		}
 
	}
 
}

UPDATE:
And here is how you can use it. It works just like the Loader class:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import SimpleLoader;
 
var ldr:SimpleLoader = new SimpleLoader();
ldr.load(new URLRequest("myimage.jpg"));
ldr.x = 20;
ldr.y = 20;
ldr.scale = true;
addChild(ldr);
 
ldr.contentLoaderInfo.addEventListener(Event.COMPLETE, completeHandler);
 
function completeHandler(evt:Event):void {
	ldr.width = 300;
	ldr.height = 100;
	trace("loader size: "+ldr.width+" x "+ldr.height);
}

How to preload AS3 clips

There are a lot of cases when your Flash animation is quite large in terms of file size. It contains images, perhaps some smaller .flv movies or a few “heavy” components.

In this case, if you publish your project somewhere on the web, you might have to wait a little, some times a little more, until the clip loads and starts playing. In this case it is very common (and quite frankly, necessary) to display something while your clip loads so the viewer knows there’s something going to happen and wait until your clip is completely loaded. In AS2 this wasn’t so hard. All you had to do was to move your animation to start from the third frame (but not necessarily), deactivate the “Export on first frame” setting from your symbols and components in the Library, place them on the second frame of your movie so they would load their assets (not necessary for all of them) by the time they would be instantiated, change the frame onto which classes were loaded from frame 1 to frame 2 and finally write some code on the first frame that would set an onEnterFrame event handler to display a preloader clip according to the size of your clip loaded so far. Lee Brimelow has made a nice video tutorial on how to preload flash clips, posted here: http://www.gotoandlearn.com/play?id=18. Again, this part isn’t necessary if you don’t want to display the amount of downloaded data and you only have a nice animation instead.

For ActionScript 3.0, the same thing is possible, only with the code changed to the AS3 syntax. You can download the example file from the link at the end of the post. However, this not always works. I’ve tried this with several components and looks like the method described earlier does not fully work. This is the case when using JumpeyeComponents’ TxEff or FlashEff. Even if you un-check the “Export in first frame” option from the Linkage Properties dialog box, there still is a considerable amount of data loading on the first frame. Don’t know why, yet… probably it has to do something with the embedded text (remember that TxEff and FlashEff need embedded characters for text effects). If this is the case, then you’ll probably have to wait a while until even your preloader shows up.

A workaround I’ve been using for this problem is to create the animation clip normally, without considering any preloading and then create a second clip I would use to load the first clip. This second loader clip would have all the preloading capabilities implemented into it. So, now I’m preloading an external .swf file. There’s also an item in JumpeyeComponent’s Knowledge base that addresses this issue: http://www.jumpeyecomponents.com/knowledgebase/TxEff-AS3.0/Preload-TxEff-animations~439/.

However, this arises another problem: in case of timeline animations, if that animation is in the external .swf file that you’re preloading, it so happens that the animation would start before the preloader is gone (while the preloading action is still going on). In this case, there’s a simple solution: move the entire timeline animation so it starts on the second frame and on the first frame place a stop() function. This way, your swf will not start the animation. Finally, after all the external .swf is loaded, just access the content in your loader object and instruct it to start playing from the second frame by using a gotoAndPlay(2) or gotoAndStop(2), if that’s the case.

You can check out these examples I’ve made.

Loader is not resizing

In ActionScrirpt 3.0 the way to load external media content like images or .swf files is to use the Loader class. The instance of this class is a DisplayObject too (actually a DisplayObjectContainer) which loads the content inside it and has to be added to display list to be viewed, just like any other DisplayObject.

The way you have to load the external content is a little bit different in AS3 using the Loader class than in AS2, using the well known MovieClip.loadMovie() method or loadMovieNum(). It resembles more on using the MovieClipLoader class from AS2.

Here is an example. If you want to load an image called flower.jpg and display it, here’s what you have to do:

  • - create the loader object:
    var loaderObject:Loader = new Loader();

  • - set the coordinates:
    loaderObject.x = 150;
    loaderObject.y = 45;

  • - add the loader to the display list:
    addChild(loaderObject);

At this point, anything you load inside it will be visible on the stage. If you would load the content without adding the loader object to the stage, there wouldn’t be anything displayed until the addChild() method call.

  • - you need to create a URLRequest object which is based on the URL of the content itself:
    var request:URLRequest = new URLRequest("flower.jpg");

  • - now you can instruct the Loader instance to load the image:
    loaderObject.load(request);

You could use a shortcut and combine the previous two lines into a single line of code:

loaderObject.load(new URLRequest("flower.jpg"));

Sometimes the content you want to load has a larger size than the stage or the area where you want to display it. So, you’ll have to resize the loader. Doing it write after the load() method call would result in not displaying anything, even if the content has been successfully loaded. This is because the loader gets resized after the load() method but before the content gets fully loaded (especially if it has a larger file size).

The solution for this problem is to always resize the Loader object after the content has completely loaded. To do this, you need to listen for the event the loader dispatches when it finishes loading the content: Event.COMPLETE:

loaderObject.contentLoaderInfo.addEventListener(Event.COMPLETE, resizeLoader);

function resizeLoader(evtObj:Event):void {
    loaderObject.width = 400;
    loaderObject.height = 300;
}