Jump to content
GreenSock

Search In
  • More options...
Find results that contain...
Find results in...
sdr984

Loading conditionally and other architecture questions

Recommended Posts

I see that the LoaderMax class provides a lot of ease when adding a loaded image to the stage. I have a situation where I have existing code where I have a URL, or bytes in an object that represent an image. If the bytes are there I load them (unfortunately in actionscript this is async even when bytes in memory). If the URL is used, I store the bytes so they are loaded quickly the next time (lazy load and cache).

 

I'm having a problem where using the data loader the image is not even loading. I have included the code below:

 

public function loadImageBytes(callback:Function, sessionId:String=null):DataLoader {
		this.imageBytesCallback = callback;
		if (_image == null && publicURL != null){
			var loadName:String = "test";     //id+"-"+new Date().getTime();  //what i want this id to be so unique
			var alternateURL:String = null;
			publicURL = "http://www.greensock.com/wp-content/themes/greensock/images/greensock_member_on.png";

			var loader:DataLoader = new DataLoader(publicURL, {name:loadName, format:"binary", estimatedBytes:25000, 
										autoDispose:true, 
										onProgress:progressHandler, onComplete:completeHandler, onError:errorHandler});
			loader.load();
			attemptedLoad = true;
			return loader;
		}
		return null;
	}

	function completeHandler(event:LoaderEvent):void {
		trace("complete "+event.text);
		_image = LoaderMax.getContent("test");
		imageBytesCallback(this);
	}

 

When this returns to _image the result is null (even though this image is obviously there if you go to the URL).

 

A few questions. I want to have the loader have a unique Id every time, however it seems that this was created to add the LoaderMax object to the stage instead of extracting out the raw bytes, and binding them to an Image object on the stage. Is that true, and is there any way to extract the bytes to the containing object? Would you recommend instead just having the LoaderMax object on the stage instead of an Image object?

 

It seems problematic that the completeHandler must know the content name of the LoaderMax. Doesn't this become an issue if you line up many images in a row to load as I'm planning on doing. The function only knows the Id if I include it within the scope of the function creating the LoaderMax. This means I can't pass the DataLoader back to the caller and allow it to wait on the complete event because it won't know what the content name is. Is there a way around this, or am I trying to use the class incorrectly.

 

Thanks for a great product. I have been using TweenLite and TransformManager and they have been great.

Link to comment
Share on other sites

When this returns to _image the result is null (even though this image is obviously there if you go to the URL).

Hmm...I think there must be something else going on in your app, then, because I just tested this code and it worked perfectly for me:

import com.greensock.events.LoaderEvent;
import com.greensock.loading.*;

var imageBytesCallback:Function;
var _image:ByteArray;
var publicURL:String = "blah";
var attemptedLoad:Boolean;

loadImageBytes(test);
function test(obj:Object):void {
trace("END");
}

function loadImageBytes(callback:Function, sessionId:String=null):DataLoader {
this.imageBytesCallback = callback;
if (_image == null && publicURL != null) {
	var loadName:String = "test";
	var alternateURL:String = null;
	publicURL = "http://www.greensock.com/wp-content/themes/greensock/images/greensock_member_on.png";

       var loader:DataLoader = new DataLoader(publicURL, {name:loadName, format:"binary", estimatedBytes:25000,
                                autoDispose:true,
                                onComplete:completeHandler});

	loader.load();
	attemptedLoad = true;
	return loader;
}
return null;
}

function completeHandler(event:LoaderEvent):void {
_image = LoaderMax.getContent("test");
trace("complete "+_image);
imageBytesCallback(this);
}

 

I noticed that you set autoDispose to true which means immediately after the DataLoader completes, it will be disposed. So you wouldn't be able to getContent() again for that loader after it fires its onComplete (the loader won't exist once it is disposed). But within the onComplete function, you certainly can still get it because the dispose() runs AFTER the onComplete is called.

 

