HTML5 is the new LCD

The shift from Flash to HTML5 for rich, immersive, interactive sites and website modules reminds me of the shift from CRTs to LCDs over the past decade. LCDs had some advantages over CRTs from the beginning: they were sharper, had a more attractive and convenient form factor, and used less power. But they couldn’t touch CRTs with regard to color quality, viewing angle, and refresh rate.

But it didn’t matter.

CRTs were on the way out and LCDs were replacing them. There was no denying it. There was really no fighting it. LCDs were the future, even if they weren’t better for everyone and every situation. But, even today, there are certain people and certain situations that demand a CRT instead of an LCD. This is an ever-shrinking segment, but it’s still there. Barely. LCDs that have the color quality demanded by some photography and print pros are available, but their prices are still staggeringly high. And how many companies are making CRTs?

Today, HTML5 can do a lot of what used to only be possible with Flash. It can’t do everything and when it comes to complex animation and interactivity, building something with Flash will be faster and cheaper.

But it doesn’t matter.

HTML5 is the future. There’s no denying it. There’s no fighting it. Flash is getting replaced by HTML5, even if it’s not better for every situation. If you make something with HMTL5 instead of Flash, it can be viewed properly on modern browsers and mobile devices. And, today, that’s enough to make it the better choice. Even if the end result looks and acts better and takes less time to develop and test with Flash, HTML5 is going to be used in place of Flash for many applications. As it should. Because it is the better choice. But there are certain times where Flash is still the way to go. And it’s going to be that way for years to come.

Choosing the better option isn’t always the best choice. (Yes, that’s meant to be a brain twister.) Just as the image quality of LCDs couldn’t touch the image quality of CRTs, LCDs won. Digital cameras outsold film cameras before digital cameras actually took better pictures than film cameras. The fact that the pictures weren’t as good and they had frustrating shutter-lag didn’t matter.

Digital won before it was better.

What’s my point? Maybe just that someone who says that HTML5 is better than Flash is like someone saying, five or six years ago, that LCDs are better than CRTs. In what way? Maybe yes, maybe no. I’ve always been and remain more interested in the absolute quality of things. Especially visual things. So it hurts a little to see the look and feel of the web degrade a little bit in the name of progress.

I’m just musing. This blog is titled Erik’s Brain, and this is the thought that popped up there just a few moments ago. Onward to the next though: I need lunch.

Mercurial and FLAs

I’m starting to play around with Mercurial and wanted to see how well it handled FLA files. Mainly I wanted to know how much the repository would balloon as your revised and committed the FLA. So I did a simple test where I made an FLA, committed, added a big image to it, committed, made some other changes, committed, etc. Based on this very quick, simple and unscientific test, Mercurial does a pretty good job storing FLAs efficiently.

Start with empty FLA:
FLA is 57K, .hg is 53K

Added a single image to the FLA (+ the .hgignore file):
FLA is 381K, .hg is 274K

Embedded a font in the FLA:
FLA is 385K, .hg is 283K

Added another image to the FLA:
FLA is 1.1MB, .hg is 1MB

Converted image to MC, added to stage, blurred it:
FLA is 1.1MB, .hg is 1MB (977K)

Changed the blur on the MC:
FLA is 1.1MB, .hg is 1MB (989K)

Made another MC, added to stage, blurred it:
FLA is 1.1MB, .hg is 1.1MB (1010K)

So essentially Mercurial is storing 7 different versions of the same FLA and the repository is only 1.1MB (it’s actually smaller than the FLA it’s been tracking).

Just thought you might like to know.

TextField.onSetFocus and onKillFocus weirdness

I had to make a quick form using ActionScript 2 today. I wanted to add a quick little feature where the default “filler” text would be wiped out when someone clicks on the text field to start editing. Also, if the user left the text field without typing anything, the “filler” text would come back.

Simple stuff. And the code goes like this:

var startTextFirstName:String = "First Name";
 
tfFirstName.onSetFocus = function (obj:Object)
{
	if (tfFirstName.text == startTextFirstName) tfFirstName.text = "";
}
 
