Variable scope in function closure? - actionscript-3

I've just started using nested functions and they come in very handy. However I am running into problems with populating a variable which is later returned by the enclosing function.
The code below is for an AIR project to load some BitmapData. I expected that my variable ("bitmapData") would be populated in the loaderComplete function but it is always returned as null. However, in my trace statements, the image dimensions are being logged, so clearly it has been read.
Is there something wrong with my syntax or my understanding (or both ;-)
private function getBitmapData(url:String):BitmapData
{
var bitmapData:BitmapData;
var bytes:ByteArray = getByteArray(url);
if (bytes == null){
trace("bytes null");
return null;
}
var loader:Loader = new Loader();
loader.loadBytes(bytes);
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, loaderComplete);
loader.contentLoaderInfo.addEventListener(IOErrorEvent.IO_ERROR, ioErrorListener);
function loaderComplete(event:Event):void
{
loader.contentLoaderInfo.removeEventListener(Event.COMPLETE, loaderComplete);
loader.contentLoaderInfo.removeEventListener(IOErrorEvent.IO_ERROR, ioErrorListener);
var loaderInfo:LoaderInfo = LoaderInfo(event.target);
trace("loader",loaderInfo.width, loaderInfo.height);
bitmapData = new BitmapData(loaderInfo.width, loaderInfo.height, false, 0xFFFFFF);
trace("bitmapData.rect", bitmapData.rect);
bitmapData.draw(loaderInfo.loader);
}
function ioErrorListener(event:IOErrorEvent):void
{
trace("ioErrorListener", event.errorID);
loader.contentLoaderInfo.removeEventListener(Event.COMPLETE, loaderComplete);
loader.contentLoaderInfo.removeEventListener(IOErrorEvent.IO_ERROR, ioErrorListener);
}
return bitmapData;
}
public function getByteArray(url:String):ByteArray{
var byteFile:File = new File(url);
if(byteFile.exists && !byteFile.isDirectory){
var fileStream:FileStream = new FileStream();
fileStream.open(byteFile, FileMode.READ);
var tempBytes: ByteArray = new ByteArray();
fileStream.readBytes( tempBytes );
fileStream.close();
return tempBytes;
} else {
trace("file doesnt exist");
return null;
}
}

Loader.loadBytes() is an asynchronous method. The COMPLETE event is not fired in the same frame as the call to loadBytes. Therefore, you're actually attempting to return bitmapData before the code in loaderComplete executes.
You may want to try doing something with callbacks. For example, you can pass a reference to a function as a parameter in getBitmapData, and then call that function in loaderComplete:
private function getBitmapData(url:String, callback:Function):void
{
var bitmapData:BitmapData;
var bytes:ByteArray = getByteArray(url);
if (bytes == null){
trace("bytes null");
callback(null);
}
var loader:Loader = new Loader();
loader.loadBytes(bytes);
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, loaderComplete);
loader.contentLoaderInfo.addEventListener(IOErrorEvent.IO_ERROR, ioErrorListener);
function loaderComplete(event:Event):void
{
loader.contentLoaderInfo.removeEventListener(Event.COMPLETE, loaderComplete);
loader.contentLoaderInfo.removeEventListener(IOErrorEvent.IO_ERROR, ioErrorListener);
var loaderInfo:LoaderInfo = LoaderInfo(event.target);
trace("loader",loaderInfo.width, loaderInfo.height);
bitmapData = new BitmapData(loaderInfo.width, loaderInfo.height, false, 0xFFFFFF);
trace("bitmapData.rect", bitmapData.rect);
bitmapData.draw(loaderInfo.loader);
callback(bitmapData)
}
function ioErrorListener(event:IOErrorEvent):void
{
trace("ioErrorListener", event.errorID);
loader.contentLoaderInfo.removeEventListener(Event.COMPLETE, loaderComplete);
loader.contentLoaderInfo.removeEventListener(IOErrorEvent.IO_ERROR, ioErrorListener);
}
}
private function handleNewBitmapData(bitmapData:BitmapData):void
{
trace("handled bitmapData.rect", bitmapData.rect);
}
getBitmapData("[some url]", handleNewBitmapData);

Related

Adobe Flash image data