I want to have the loader have a unique Id every time, however it seems that this was created to add the LoaderMax object to the stage instead of extracting out the raw bytes, and binding them to an Image object on the stage. Is that true, and is there any way to extract the bytes to the containing object? Would you recommend instead just having the LoaderMax object on the stage instead of an Image object?

When you say "add the LoaderMax object to the stage", do you mean the loader's "content" (which is a ContentDisplay Sprite)? You certainly do NOT need to use that if you don't want to. With an ImageLoader, if you want to get the loaded image's raw bytes (the BitmapData), you're welcome to do that using the "rawContent" property which refers to the Bitmap that was loaded. For example, if you wanted to copy the BitmapData into a new Bitmap, it'd be as simple as:

 

var copy:Bitmap = new Bitmap(myImageLoader.rawContent.bitmapData);

 

The great thing about the ContentDisplay Sprite, though, is that it provides a bunch of functionality that you couldn't get otherwise. For example, it can resize things automatically using various scaleModes, it can crop the image, it can center the registration point, and it is available IMMEDIATELY when you create your loader, so you can put it wherever you want on the stage even before you load() the raw content. You could add MouseEvent listeners for interactivity, etc. It is much more flexible and convenient. And it has a "loader" property that points back to the original loader instance which means you can get the associated data anytime. Imagine adding a ROLL_OVER listener to 50 ContentDisplay objects before you even load them, and then in your event handler, you could find out the url of the image that's going to load into that spot by simply using event.target.loader.url.

 

It seems problematic that the completeHandler must know the content name of the LoaderMax. Doesn't this become an issue if you line up many images in a row to load as I'm planning on doing. The function only knows the Id if I include it within the scope of the function creating the LoaderMax. This means I can't pass the DataLoader back to the caller and allow it to wait on the complete event because it won't know what the content name is. Is there a way around this, or am I trying to use the class incorrectly.

Nope, the completeHandler certainly does NOT need to know the name of the LoaderMax-related loader. That's the beauty of the event system. You can just use the event.target to find out which loader the event pertains to. For example:

 

var loader:DataLoader = new DataLoader("1.jpg", {name:"randomName", onComplete:completeHandler});
loader.load();
function completeHandler(event:LoaderEvent):void {
   trace("completed loader named: " + event.target.name);
}

 

In fact, that technique is very convenient for LoaderMax queues because you can throw tons of loaders into a LoaderMax and listen for onChildComplete like this:

 

var queue:LoaderMax = new LoaderMax({onChildComplete:childCompleteHandler});
queue.append( new DataLoader("1.jpg", {name:"random1"}) );
queue.append( new DataLoader("2.jpg", {name:"random2"}) );
queue.append( ... );
queue.load();

function childCompleteHandler(event:LoaderEvent):void {
   trace("completed loader: " + event.target);
   trace("content is: " + event.target.content);
}

 

By the way, is there a reason you're using DataLoader to load an image instead of ImageLoader? Just curious.

 

Does that clear things up for you?

Link to comment
Share on other sites

That's strange. Could it be because I'm using flex? I'm testing within Flash Builder 4 and this is a flex app using SDK 3.5. There isn't much that I'm doing apart from the code I pasted in. When I assign to _image, that is assigning to a ByteArray, which you have in your code. That is good that the data comes back on the event. I was looking at the event.data and it was null, so assumed it was not. I see that it is in the target. Here's where things get a little strange. When I debug there is a DataLoader in the target property of the complete event. This has a name of "test". But when I call _image = LoaderMax.getContent("test"); it does not get assigned to the _image var, it stays as null. I guess I could just do event.target.content as you suggest, but wondering why the other does not seem to work. Is it something with scope?

 