tfFirstName.onKillFocus = function (obj:Object)
{
	if (tfFirstName.text == "") tfFirstName.text = startTextFirstName;
}

I did that for three text fields. But it only worked for one of them. Weird.

Turns out the fix was simple. I had the uncooperative text fields set as MULTILINE input text fields. Change them to SINGLE LINE fields and all was well.

Loading in local external assets into an Air app

I’ve recently been tasked with turning an existing Flash app (that is to say, a self contained Windows EXE/Mac App) into an Air app. The original was only meant to run locally. It was never meant to be hosted on a web server. This module depends on loading in lots of external XML docs and images when you navigate to different sections. When loading in an XML doc, the code looks something like this:

// make the URLLoader that'll load in XML doc
var _l:URLLoader = new URLLoader(new URLRequest(_url));
 
// add a listener to use when the XML doc has loaded
// The xmlLoaded() method actually creates the XML object
_l.addEventListener(Event.COMPLETE, xmlLoaded)
 
// load in an XML doc named "myxml.xml" located in the
// "swf_assets" folder that's next to the SWF
_l.load(new URLRequest("swf_assets/myxml.xml"))

That should look familiar to you if you’ve loaded in an external XML doc before. But this doesn’t work with an Air app. Why? You don’t package external files with an Air app like you do with a SWF. So you need to handle finding external files differently with Air. It goes a little something like this:

// the File object is used to find the location of the
// XML doc we want to load in
var file:File = File.documentsDirectory.resolvePath("air_app_assets/myxml.xml"); 
 
// Pass the file.url property to the URLRequest constructor
var _l:URLLoader = new URLLoader(new URLRequest(file.url));
_l.addEventListener(Event.COMPLETE, xmlLoaded)

What that File.documentsDirectory.resolvePath business is doing is telling the Air app to go to the computer’s Documents directory, then dig into the “air_app_assets” folder to find the “myxml.xml” doc. The key here is that Air will know where the Documents folder is. It’s different on OS X vs. Windows vs. Linux. We just need to make sure that the “air_app_assets” folder lives in the Documents folder.

This isn’t an ideal solution. Had I known that this SWF was going to turn into an Air app, I would have set things up a lot differently. But at this point it’s not worth the trouble to deal with turning a TON of XML docs into a database.

The same principles used above will work for loading external images into your Air app. The key remains using the File class to tell Air where to find the files you want to load in. We can’t just pass in folder and file names when creating new URLRequest instances.

It took way too long to figure this out. I originally found a more complicated way to do this. It went like this:

// Same as before...
var file:File = File.documentsDirectory.resolvePath("air_app_assets/myxml.xml"); 
 
// We're going to use a FileStream object to load in the XML doc
var fileStream:FileStream = new FileStream();
fileStream.open(file, FileMode.READ);
 
// Use the FileStream.readUTFBytes() method to write the myxml.xml data
// directly to a new XML object
var _xml:XML = new XML(fileStream.readUTFBytes(fileStream.bytesAvailable)); 
fileStream.close();

That worked and I thought it was the only way to handle loading in external assets that lived on the user’s hard drive. But that’s messier and I like the first method better.

I’m new to this Air thing. Maybe method two is better. I really don’t know. If someone has a real answer and can explain it, I’d love to hear!

Compiling SWFs from TextMate with the Flex SDK

I finally feel like I’m all growds up.

Perhaps some details about this transition will follow in the coming days. But let me just say that this is the post that finally got me up and running with the Flex 3.3 SDK and TextMate. I’m not sure how often I’ll use this method. It’s way too soon to tell one way or another. I’m only now trying to figure out what Flex can do compared to Flash. My impression is that Flex isn’t as adept at doing totally custom visual experiences, but I could very well be totally wrong about that.

Two quick notes:

  • I couldn’t figure out why the flashlog.txt output wasn’t getting updated each time I compiled the SWF. Turns out I simply wasn’t opening the SWF after compiling it. Gotta open the SWF to generate the output text!

  • Which leads me to wonder, from TextMate can I quickly compile the SWF and open it in the Flash Player right away?

ActionScript TextFormat.font values

