I have a problem with my assets loader I've created for my flash app. In a function I try to load an external image and return it from this function. My problem is that I always get loader.content set to null. This how my code looks like:
public static function loadImage(path:String):BitmapData
{
var loader:Loader = new Loader();
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, check);
if (path == "")
loader.load(new URLRequest("images/default.png"));
else
loader.load(new URLRequest("images/" + path));
var image:Bitmap = new Bitmap((loader.content as BitmapData));
return image.bitmapData;
}
public static function check(e:Event):void
{
trace("Loading completed");
e.target.removeEventListener(Event.COMPLETE, check);
}
Any ideas???? I've also included my event listener which does launch.
I'm pretty sure it's because the loader works asynchronously, but I have no idea how to modify it to be able to return the image from the function.
Thanks in advance!
I'm pretty sure it's because the loader works asynchronously
Correct
but I have no idea how to modify it to be able to return the image from the function
There is no way to do that.
Why?
Because it is asynchronous.
You can't return a result of a request straight after the request has been made.
The execution of the code does not pause and wait for the response to come back, so your function will hit the return statement before you get the data, and that is why it is returning null.
The only way to handle this is to listen for the response (Event.COMPLETE) and run a callback function after the response from the server has been received.
So following your code example, you could do:
private static var _callback :Function;
public static function loadImage(path:String, callback:Function):void
{
_callback = callback;
// ....
and then:
public static function check(e:Event):void
{
// ...
_callback( whateverYouWantToReturnGoesHere );
This should work assuming that you only have one image loaded at a time (as your methods are static), otherwise you will need to implement some sort of request queue handling.
I hope this helps.
Your getting a 'null' because you're referring to loader.content too soon. Here's a simplified version that works, just fine, in a timeline:
var loader:Loader = new Loader();
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, check);
loader.load(new URLRequest("images/default.png"));
function check(e:Event):void
{
var bmd:BitmapData = new BitmapData(195,130,false,0xFFFFFFFF)
var image:Bitmap = new Bitmap(bmd);
bmd.draw(loader.content);
trace(loader.content);
trace("Loading completed");
addChild(image);
e.target.removeEventListener(Event.COMPLETE, check);
}
You can put it back into a form suitable for your classes.
You should say:
var bitmapData:BitmapData;
and
var image:Bitmap = new Bitmap(event.target.content.bitmapData)
instead of
var image:Bitmap = new Bitmap((loader.content as BitmapData));
Related
I am trying to get my function to return a value from a php script once it has loaded. I am having issues with the 'return' aspect. Trying to the the value of 'TheLink" from a function with a 'return' in it. It's usually null. What am I doing wrong here?
var theLink = loadAudio();
public function loadAudio():String
{
var req:URLRequest = new URLRequest("myScript.php");
var loader:URLLoader = new URLLoader(req);
loader.dataFormat = URLLoaderDataFormat.VARIABLES;
loader.addEventListener(Event.COMPLETE, Finished);
function Finished(e:Event)
{
var theValue = JSON.parse(e.target.data.audioLink);
return theValue;
}
}
A lot of things are wrong in your code but more important is that you cannot escape the asynchronous nature of AS3.
First thing first, even if AS3 wasn't asynchronous your code would still not work. The "loadAudio" never returns anything so theLink can never get any data. The 'Finished' method is the one return something but there's no variable to catch that data either.
Now the real stuff, AS3 is asynchronous that means that things will happen at a later time and this is why you need to pass an event listener in order to 'catch' when things happen. In your case the Event.COMPLETE will trigger at a later time (asynchronous) so there's no way for a variable to catch the result before the result is actually available so:
var theLink:Object = loadAudio();//this is not possible
The correct way is:
private var loader:URLLoader;//avoid GC
private var theLink:Object;//this will store result when available
//I assume you are using a class
public function MyClass()
{
loadAudio();//let's start loading stuff
}
private function loadAudio():void
{
var req:URLRequest = new URLRequest("myScript.php");
loader = new URLLoader();
loader.dataFormat = URLLoaderDataFormat.VARIABLES;
loader.addEventListener(Event.COMPLETE, handleLoaded);
loader.load(req);
}
private function handleLoaded(e:Event):void
{
theLink = JSON.parse(e.target.data.audioLink);
//done loading so store results.
}
I get error with load image into swf file. Here is my code:
private var loader:Loader = new Loader( );
public function Battle()
{
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, onComplete);
loader.load(new URLRequest("http://www.google.com.vn/images/srpr/logo4w.png"));
}
private function onComplete(event:Event):void
{
addChild(loader);
}
e very thing is OK, logo of Google appeared. But I need to manipulate pixel the image have loaded. I add a image Bitmap :
private var loader:Loader = new Loader( );
private var image:Bitmap;
public function Battle()
{
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, onComplete);
loader.load(new URLRequest("http://www.google.com.vn/images/srpr/logo4w.png"));
}
private function onComplete(event:Event):void
{
image = Bitmap(loader.content);
this.addChild(image);
}
There is nothing appear. I can't find any solution on internet. I try and detect :
private function onComplete(event:Event):void
{
image = Bitmap(loader.content);
//after above line, rest of function never run
addChild(image); // <-- no run ##
trace("this no run"); // <-- no run ##
}
I use Flash Builder 4 and have no error or any warning.
Can somebody show me a solution, please?
Thank for reading.
You can't access pixel data of an image if image is in different domain than your application (and there is no crossdomain policy file)
1 You need crossdomain.xml on Google site :)
2 You need use checkpolicy:
var context:LoaderContext = new LoaderContext();
context.checkPolicyFile = true;
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, onComplete);
loader.load(new URLRequest("http://www.google.com.vn/images/srpr/logo4w.png"), context);
but it works only if Google add crossdomain.
3 You can use little serverside proxy on your domain. Flash call proxy to get file with your url, proxy download it and return to flash...
I am new to actionscript and flash, but i managed to make code that gets data from php file and refresh result every 30 seconds:
var timerRefreshRate:Number = 30000;
var fatherTime:Timer = new Timer(timerRefreshRate, 0);
fatherTime.addEventListener(TimerEvent.TIMER, testaa);
fatherTime.start();
function testaa(event:Event):void{
var loader:URLLoader = new URLLoader();
loader.dataFormat = URLLoaderDataFormat.VARIABLES;
loader.addEventListener(Event.COMPLETE,varsLoaded);
loader.load(new URLRequest("data.php"));
function varsLoaded (event:Event):void {
this.opaqueBackground = loader.data.color;
title.text=loader.data.title;
banner_text.text=loader.data.text;
}
}
But now i am facing 2 problems:
1.) User must wait 30 seconds for movie to load first time
2.) Setting background color does not work any more.
What am i doing wrong?
You can call your function once to load immediately without waiting 30 seconds. Just change the parameters of the function to default to a null event:
function testaa(event:Event = null):void{
//...
}
Now you can call the function like so:
//...
fatherTime.start();
testaa();
So you start the timer but immediately run the function once.
For your second problem, the issue is most likely that you are using a nested function, so this does not refer to your class but rather the testaa function. Nested functions are bad practice in general and you should avoid them if possible. Move the function and loader reference outside and it should work. Final result should be something like this:
var loader:URLLoader;
var timerRefreshRate:Number = 30000;
var fatherTime:Timer = new Timer(timerRefreshRate, 0);
fatherTime.addEventListener(TimerEvent.TIMER, testaa);
fatherTime.start();
testaa();
function testaa(event:Event = null):void{
loader = new URLLoader();
loader.dataFormat = URLLoaderDataFormat.VARIABLES;
loader.addEventListener(Event.COMPLETE,varsLoaded);
loader.load(new URLRequest("data.php"));
}
function varsLoaded (event:Event):void {
this.opaqueBackground = loader.data.color;
title.text=loader.data.title;
banner_text.text=loader.data.text;
}
I am struggling passing paramters to function on onComplete event handler.
It seems that my problem is with the event.Complete code..
I just want to load image from a url and transfer parameter.
This is my code:
var imageURLRequest:URLRequest = new URLRequest(pic);
var myImageLoader:Loader = new Loader();
myImageLoader.load(imageURLRequest);
myImageLoader.contentLoaderInfo.addEventListener(Event.COMPLETE,function(evt:Event.COMPLETE){
doIt(evt, "Shift key was down:")
},false,0, true);
function doIt(evt:Event, msg:String) {
var myBitmapData:BitmapData = new BitmapData(myImageLoader.width, myImageLoader.height);
myBitmapData.draw(myImageLoader);
var myBitmap:Bitmap = new Bitmap;
myBitmap.bitmapData = myBitmapData;
}
Thank you very much.
Remove .COMPLETE from the handler inner function so that your listener looks like this:
myImageLoader.contentLoaderInfo.addEventListener(Event.COMPLETE, function(evt:Event)
{
doIt(evt, "Shift key was down:")
} , false, 0, true);
Look at the Loader class as loader, not as DisplayObject even when it is:
var myBitmap:Bitmap;
var contentLoader:Loader = new Loader();
contentLoader.contentLoaderInfo.addEventListener(Event.COMPLETE, handleComplete);
contentLoader.load(imageURLRequest);
function handleComplete(e:Event):void
{
myBitmap = contentLoader.content as Bitmap;
}
Firstly, as Gio said, remove that .COMPLETE from evt:Event.COMPLETE because it's returning a String instead of the Event the function needs.
Then, instead of setting that last fearsomely unpredictable parameter (useWeakReference) to true in your addEventListener(), I recommend you keep the reference in a variable to use removeEventListener() on it at the right time. A way to do this (while answering your question) is:
var imageURLRequest:URLRequest = new URLRequest(pic);
var myImageLoader:Loader = new Loader();
myImageLoader.load(imageURLRequest);
var functionDoIt:Function = doIt("Shift key was down:");
myImageLoader.contentLoaderInfo.addEventListener(Event.COMPLETE, functionDoIt);
function doIt(msg:String):Function {
return function(evt:Event):void {
// Now you can use both "msg" and "evt" here
var myBitmapData:BitmapData = new BitmapData(myImageLoader.width, myImageLoader.height);
myBitmapData.draw(myImageLoader);
var myBitmap:Bitmap = new Bitmap(myBitmapData);
}
}
// Remove the listener when you don't need it anymore:
//myImageLoader.contentLoaderInfo.removeEventListener(Event.COMPLETE, functionDoIt);
You can understand this solution better by reading this answer.
so i'm trying to make an external preloader to load my main .swf (loading.swf) file that has a class file named mainLoading.as using this code:
var l:Loader = new Loader();
l.contentLoaderInfo.addEventListener(ProgressEvent.PROGRESS, loop);
l.contentLoaderInfo.addEventListener(Event.COMPLETE, done);
l.load(new URLRequest("loading.swf"));
var loadingPage:loading = new loading;
function loop (e:ProgressEvent):void{
addChild(loadingPage);
loadingPage.x = stage.stageWidth/2;
loadingPage.y = stage.stageHeight/2;
}
function done (e:Event):void{
removeChild(loadingPage);
addChild(l);
}
so I'm getting an error message saying:
TypeError: Error #1009: Cannot access a property or method of a null object reference.
at mainLoading()
I think i'm getting the error message because i am accessing the stage in my mainLoading() class file. I tried adding this to the constructor in my class file but it didn't work:
public function mainLoading () {
addEventListener(Event.ADDED_TO_STAGE, init);
}
private function init(e:Event): void {
initStartUpScene ();
}
my initStartUpScene function just throws the intro scene to the loading.swf
any suggestions?
thanks for your help.
(question) is your mainLoading extends either Sprite or Movieclip ?
EDIT
After reading your comment, I would suggest trying this :
Add a function call inside the swf content in your complete progress handler :
function done (e:Event):void{
removeChild(loadingPage);
addChild(l);
Object(l.content).initMethod();
}
content let you access the methods in your loaded .swf main class (e.g. mainLoading)
And replace the event handling in your mainLoading by :
public function mainLoading () {
//no event handling in constructor
}
public function initMethod():void {
//here you go
init();
}
public function init():void { ... //No more event here
Btw it's not the cleanest way to solve your problem.
If that's the exact message you are getting, then yes, adding the ADDED_TO_STAGE listener should have fixed it. Remember to recompile the "loading.swf" if you make any changes to it (a step I always seem to forget)
Does "loading.swf" run just fine without any errors when running it on it's own (not loading it into the "container" SWF)?
This may be unrelated to the question you asked, but you might get better results and avoid some errors by structuring your code like this:
var loadingPage:loading = new loading;
addChild(loadingPage);
loadingPage.x = stage.stageWidth/2;
loadingPage.y = stage.stageHeight/2;
var l:Loader = new Loader();
l.contentLoaderInfo.addEventListener(ProgressEvent.PROGRESS, onProgress);
l.contentLoaderInfo.addEventListener(Event.COMPLETE, onComplete);
l.load(new URLRequest("loading.swf"));
//I renamed this function since I believe "loop" is a reserved keyword.
function onProgress (e:ProgressEvent):void{
//No code needed
}
function onComplete (e:Event):void{
removeChild(loadingPage);
addChild(l);
}
You can remove the "onProgress" function if you won't be needing it as well.
OOOOOOKKKKK, so after about a week of admitting defeat, trying it again, rewriting almost half of my code, trying to convince myself that preloaders are overrated, i finally figured it out (drum roll please):
I had a variable that was referencing the stage being called before my constructor method was called. like this:
private var xScrollPosRight:Number = stage.stageWidth - xScrollPosLeft;
I just changed stage.stageWidth to 750 and it worked.
live and learn.