Thursday, December 20, 2007

printAsBitmap - object must be on stage

This blog has moved.

just for future reference, I've discovered today something that appears to be undocumented - when setting a PrintJob to printAsBitmap=true OR you are printing from a mac,(using the PrintJobOptions class) it is suddenly important that the object is on the stage, or it won't print.

so if you didn't want your object on the stage, the simple solution is to surround your send with an addChild and removeChild like so:


this.addChild(page);
page.visible=false; //for macs only - otherwise they display the page while the print dialogue is open.
var myOption:PrintJobOptions = new PrintJobOptions(true);
my_pj.addPage(page,null,myOption);
my_pj.send();
this.removeChild(page);

Sunday, December 16, 2007

preloader absorbing application FlashVars

This blog has moved.


As has happened frequently, after completing a Flash application, I find that the size of the application demands a preloader swf to load the application swf (complete with progress bar). the problem i have found with this preloader swf is that it now absorbs any FlashVars that the application swf was expecting to be passed to it from the html.

how to get the preloader to pass the
FlashVars to the application? well, the preloader could extract the FlashVars and explicitly send them to the application in a function call once the application is loaded. but this would mean that the application would work differently than it would have prior to the loader. it could also mean that the preloader may have to be modified for each project, whereas a generic preloader would be much simpler.

here is a solution that means the application works exactly the same, and the preloader can remain generic - within the preloader extract all the
FlashVars, and pass them along to the application as URLVariables in the URLRequest.data property.

var ldr:Loader = new Loader();
var url:String = "application.swf";
var urlReq:URLRequest = new URLRequest(url);
urlReq.data=getObjectURLVariables(this.loaderInfo.parameters)
ldr.load(urlReq);
this.addChild(ldr);


public function getObjectURLVariables(whichObject:Object):URLVariables {
var variables:URLVariables=new URLVariables();
for (var i in whichObject) {
variables[i]=whichObject[i];
}
return(variables);
}

You could quite easily set this up as a custom Loader class that automatically passes the FlashVars along to the loaded application.

Tuesday, November 6, 2007

adding a symbol dynamically from the library

how to add a symbol from the library?
in AS2,
this.attachMovie("square", "square", 1);

and in AS3,
var square1:square=new square();

but how do you add a symbol dynamically from the library? say if you have a shape stored in a variable:

var shape:String = "square";

and you want to add the symbol in the library with this name.
in AS2, simple! you could have written:
this.attachMovie(shape, shape, 1);

however the AS3 syntax doesn't obviously allow for dynamic references. so how is it done? (credit to kglad for assisting in this discovery)

getDefinitionByName is what you need to extract the class from the library like so:

import flash.utils.getDefinitionByName;

var classRef:Class = getDefinitionByName(shape) as Class;

var square1:* = new classRef();

Frustrating Flex problems solved #2 - addChild

This blog has moved.

this.addChild(new Sprite())

looks simple enough doesn't it? especially if you've been working solely in Flash AS3. But if you try this line within a Flex component, you'll receive the error: 'Type Coercion failed: cannot convert flash.display::Sprite@86cc0e1 to mx.core.IUIComponent.'

This is because Flex components implement a different addChild method. although addChild is described as accepting a DisplayObject as a parameter, this DisplayObject must implement the IUIComponent interface.
How to add a sprite then? You can either:
specify a UIComponent. eg:
this.addChild(new UIComponent())
This sort of works in this case as UIComponent implements the Sprite. However the additional implementation of UIComponent may at best be unnecessary and at worst cause problems with your code. Also, this alternative won't work in some cases, say if for some reason you wanted to add a MovieClip.

so an alternative is that you can add your child to the component's rawChildren, a property which contains a list of ALL of the container's children - not just the children that the container wants you to know about! (this.numChildren<=this.rawChildren.numChildren) see flex help for more info on this. this alternative looks like:
this.rawChildren.addChild(new Sprite())

Tuesday, September 25, 2007

Embedding a font in a component

it took me quite a while and a variety of sources to work out how to set an embedded font for use in a comboBox component in AS3, so i thought i would record the solution for others(and myself in future when i forget!).

Here are steps to follow:

Step 1. add the font you wish to embed to the library, set it to 'Export for ActionScript' and give it a Class name.(in this case 'ComicSans')
Step 2. add a comboBox component to your stage.
Step 3. add the following script:

var Comic:TextFormat=new TextFormat();
Comic.font=new ComicSans().fontName;
Comic.size=12; //let's say we want to change the font size

combo.textField.setStyle("embedFonts", true);
combo.textField.setStyle("textFormat", Comic);
combo.dropdown.setRendererStyle("embedFonts", true);
combo.dropdown.setRendererStyle("textFormat", Comic);


A couple of points to be aware of:

when setting your TextFormat font, don't use the font's linkage identifier directly, this property requires the name that the system uses when referring to the font.
rather request the name of the font through the font class.
i did this above with the line:
Comic.font=new ComicSans().fontName;

the font of the textfield within the combo needs to set, and the text within the list component(dropdown property) within the combo also needs to be set.
these are set differently.
while the textfield is set directly with setStyle, the list component is set indirectly, with setRendererStyle as there will be multiple textfields within the list component.

ENTER_FRAME problem

i've got an interesting conundrum for you all.

According to Flash help, ENTER_FRAME "...does not go through a 'capture phase' and is dispatched directly to the target, whether the target is on the display list or not."

and it is true that an object with an ENTER_FRAME event does seem to work okay if it's not on the display list.

however, if you start loading external assets, it seems to forget about this ENTER_FRAME event at some random point in time! insert the code below into a frame, and replace imageFile with an actual external asset. the enterFrame function should count up from 1 to infinity, but instead at around 30(for me) it stops!

however, if you add the MovieClip to the stage, it receives the ENTER_FRAME event without fail.

const imageFile:String="put a swf here.swf";
var tally:int=0;
var loadertally:int=0;
setUpEnterFrame();
nextLoader();
function setUpEnterFrame() {
var newObject:MovieClip=new MovieClip();
newObject.addEventListener(Event.ENTER_FRAME,enterFrame);
//this.addChild(newObject); -------works when these comments are removed...
}
function nextLoader() {
var loader:Loader = new Loader();
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, loaderDisplayed);
var request:URLRequest = new URLRequest(imageFile);
loader.load(request);
moveToRandomPlace(loader);
this.addChild(loader);
}
function enterFrame(event:Event):void {
tally++;
trace(">>>>enterFrame - "+tally);
}
function loaderDisplayed(event:Event):void {
loadertally++;
if (loadertally<200)>
nextLoader();
}
trace("loader complete - "+loadertally);
}
function moveToRandomPlace(whichObject:DisplayObject):void {
whichObject.x=(Math.random()*stage.stageWidth);
whichObject.y=(Math.random()*stage.stageHeight);
}

STOP PRESS!

I've resolved this issue with some credit to kglad in forums - the problem is that as newObject is a variable local to the nextLoader function, when this function is complete and is garbage collected, the EnterFrame no longer triggers... i initially encountered this problem with Tweens - it is a very easy mistake to make - something to be cautious of!

Thursday, September 20, 2007

labels that are numbers

today i was given a movieClip of a candle slowly burning down. it had 15 states represented by numbered frame labels, and i wanted to go to the appropriate label based on the number of days the candle had been burning. the problem is, even if you convert the variable to a string, flash treats it as a number. so:

days=14;
gotoAndStop(String(days));


even though this is a string, flash still interprets this as frame number rather than a frame label. it goes to frame 14, rather than label '14'. there may be a solution out there, but the only one i know is renaming all of the labels to include an alpha character. laborious...

AS3 to the rescue!

we now have access to available frame labels and the related frame numbers in an array. so assuming we know where which number label we're after - which is easy in my case where the labels where numbered from 1 to 15, we can easily access the label even if its name is a number:

days=14;
this.gotoAndStop(this.currentLabels[days-1].frame);

Wednesday, September 12, 2007

double click problems

Let's say we want to set up a standard doubleClick interaction on a movie-clip(draw a square inside this clip) with instance name square:

square.doubleClickEnabled=true;
square.addEventListener(MouseEvent.DOUBLE_CLICK,double);
function double(event:MouseEvent):void {
trace("doubleclick");
}


Compile, it should work fine - "doubleclick" should trace when you double click the square.