One thing I have to do a lot with ActionScript 3 is make TextField instances at run time that use something other than the default font. One way that I’ll do that is like this:

// make a new TextFormat object and set its "font" property
var myFormat:TextFormat = new TextFormat();
myFormat.font = "Arial";
 
// then make the TextField and apply the TextFormat to it
var tf:TextField = new TextField();
tf.defaultTextFormat = myFormat;

(This doesn’t deal with actually making sure that the font you want to use is embedded in the movie, but that’s another story.)

One part of this that’s been a bit of a mystery to me in some cases is exactly what to set as the TextFormat.font property. And, as you’ll see, there’s good reason to be a little confused, depending on what font you want to use. What you see in the CHARACTER portion of Flash’s PROPERTIES panel doesn’t always match what you need to type in as the TextFormat.font value. For example, if you want to use the following font:

Arial Regular

You need to type the following:

myFormat.font = "Arial"

That’s fairly straightforward, I guess. This one is a little less straightforward:

Meta Thin

…and the code you need to type to Flash:

myFormat.font = "Meta-Thin"

Where did that hyphen come from that goes betwee “Meta” and “Thin”? And how would I know I need to insert when, if I wanted to use Arial Bold, I’d simply type:

myFormat.font = "Arial Bold"

There’s no hyphen in that one. Mysterious.

Even worse, check this one out. Suppose you want to use this specific style of The Sans:

Thesans Sansplain

Guess what name you need to plug into the TextFormat.font property…

myFormat.font = "TheSans Plain"

Hmmm… How in the hell would you ever figure that out? And is there any logic here? And where do I find the string to type in for the TextFormat.font property?

The Solution

Turns out, there is an easy way to figure out exactly what to type in for the TextFormat.font property. It’s a bit of a hack, but it gets the job done. The process goes like this:

  1. Make a dynamic text field in the Flash authoring tool and set its font to the font name you are trying to figure out.
  2. Give that dynamic text field an instance name (let’s use “myField” in this example)
  3. Then type this code into the ActionScript panel:
trace(myField.getTextFormat().font)

That’s it. When you test the movie, the Output panel will print out the ActionScript version of the font name, which is exactly what we are looking for.

Error #2121 when loading external SWF

I’m running into an issue that goes a little something like this:

  • I have an external SWF that I’m loading into another parent SWF
  • The external SWF is simply a still image I created from a PNG using Fireworks CS3
  • I package up the parent SWF and external SWF and send it to another computer
  • When loading the external SWF with a Loader, I get the following message when trying to access the loader.contentLoaderInfo.content property within the Loader’s Event.INIT listener:

SecurityError: Error #2121: Security sandbox violation:
LoaderInfo.content: file:///Users/erikhansen/Desktop/SWF Loader Test/swf_loader.swf
cannot access file:///Users/erikhansen/Desktop/SWF Loader Test/from_fireworks.swf.
This may be worked around by calling Security.allowDomain.

Some more info that I know:

  • This works just fine when posted to a web server
  • This will also work without throwing an error if I create the external SWF with Flash (which will just make the whole process of making SWFs from PNGs even a bigger pain in the ass than it already is)
  • If I want, I could just add the Loader directly to the stage, but there are times that I need access to the loader.contentLoaderInfo.content property

So I’m trying to figure out a way around this problem. It usually isn’t a problem at all since, like I said, it works fine when being loaded from a web server. But it is a problem when I send a project to a client and they can’t view it on their computer. Hmm…

No solution yet. Maybe you know something I don’t.

Flash Developer vs. Flex Developer

I’m Erik P. Hansen. And I’m a Flash Developer. But I should really just call myself an ActionScript Developer.

I’m in the middle of watching episode two of Flasher Magazine which has an interview with Doug McCune. The thing that struck me most, and made me cringe, is when he talks about how the term “Flash Developer” is sort of a gross (I think he uses the word “gross”) term amongst Flex Developers. He says that the impression it gives is that, if you are Flash Developer, you write a lot of code that’s scattered throughout the timeline and it’s basically a mess. In his defense, he isn’t saying that’s true, that’s just the feeling Flex Developers have.