I have never worked in AS3. I want to send a screen shot of flash movie to a php file. Somewhere from internet I copied this function :
function sendSprite(sprite: Sprite, scriptLocation: String): void {
var bmpData: BitmapData = new BitmapData(sprite.width, sprite.height, true, 0xFFFFFF);
bmpData.draw(sprite);
var encodedFile: Base64Encoder = new Base64Encoder();
encodedFile.encodeBytes(PNGEncoder.encode(bmpData));
var data: URLVariables = new URLVariables();
data.fileData = encodedFile;
var request: URLRequest = new URLRequest(scriptLocation);
request.method = URLRequestMethod.POST;
request.data = data;
var loader: URLLoader = new URLLoader();
loader.addEventListener(Event.COMPLETE, spriteSend);
loader.addEventListener(Event.OPEN, traceEvent);
loader.addEventListener(HTTPStatusEvent.HTTP_STATUS, traceEvent);
loader.addEventListener(IOErrorEvent.IO_ERROR, traceEvent);
loader.addEventListener(SecurityErrorEvent.SECURITY_ERROR, traceEvent);
loader.addEventListener(ProgressEvent.PROGRESS, traceEvent);
try {
loader.load(request);
} catch (e: * ) {
trace("an error occured of type", e);
}
function traceEvent(e: * ): void {
trace(e);
}
function spriteSend(e: Event): void {
trace(e, "\n sprite succesfully send \n");
}
}
However I am not sure how to create a sprite object for calling the above function. Please help.
You can create an Spriteobject with the Sprite class:
//---Create a Sprite
var mySprite:Sprite = new Sprite();
//---Add Sprite to Display List
addChild(mySprite);
Inside this Sprite you can put any DisplayObject, for example:
//---Create the Video Object
var myVideo:Video = new Video();
mySprite.addChild(myVideo);
//---Create the NetConnection
var nc:NetConnection = new NetConnection();
nc.connect(null);
//---Create NetStream
var ns:NetStream = new NetStream(nc);
myVideo.attachNetStream(ns);
//---Play the video
ns.play("example.com/video.mp4");
Next, you could send the Spriteobject to your function and do your stuff.

ActionScript3 remove child error