Now place a movie-clip inside square with instance name circle(draw a circle inside this clip).

Recompile. Notice that double-click isn't recognised if you double click on the circle...

For some reason the circle movieClip is preventing the double-click event from bubbling up to square. forcing 'circle' to ignore the double-click and allow the event to continue on its merry way to 'square' is the best solution i have found:


square.circle.mouseEnabled=false;

Friday, August 17, 2007

Weird sizing problem

After a lot of pulling my hair out trying to work out why my custom scrollbars weren't resizing correctly, thinking there must be a problem with my code, i've discovered that it's actually a strange problem with flash resizing movieclips containing objects with strokes that aren't hairline.

to replicate the problem, do the following:

1. draw a rectangle 350 pixels high and say 10 pixels wide with a stroke width of 1.

2. draw a rectangle beside it 30 pixels high and 10 pixels wide with a stroke width of 1. convert this second rectangle to a movieClip and give it an instance name of test. turn on 9-slice scaling.

3. draw a rectangle beside test 30 pixels high and 10 pixels wide with a hairline stroke width. convert this third rectangle to a movieClip and give it an instance name of test2. turn on 9-slice scaling for this clip also.(although it doesn't need it as it is rectangular and uses hairline stroke - do this in the interests of consistency with the other clip for this test)

4. type the following in the frame's actions:

test.height=350;
test2.height=350;


5. compile the movie, and notice that the movie clip called test, with a stroke width of 1, doesn't quite make the correct height, whilst the hairline clip works fine.

what's going on?

flash appears to get a little confused between the height of the clip including stroke,
and the height of the fill.

to illustrate this, click on the movieClip and notice that the properties show it to be 30 pixels high.

trace(test.height);

and notice that flash thinks it is 31 pixels high...

so when flash calculates its new height, it is 1/31st off.

what we need to do is rectify these incorrect calculations. this is done by:
clip.height=preferredHeight*(clip.height/currentHeight);
or, in this case:

test.height=350*(test.height/30);

if you trace test.height again, you'll see that flash now thinks it is 361.7 pixels high, but it is
actually exactly the same height as the 350 pixel high rectangle we drew in step 1.

by the way, all of this can be avoided by drawing the rectangle originally with actionScript, but i believe these workings are still necessary for cases where simple shapes are not sufficient and we need to accurately resize a designer's work.

Monday, July 9, 2007

Frustrating Flex problems solved #1 - dropEnabled

I set up a drag-drop interaction between two components - from a custom 'dragMe' component i set up(extending Canvas) to a Tree component, but couldn't work out why my Tree wouldn't allow dragMe to drag over it. Until I started experimenting with removing properties from the Tree component. Eventually(this sort of trial and error can be soo slow going in flex) I found the source of the problem: i had dropEnabled set to true. huh? how unintuitive is that?!! as soon as i set dropEnabled to false(or left it out altogether because that's the default) the behaviour works fine.

After reading up on this property it does make sense - dropEnabled refers purely to the default drop behaviour. really defaultDropEnabled would be a more intuitively named property, but anyway i put this information out there for others not to waste the time i did yesterday!

Tuesday, July 3, 2007

line width

i was creating a dashed line function which basically drew a short line, then moved on a few pixels. i was trying to display this line with one pixel width. what i discovered was that if the dash was three pixels or less in length the width would increase to two pixels.?? have a look at this flex code(which assumes a Canvas component called 'canvas'), which displays several lines with the same linestyle, but with the length of each one increasing by one pixel:

import flash.geom.*
import flash.display.*
private function drawLine():void {
  canvas.graphics.lineStyle(1,0x000000);
  for (var i:int;i<10;i++) {
    canvas.graphics.moveTo(10,(i*10));
    canvas.graphics.lineTo(10+i,(i*10));
  }
}

what's that about? well i had a look at whether other lineStyle options had any effect and discovered pixelhinting. pixelhinting is a boolean that specifies whether to hint strokes to full pixels. you'd think that a straight horizontal line wouldn't require much in the way of hinting, but the player does seem to display it incorrectly. perhaps short lines confuses it?

anyway, to resolve this issue, add pixelhinting to the lineStyle:


canvas.graphics.lineStyle(1,0x000000,1.0,true);