I can see where he’s coming from and understand why certain developers might feel that way. But it is so far off base it makes me a little ill. Just because you live in Flash because you are making totally customized UI elements for designers doesn’t mean that you are a sloppy programmer. You can be an ActionScript 3 jockey and like Flash and practice good coding conventions. It’s possible. I think I do.

Mostly.

Okay, confession time. I’ve got some old projects that basically fit the “Flash Developer is gross” mindset. I’ve built certain sections of certain nameless sites poorly, frankly, they make me ill. It was done that way because the client needed a fast turnaround. So, I built it as fast as possible. Which means those sections are maintenance nightmares. Okay, what the hell, I’ll come out and say it. The home page for this site is horrible. I mean, the code is horrible. It’s actually kinda sweet for the end user. It makes me cry every time I have to update it. It is wrong wrong WRONG. But the end user doesn’t know that. And the client only sorta knows that (because it takes way too long for me to make what should be simple updates). But I certainly know it and it makes me feel like a fraud whenever I have to dig into that code and see what I’ve done. I just hope that fellow developers out there know what I mean (and I know you have projects like this). The important thing is that I recognize where I have erred and I do everything I can to do things the right way. A client of mine (that is to say a designer) who might have a littel bit of experience playing with the timeline in Flash might not understand why it’ll take me longer than he thinks it should to create a linear animation. But that’s because I’m actually setting up the animation so that it’s programmatic rather than timeline based. And why would I do that? Because if he wants an element to stay on screen for half a second longer or slide out of place with stronger easing or if he wants all of the slides to fade in faster, it’s just a matter of changing a few numbers in the script rather than dragging keyframes around a timeline (which invariably screws up other parts of the animation).

So, for the record, I develop all of my SWFs in Flash. I don’t own Flex. But I am also a good coder. I try to do things the right way whenever possible. I love Colin Moock’s books and embrace design patterns. I’m reading and loving “The Pragmatic Programmer.”

I’m Erik P. Hansen. And I’m not a hack.

Hit Area of external assets with alpha channels (PNGs and SWFs)

Let’s say you are loading in an external asset that has an alpha channel, such as a PNG or a SWF*. Now let’s say that you want that asset to act like a button, with rollover and click actions. No problem. No reason you can’t do that.

But, actually, there is a problem. The hit area for that external asset will be its entire bounding box.

Take this octy image for example:

Octy

Even if you rolled over the top left of the image where it’s totally transparent, it will still trigger rollover actions, which is certainly not ideal and could potentially be very confusing for a user. This isn’t what happens if you have that image sitting in the library and you then add it to the stage at run time. But c’mon, we need it to work right with externally loaded assets! Of course I wouldn’t be writing this if I was just bitching and moaning. I’ve got a solution!

First, here’s the code in its entirety:

package
{   
    import flash.display.*;
    import flash.events.*;
    import flash.net.*;
    import flash.filters.*;
 
    public class Main extends Sprite
    {
        // this will hold the external asset and will act like a button
        public var s:Sprite
 
        public function Main()
        {
            init();
        }
 
        private function init():void
        {
            var l:Loader = new Loader();
            l.contentLoaderInfo.addEventListener(Event.INIT, assetLoaded);
            l.load(new URLRequest("octy.png"))
        }
 
        private function assetLoaded(e:Event):void
        {
            // copy the content of the loaded asset into a new BitmapData object
            var bmpData:BitmapData = new BitmapData(e.target.content.width, e.target.content.height, true, 0);
            bmpData.draw(e.target.content, null, null, null, null, true);
            var newBmp:Bitmap = new Bitmap(bmpData, "auto", true);
 
            s = new Sprite();
            s.x = s.y = 50;
            s.addChild(newBmp);
            addChild(s);
 
            // make our Sprite act like a button
            s.addEventListener(MouseEvent.CLICK, clickHandler)
            s.addEventListener(MouseEvent.ROLL_OVER, rollOverHandler)
            s.addEventListener(MouseEvent.ROLL_OUT, rollOutHandler)
            s.buttonMode = true;
        }
 
        // when you roll over the Sprite, create a MOUSE_MOVE listener
        private function rollOverHandler(e:MouseEvent):void
        {
            s.addEventListener(MouseEvent.MOUSE_MOVE, mouseMoveHandler)
        }
 
        // when you roll off of the Sprite, remove the MOUSE_MOUSE listener
        private function rollOutHandler(e:MouseEvent):void
        {
            s.removeEventListener(MouseEvent.MOUSE_MOVE, mouseMoveHandler)
            hideGlow();
        }
 
        // constantly check the alpha value of the pixel under the mouse pointer
        private function mouseMoveHandler(e:MouseEvent):void
        {
            var av:int = getAlpha(s)
            // if the pixel under the mouse isn't transparent,
            // show the hand cursor and turn on the image's glow 
            if (av > 0)
            {
                s.useHandCursor = true;
                showGlow()
            }
            // otherwise hide the hand cursor and turn off the glow
            else
            {
                s.useHandCursor = false;
                hideGlow()
            }
        }
 
        private function clickHandler(e:MouseEvent):void
        {
            var av:int = getAlpha(s)
            if (av > 0)
            {
                //do whatever it should do on click!
            }
        }
 
        private function showGlow():void
        {
            s.filters = [new GlowFilter()]
        }
 
        private function hideGlow():void
        {
            s.filters = []
        }
 
        // a little helper function that returns the alpha value of
        // the pixel under the mouse pointer
        private function getAlpha($t:Sprite):int
        {
            var bmd:BitmapData = Bitmap($t.getChildAt(0)).bitmapData;
            var av:int = (bmd.getPixel32($t.mouseX, $t.mouseY)) >> 24 & 0xFF
            return av;
        }
    }
}

The idea is pretty simple. Since Flash isn’t smart enough to handle things the way we’d like, we’ll just have to do it ourselves. To do that we’ll use BitmapData.getPixel32() to find the alpha value of the pixel underneath the mouse whenever the mouse moves. If that pixel’s alpha value is 0, then we’ll act like we aren’t over the button. But if the alpha value is greater than 0, then we will act accordingly by showing the hand cursor and triggering any cool rollover effects (in this case just a simple Glow Filter).

private function mouseMoveHandler(e:MouseEvent):void
{
    var av:int = getAlpha(s)
    // if the pixel under the mouse isn't transparent,
    // show the hand cursor and turn on the image's glow 
    if (av > 0)
    {
        s.useHandCursor = true;
        showGlow()
    }
    // otherwise hide the hand cursor and turn off the glow
    else
    {
        s.useHandCursor = false;
        hideGlow()
    }
}

But since you don’t want to waste CPU cycles by constantly checking the mouse position when you don’t need to, we’ll only enable that MOUSE_MOVE event listener when the mouse is over the bounding box of the externally loaded asset.

private function rollOverHandler(e:MouseEvent):void
{
    s.addEventListener(MouseEvent.MOUSE_MOVE, mouseMoveHandler)
}

When you roll off of the bounding box, remove the listener.

private function rollOutHandler(e:MouseEvent):void
{
    s.removeEventListener(MouseEvent.MOUSE_MOVE, mouseMoveHandler)
    hideGlow();
}

The same kind of logic is used for the CLICK event handler. You only actually do anything if the mouse isn’t over the transparent part of the image.

private function clickHandler(e:MouseEvent):void
{
    var av:int = getAlpha(s)
    if (av > 0)
    {
        //do whatever it should do on click!
    }
}

So that should give you a pretty good idea of how to do this in your own project where needed. Hopefully someone in need finds this post and saves themselves a lot of effort!

*A word on SWFs as images

SWFs are a great alternative to PNGs if you need external images to have nice alpha channels. By using a tool like Fireworks you can open a PNG with a nice alpha channel and then save it as a SWF. This is great because by saving the PNG as a SWF, you keep the alpha channel AND get JPEG compression which will give you MUCH smaller file sizes. The PNG to SWF conversion is an extra step (why why why can’t you just save to SWF direct from Photoshop?) but it might be worth it depending on your situation.