April 21, 2009

Javascript: scroll / animate a background image

I got the urge to add a little flavor to a repeating background image on one of my personal sites. When you roll over the div with your mouse, the image starts scrolling by incrementing the background-position css. First you need a div with a background-image, a background-position, and an id of "header":

#header {
background-image:url(../images/site-header-single.gif);
background-position:-73px 0;
background-repeat:repeat-x;
width:752px;
height:242px;
}

Then you need to attach the mouse rollover functionality to the header - I did this dynamically with protoculous, as seen below in the final Event.observe method. The rest is simple: start a timer, grab the background position offset from the css, increment, and apply the reassembled css. Easy:

// setTimeout variable for clearing on rollout
var headerTimeout;

function addHeaderBehavior() {
Event.observe( $('header'), 'mouseover', startHeaderAnim, false);
Event.observe( $('header'), 'mouseout', stopHeaderAnim, false);
}

function startHeaderAnim() {
headerTimeout = setTimeout( "incrementHeader()", 50 );
}

function incrementHeader()
{
// get x variable of background-position
var bgOffsets = $('header').getStyle('background-position').split(' ');
var offsetX = bgOffsets[0];
var offsetY = bgOffsets[1];
// strip px and increment
offsetX = offsetX.replace( /px/, '' );
offsetX = parseInt( offsetX ) + 1;
// update style
$('header').setStyle( { backgroundPosition:offsetX + 'px ' + offsetY } );

startHeaderAnim();
}

function stopHeaderAnim(event) {
clearTimeout( headerTimeout );
}


Event.observe(window, 'load', addHeaderBehavior, false);

April 2, 2009

ActionScript 3: Easily generate code to draw a complex image

In Flash, I always do everything in a programmatic way if possible. This includes drawing shapes with code instead of having library items. This is especially true, now that the preferred method of compiling is using the Flex mxmlc compiler. Sometimes, though, certain things are too much of a pain to draw pixel by pixel. No longer though, as there's a REALLY simple way to generate the code to draw complex shapes for you.

Step 1: save a .png of the image you want drawn.
Step 2: open Flash, and drop your image into the library, giving it a linkage ID of "MyGraphic".
Step 3: drop the following code into the actions panel and publish.
Step 4: copy the output code and apply it to a BitmapData object in your project.

var mc : MyGraphic = new MyGraphic( null, null );
for( var i:int = 0; i < mc.width; i ++)
{
for( var j:int = 0; j < mc.height; j ++)
{
if( mc.getPixel32( i, j ) != 0 ) trace( "bmp.setPixel32( "+i+", "+j+", "+mc.getPixel32( i, j )+" );" );
}
}



the resulting code in my case looked like this:

public static function drawVideoPlayhead():Bitmap
{
var bitmap:Bitmap = new Bitmap();
var bmp:BitmapData = new BitmapData( 7, 8, true, 0x00000000 );

// begin generated code
bmp.setPixel32( 0, 3, 4289309097 );
bmp.setPixel32( 0, 4, 4288322202 );
bmp.setPixel32( 0, 5, 4287664272 );
bmp.setPixel32( 0, 6, 4286874756 );
bmp.setPixel32( 1, 2, 4290888129 );
bmp.setPixel32( 1, 3, 4291085508 );
bmp.setPixel32( 1, 4, 4290822336 );
bmp.setPixel32( 1, 5, 4290032820 );
bmp.setPixel32( 1, 6, 4288256409 );
bmp.setPixel32( 1, 7, 4286874756 );
bmp.setPixel32( 2, 1, 4291677645 );
bmp.setPixel32( 2, 2, 4291875024 );
bmp.setPixel32( 2, 3, 4291677645 );
bmp.setPixel32( 2, 4, 4290822336 );
bmp.setPixel32( 2, 5, 4290032820 );
bmp.setPixel32( 2, 6, 4289374890 );
bmp.setPixel32( 2, 7, 4287598479 );
bmp.setPixel32( 3, 0, 4293322470 );
bmp.setPixel32( 3, 1, 4293388263 );
bmp.setPixel32( 3, 2, 4292335575 );
bmp.setPixel32( 3, 3, 4291677645 );
bmp.setPixel32( 3, 4, 4290822336 );
bmp.setPixel32( 3, 5, 4290032820 );
bmp.setPixel32( 3, 6, 4289374890 );
bmp.setPixel32( 3, 7, 4287598479 );
bmp.setPixel32( 4, 1, 4294046193 );
bmp.setPixel32( 4, 2, 4293256677 );
bmp.setPixel32( 4, 3, 4291677645 );
bmp.setPixel32( 4, 4, 4290822336 );
bmp.setPixel32( 4, 5, 4290032820 );
bmp.setPixel32( 4, 6, 4289374890 );
bmp.setPixel32( 4, 7, 4287598479 );
bmp.setPixel32( 5, 2, 4293717228 );
bmp.setPixel32( 5, 3, 4292861919 );
bmp.setPixel32( 5, 4, 4290822336 );
bmp.setPixel32( 5, 5, 4290032820 );
bmp.setPixel32( 5, 6, 4289703855 );
bmp.setPixel32( 5, 7, 4288059030 );
bmp.setPixel32( 6, 3, 4293914607 );
bmp.setPixel32( 6, 4, 4293190884 );
bmp.setPixel32( 6, 5, 4292730333 );
bmp.setPixel32( 6, 6, 4291677645 );
// end generated code

bitmap.bitmapData = bmp;
return bitmap;
}


Notes: using the setPixel32() function, and defaulting the BitmapData to a background color of 0x00000000 ensures that your Bitmap will have a transparent background. Note that the code here ignores empty pixels for efficiency. Finally, this code could easily be converted to draw into a Shape or Sprite, but for my purposes, bitmaps are usually what I want. Hope you find this useful!

March 16, 2009

ActionScript 3: Toggling a Boolean the easy way

This may be common knowledge to some, but I was pretty excited when I realized I didn't need an conditional statement to figure out which direction to toggle a Boolean value. Check it out:

var isTrue:Boolean = true;
isTrue = !isTrue;
trace(isTrue); // prints "false"
isTrue = !isTrue;
trace(isTrue); // prints "true"

Simplicity is your friend.

March 9, 2009

ActionScript 3: Checking for empty xml nodes

When parsing xml in AS3, you grab a leaf node's inner content by using the XML( node ).text() function. You can compare the result with a String, but if you're checking for an empty string, the .text() function doesn't compute, because it returns an XMLList, instead of a String. While a non-empty string comparison works fine between an XMLList and a String, an empty String ( i.e. var mystring:String = ""; ) is not the same as an empty XMLList. To ensure that your comparison works, it's always a good policy to cast your text node to a String. See the example below:

var isEmptyNode:Boolean = false;
if( String( node["myNodeName"].text() ) == "" )
{
isEmptyNode = true;
}

February 4, 2009

iPhone: MPMoviePlayerController preload issues

As of this writing, the MPMoviePlayerController object doesn't seem to send the MPMoviePlayerContentPreloadDidFinishNotification notification on the iPhone Simulator. This is happening when I'm loading an .m4v over the net. This issue makes my app appear broken on the simulator, because I'm waiting for the preload to finish before calling the "play" function on the MPMoviePlayerController object. When I test on the iPhone itself, there's no problem. Just an FYI.