The reason why I'm using DataLoader instead of ImageLoader is that sometimes on my imageVO from the server I return the bytes over AMF. I have an existing mxml object in a view which I want to bind to a ByteArray. My logic goes that if the bytes are not returned from the server, I look to load them from a URL. Then I assign those bytes to the ByteArray which is bound to the Image. I'm not sure that the ImageLoader would help with this, but if you have recommendation it would be appreciated.

 

Anther reason I use raw bytes is to keep cached images small. If I loaded as an Image, it converts the jpg bytes I have into a bitmap that is about 30x the size. I keep these loaded bytes in memory cache so I want to keep them as small as possible.

 

The one thing that I haven't used that seems like it would be useful is the resizing tools. I have built my own Canvas to resize the image based on the background. This is kind of how iTunes resizes the videos as you resize the window around it (adjusting black bars on top and bottom to deal with the proportions). The biggest problem I ran into is that when you ask the image what size it is, it reports back the size of the canvas. I have canvases that I want to put ontop of the actual visible image (not what the Image object reports back as size, which is the size of the backing Canvas). So I look at the proportions of the image when it first comes in. Then on a resize event I change the Image wrapping Canvas to be the same size as the actual image. This allows me to put another drawing canvas ontop of the Image without having to worry about it creeping into the 'black bands'. Hopefully that makes sense. Do any of the loading tools allow for this use case?

Link to comment
Share on other sites

First of all, as far as _image = LoaderMax.getContent("test") ending up as null, where are you doing that? In your onComplete? If so, I bet the problem is that _image uses the wrong data type. Whenever you cast an object as something it's not, it will null the variable. Have you tried trace(LoaderMax.getContent("test")) and see if it really is null? If you're doing the call after your onComplete finishes, then the problem is that you set autoDispose to true which kills the loader as soon as the onComplete runs. That makes LoaderMax unable to find it (of course).

 

Feel free to post a simple Flex project so that I can see what's going on (if you're still having trouble).

 

Also, since you're using Flex, make sure you run this line of code once at the beginning of your app:

LoaderMax.contentDisplayClass = FlexContentDisplay;

This will use FlexContentDisplay instead of ContentDisplay for the container Sprites that wrap around your raw content. FlexContentDisplay extends UIComponent which is necessary in order to play nice with Flex (that's a Flex limitation/requirement, not LoaderMax).

 

As far as tools that will help you with the scenario you described, I read through your description a few times but I'm still having a hard time understanding exactly what you're doing. However, I bet that the FlexContentDisplay's "fitWidth" and "fitHeight" properties might come in useful as well as the scaleMode of course. You could also consider using an AutoFitArea - http://www.greensock.com/autofitarea/ although I haven't tested that with Flex specifically. Disclaimer: I don't like Flex. At all. I avoid it as much as possible for a bunch of reasons which I won't bore you with here. I only mention this so that you understand that I'm not a Flex guru although I have worked with it and I do have Flex Builder 4.

Link to comment
Share on other sites

It's probably better if I show an actual picture of what I'm talking about in the second part. Sorry it was confusing, I could tell it was as I was trying to describe it. So in mxml if I have something like this

 




 

I get something like this:

 

canvasIssue.png

 

Notice that if I ask the Image object for its width, it will not provide the actual width of the bitmap, it provides the width of the image object, which in the demo case above is wider than the actual image width.

 

This is fine if I just want to display the image. However, if I want to add an overlay for drawing ontop of the image I have to figure out how big the image is first. This requires me to 'shrink' the Canvas which contains the image to the image proportions first, then addChild to the canvas to add another drawing canvas on top. This is not easy to do as I have to figure out if the extra space is to the right or the bottom first based on the image proportions. Then I have to shrink it to the image. Then I can add another canvas child with width and height = 100%.

Link to comment
Share on other sites

If that was a FlexContentDisplay, you could figure out the actual image's size by referencing the rawContent.width and rawContent.height. And if you need to convert those values to coordinates/bounds in the parent, you could use getBounds().

 

Does that help?

Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
  • Recently Browsing   0 members

    • No registered users viewing this page.
×