Overlays on Components in Flex 3 July 17th, 2009
Recently while working on a Flex app I needed to draw an image on top of a component which I didn't know the dimensions of until run-time. That is, I couldn't just make my overlay the exact size of the component and choose it's location in Photoshop. For example, here's a sort of 'placeholder' image we might want the user to interact with. Hover over it to see the desired effect we'll need in Flex:

So the placeholder image in this case is a square, but that wont be the case every time — we need to support every size that is larger than the size of the overlay itself.
After some of the usual API doc digging it was clear that I'd need to use
Graphics#beginBitmapFill, it's prototype looks like this:
public function beginBitmapFill( bitmap:BitmapData, matrix:Matrix=null, repeat:Boolean=true, smooth:Boolean=false ):void
.
Looks fine, once you call this you can do something like:
this.graphics.drawRect( 0, 0, this.width, this.height );
which
will cover the entire component with whatever is in the BitmapData
.
Attempt #1

So far so good, now let's try to draw the overlay at a bit of an
offset, working towards centering it on the canvas:
this.graphics.drawRect( 50, 50, overlay.width, overlay.height );
.
Attempt #2

Oops. Notice the repeat
argument — reading the documentation
doesn't shed much light but #beginBitmapFill
will always
'start' the drawing at the component's top-left corner!
Hey not a big deal, we'll just use the BitmapData#scroll method to shift the thing by a calculated amount to center it... Problem solved!
Attempt #3

Uhm... what!? How did that even happen? Now it's drawing the
overlay at 0, 0
and at our offset amount... The image
with offset is exceeding the bounds of the BitmapData causing the transparent
edge to be pushed out and since it (from the docs) 'uses the edge pixels for
the fill area outside the bitmap', we get the strange smear to the right
and bottom of the canvas. In short, it looks like #scroll
doesn't
do what I think it does.
Finally...
It should have occurred to me from the beginning that the trick would be in
the matrix
argument to #beginBitmapFill
. Rather than
trying to shift the bitmap within itself, we simply apply a matrix to it
during the fill. Now I'm no math wiz but this sort of thing is made
terribly simple by the
Matrix
class. All that was needed was taking my offset calculation out of
the call to BitmapData#scroll
and putting it into a call to
Matrix#translate
— and...

Now with a bit of event handling we can make the effect:
The Source
And, assuming there's a Canvas
instance around named
canvas
, the code could be:
One more thing...
This works perfectly if you're drawing on an element like a Canvas
but if you want to put an overlay on an Image
element, well,
this wont work because the image rendering takes over and you wont see any
work done with the graphics
property.
Fortunately this is only a minor change. I'll leave it as an exercise for
the reader to pick through this code, but the gist is that rather than
drawing directly on the element you draw on an instance of Shape
which you then add as a child and position on the image.
Similarly to above this code assumes an Image
instance named
image
.
This one might look like...
Also, you can download the full source for both Flex apps on this page.