I recently have been converting an as2 fla to as3 (new to AS3) and have the entire thing working on export, but I am getting an error when I try to remove previously loaded swf's before a new swf is loaded
ArgumentError: Error #2025: The supplied DisplayObject
must be a child of the caller.
at flash.display::DisplayObjectContainer/removeChild()
at MethodInfo-11()
I know the error relates to my removeChild code here:
`stage.addEventListener(MouseEvent.CLICK, removeSWF);
function removeSWF (e:MouseEvent):void
{
if(vBox.numChildren !=0){
// swfLoader.unloadAndStop();
vBox.removeChild(swfLoader);// empty the movieClip memory
}
}`
However, I cannot seem to find a suitable rewrite for this code that will work and not have an error. This code IS working, so I'm not sure if it would be worth my time to fix this error, or just leave it. I've already messed with it for a couple days, so at this point it's just frustrating me that I cannot fix it.
The stage mouse click listener is useful in this case because I have a back button not shown in this code that clears the loaded swf's before moving to another scene.
Does anyone see a simple solution for this, or do you think it is unnecessary to pursue since the code does what I require?
ENTIRE CODE:
function launchSWF(vBox, vFile):void {
var swfLoader:Loader = new Loader();
var swfURL:URLRequest = new URLRequest(vFile);
swfLoader.contentLoaderInfo.addEventListener(ProgressEvent.PROGRESS, onProgressHandler);
swfLoader.contentLoaderInfo.addEventListener(Event.COMPLETE, loadProdComplete);
swfLoader.load(swfURL);
function loadProdComplete(e:Event):void {
trace("swf file loaded");
vBox.removeChild(preLoader);
vBox.addChild(swfLoader);
currentSWF = MovieClip(swfLoader.content);
currentSWF.gotoAndPlay(1);
currentSWF.addEventListener(Event.ENTER_FRAME , checkLastFrame);
swfLoader.x = 165;
swfLoader.y = 15;
function checkLastFrame(e:Event):void {
if (currentSWF.currentFrame == currentSWF.totalFrames) {
currentSWF.stop();
// trace("DONE");
}
}
}
var preLoader:loader = new loader();
preLoader.x = 450;
preLoader.y = 280;
vBox.addChild(preLoader);
function onProgressHandler(event:ProgressEvent){
var dataAmountLoaded:Number=event.bytesLoaded/event.bytesTotal*100;
//preLoader.bar.scaleX = dataAmountLoaded/100;
preLoader.lpc.text= int(dataAmountLoaded)+"%";
//trace(preLoader.bar.scaleX );
}
//NEW ERRORS BUT WORKING
stage.addEventListener(MouseEvent.CLICK, removeSWF);
function removeSWF (e:MouseEvent):void
{
if(vBox.numChildren !=0){
// swfLoader.unloadAndStop();
vBox.removeChild(swfLoader);// empty the movieClip memory
}
}
}
var container:MovieClip = new MovieClip();
var currentSWF:MovieClip = new MovieClip();
fall_b.addEventListener(MouseEvent.CLICK, fall_bClick);
function fall_bClick(e:MouseEvent):void {
var swfFile:String = 'load/fall.swf';
launchSWF(container, swfFile);
addChild(container);
}
face_b.addEventListener(MouseEvent.CLICK, face_bClick);
function face_bClick(e:MouseEvent):void {
var swfFile:String = 'load/face.swf';
launchSWF(container, swfFile);
addChild(container);
}
rott_b.addEventListener(MouseEvent.CLICK, rott_bClick);
function rott_bClick(e:MouseEvent):void {
var swfFile:String = 'load/rottgut.swf';
launchSWF(container, swfFile);
addChild(container);
}
//MORE SWFS...
Any advice anyone has is appreciated
First of all function launchSWF(vBox, vFile):void { isn't closed. You've also got function inside functions which is easy enough for you to solve if you click the lines the curly brackets start and end on to track them.
I can't see anything wrong with the code you said has an error but I'm guessing this isn't all the code. If you using Flash Professisonal you can use permit debugging to show the line the error is on.
EDIT: Please note this hasn't been tested as I'm on my mobile writing out code. That being said this should now work:
var container:MovieClip;
var currentSWF:MovieClip;
var swfFile:String;
var swfLoader:Loader;
var preLoader:Loader;
var swfURL:URLRequest;
init();
function init():void {
preLoader = new Loader();
preLoader.x = 450;
preLoader.y = 280;
vBox.addChild(preLoader);
container = new MovieClip();
currentSWF = new MovieClip();
fall_b.addEventListener(MouseEvent.CLICK, fall_bClick);
face_b.addEventListener(MouseEvent.CLICK, face_bClick);
rott_b.addEventListener(MouseEvent.CLICK, rott_bClick);
stage.addEventListener(MouseEvent.CLICK, removeSWF);
}
function launchSWF(vBox, vFile):void {
swfLoader = new Loader();
swfURL = new URLRequest(vFile);
swfLoader.contentLoaderInfo.addEventListener(ProgressEvent.PROGRESS, onProgressHandler);
swfLoader.contentLoaderInfo.addEventListener(Event.COMPLETE, loadProdComplete);
swfLoader.load(swfURL);
}
function loadProdComplete(e:Event):void {
trace("swf file loaded");
vBox.removeChild(preLoader);
vBox.addChild(swfLoader);
currentSWF = MovieClip(swfLoader.content);
currentSWF.gotoAndPlay(1);
currentSWF.addEventListener(Event.ENTER_FRAME , checkLastFrame);
swfLoader.x = 165;
swfLoader.y = 15;
}
function checkLastFrame(e:Event):void {
if (currentSWF.currentFrame == currentSWF.totalFrames) {
currentSWF.stop();
// trace("DONE");
}
}
function onProgressHandler(event:ProgressEvent) {
var dataAmountLoaded:Number = (event.bytesLoaded / event.bytesTotal * 100);
//preLoader.bar.scaleX = dataAmountLoaded/100;
preLoader.lpc.text = int(dataAmountLoaded)+"%";
//trace(preLoader.bar.scaleX );
}
function removeSWF (e:MouseEvent):void {
if(vBox.numChildren !=0){
//swfLoader.unloadAndStop();
vBox.removeChild(swfLoader);// empty the movieClip memory
}
}
function fall_bClick(e:MouseEvent):void {
swfFile = 'load/fall.swf';
launchSWF(container, swfFile);
addChild(container);
}
function face_bClick(e:MouseEvent):void {
swfFile = 'load/face.swf';
launchSWF(container, swfFile);
addChild(container);
}
function rott_bClick(e:MouseEvent):void {
swfFile = 'load/rottgut.swf';
launchSWF(container, swfFile);
addChild(container);
}
I have this rewritten. I could not get the vBox errors cleared in the original code, and I was getting many other errors with what was posted. The vBox code was seen on a tutorial. I think it was supposed to reference the loader for the preloader and the swf, and vFile was for the actual .swf. The following code preloads multiple swfs and clears them with no errors. I appreciate your help AntBirch. I'm beginning to understand loaders in as3 a little more now.
//LOAD FIRST PIECE ON OPEN (required to removeChild later)
var swfLoader:Loader = new Loader();
var defaultSWF:URLRequest = new URLRequest("load/fall.swf");
swfLoader.load(defaultSWF);
swfLoader.x = 165;
swfLoader.y = 15;
addChild(swfLoader);
//PRELOADER
var preLoader:loader = new loader();
preLoader.x = 450;
preLoader.y = 280;
function loadProdComplete(e:Event):void {
trace("swf file loaded");
removeChild(preLoader);
addChild(swfLoader);
}
function onProgressHandler(event:ProgressEvent){
var dataAmountLoaded:Number=event.bytesLoaded/event.bytesTotal*100;
preLoader.lpc.text= int(dataAmountLoaded)+"%";
}
//BUTTONS
function btnClick(event:MouseEvent):void {
swfLoader.unloadAndStop();
removeChild(swfLoader);
swfLoader.contentLoaderInfo.addEventListener(ProgressEvent.PROGRESS, onProgressHandler);
swfLoader.contentLoaderInfo.addEventListener(Event.COMPLETE, loadProdComplete);
addChild(preLoader);
var newSWFRequest:URLRequest = new URLRequest("load/" + event.target.name + ".swf");
swfLoader.load(newSWFRequest);
swfLoader.x = 165;
swfLoader.y = 15;;
addChild(swfLoader);
}
// BUTTON LISTENERS
fall.addEventListener(MouseEvent.CLICK, btnClick);
face.addEventListener(MouseEvent.CLICK, btnClick);
rott.addEventListener(MouseEvent.CLICK, btnClick);
angel.addEventListener(MouseEvent.CLICK, btnClick);
ratts.addEventListener(MouseEvent.CLICK, btnClick);
metal.addEventListener(MouseEvent.CLICK, btnClick);
//etc...
//BACK BUTTON
BB3.addEventListener(MouseEvent.CLICK, BB3Click);
function BB3Click(e:MouseEvent):void {
swfLoader.unloadAndStop();
removeChild(swfLoader);
this.gotoAndPlay(1 ,"Scene 2")
}

AS3 call function inside loader event function

I have a problem to call a function inside a conditional in another function. I've tried it and it works perfectly but it depends on where the call is made.
Process: I have the checkCode(); function that is called when a form is completed and sent.
private function checkCode():void {
var url = "checktrue.php";
var loader:URLLoader = new URLLoader;
var urlreq:URLRequest = new URLRequest(url);
var urlvars:URLVariables = new URLVariables;
loader.dataFormat = URLLoaderDataFormat.VARIABLES;
urlreq.method = URLRequestMethod.POST;
urlvars.codeVar = formattedCode;
urlreq.data = urlvars;
loader.addEventListener(Event.COMPLETE, completed); //Completed function
status.text = "calling loader";
loader.load(urlreq);
}
When php is complete, "completed" is called and:
private function completed(event:Event = null):void {
var loader2: URLLoader = URLLoader(event.target);
var checked:String = loader2.data.checked;
var codetrue:String = loader2.data.codetrue;
if(checked == "1" || codetrue == null || codetrue == "") {
trace("WRONG");
}
else{
download(); //download function
trace("ok");
}
}
Here I get the php vars and check if they are valid to call the download() function.
public function download():void {
req = new URLRequest(proxy + filename);
file = new FileReference();
file.addEventListener(Event.COMPLETE, completeHandler);
file.addEventListener(Event.CANCEL, cancelHandler);
file.addEventListener(IOErrorEvent.IO_ERROR, ioErrorHandler);
file.addEventListener(ProgressEvent.PROGRESS, progressHandler);
file.download(req, "file.zip");
}
It is possible this function cannt be called from the "completed" loader function?
Thank you very much!
You need to put the download function in another button for the correct use. Or create your own event.

Putting Urlloader in a Class

I have searched on google en different pages i found the problem but not the solution.
I have a class i made and that class is called WebServiceController:
public class WebServiceController
{
private var url:String;
private var post:Object=new Object();
private var loader:URLLoader = new URLLoader();
private var postVariable:String="";
private var getVariable:String="";
private var Geladen:Boolean=false;
public function WebServiceController()
{
}
public function postUrlData(u:String,p:Object):String
{
url=u;
post=p;
var urlReq:URLRequest = new URLRequest (url);
var i:int=0;
for(var key:String in post)
{
//trace(key +" = " + post[key]);
if(i==0)
{
// urlVars.key = post[key];
postVariable=postVariable+""+key+"="+post[key];
}
else
{
//urlVars.key = post[key];
postVariable=postVariable+"&"+key+"="+post[key];
}
i++;
}
//trace(postVariable);
var urlVars:URLVariables = new URLVariables(postVariable);
//trace(urlVars);
// Add the variables to the URLRequest
urlReq.data = urlVars;
urlReq.method = URLRequestMethod.POST;
// Add the URLRequest data to a new Loader
//loader.load(urlReq);
// Set a listener function to run when completed
loader.addEventListener(Event.COMPLETE, onLoaderComplete);
// Set the loader format to variables and post to the PHP
loader.dataFormat = URLLoaderDataFormat.VARIABLES;
loader.load(urlReq);
function onLoaderComplete(event:Event):void
{
return loader.data;
}
}
Now from my movieclip i call the next function but it says undefined:
var wb:WebServiceController = new WebServiceController();
trace(wb.postUrlData(url,post));
I dont know how to solve this. I tried different things but it keeps saying undefined.
The URLLoader.load call is executed asynchronously so the data has not been returned when you attempt to trace it out in the class that instantiates your WebServiceController class.
To access the data in the parent class, your best bet is probably to dispatch an event from the WebServiceController class when the data has loaded and catch it in the parent class.
WebServiceController:
public function postUrlData(u:String, p:Object):void
{
url=u;
post=p;
var urlReq:URLRequest = new URLRequest (url);
var i:int=0;
for(var key:String in post)
{
//trace(key +" = " + post[key]);
if(i==0)
{
// urlVars.key = post[key];
postVariable=postVariable+""+key+"="+post[key];
}
else
{
//urlVars.key = post[key];
postVariable=postVariable+"&"+key+"="+post[key];
}
i++;
}
//trace(postVariable);
var urlVars:URLVariables = new URLVariables(postVariable);
//trace(urlVars);
// Add the variables to the URLRequest
urlReq.data = urlVars;
urlReq.method = URLRequestMethod.POST;
// Add the URLRequest data to a new Loader
//loader.load(urlReq);
// Set a listener function to run when completed
loader.addEventListener(Event.COMPLETE, onLoaderComplete);
// Set the loader format to variables and post to the PHP
loader.dataFormat = URLLoaderDataFormat.VARIABLES;
loader.load(urlReq);
}
private function onLoaderComplete(event:Event):void
{
// We can pass the event on like this
dispatchEvent(event);
}
Parent class:
public function initWebServiceController():void
{
var wb:WebServiceController = new WebServiceController();
wb.addEventListener(Event.COMPLETE, onWebServiceControllerDataLoaded);
wb.postUrlData(url, post);
}
private function onWebServiceControllerDataLoaded(event:Event):void
{
// The event target is the URLLoader instance. We can
// access the loaded data via its data property
trace(URLLoader(event.target).data);
}

AS3 Attaching event listeners to elements of an array

I am having trouble attaching eventListeners to the elements within my array, any help/suggestions would be great.. thanks for reading
var urls:Array=["../showcaseThumbnails/1.jpg",
"../showcaseThumbnails/2.jpg",
"../showcaseThumbnails/3.jpg",
"../showcaseThumbnails/4.jpg",
"../showcaseThumbnails/5.jpg",
"../showcaseThumbnails/6.jpg"];
var loader:Loader;
for (var i:int=0;i<urls.length;i++)
{
loader=startLoading(urls[i]);
loader.y=510;
loader.x=i*54+314;
addChild(loader);
}
function startLoading(url:String):Loader
{
var loader:Loader=new Loader();
loader.load(new URLRequest(url));
return loader;
}
I guess you don't need one more array. you can use loaderInfo's url property to find out the URL.function onLoad(e:Event):void {
var url:String = e.traget.url;
var index:Number = urls.indexOf(url);
}
var urls:Array=["../showcaseThumbnails/1.jpg",
"../showcaseThumbnails/2.jpg",
"../showcaseThumbnails/3.jpg",
"../showcaseThumbnails/4.jpg",
"../showcaseThumbnails/5.jpg",
"../showcaseThumbnails/6.jpg"];
var loaders:Array = [];//array to store loaders
var loader:Loader;
for (var i:int=0;i<urls.length;i++)
{
loader=startLoading(urls[i]);
loaders.push(loader);
loader.y=510;
loader.x=i*54+314;
addChild(loader);
}
function startLoading(url:String):Loader
{
var loader:Loader=new Loader();
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, onLoad);
loader.load(new URLRequest(url));
return loader;
}
function onLoad(e:Event):void
{
var index:Number = loaders.indexOf(LoaderInfo(e.target).loader);
trace(urls[index]);
//call the corresponding function from here based on index.
}