A Better Way To Zoom Your Custom Widgets

One common issue I’ve had when deploying widgets and visualizations is handling scaling for different resolutions.  I wrote a post a long time ago (one of my first) about the subject in which I detected the size change, cleared the screen and just redrew everything based on the new scale.  This worked fined when my widgets were simple, but as they’ve grown more and more complex and include lots of text, this became a nightmare.  Eventually I started limiting the range in which I’d support my visualizations to scale, and if a user dropped below that range (common when using a projector during a presentation), they’d be greeted with some scroll bars.  They were ugly and defeated the feeling of the visualization.  Fortunately, after some heavy Googling, I found some tips that lead me down a path that not only works perfectly, but it’s only a dozen lines of code!  Way, way easier than what I was doing before.

As previously stated, my original strategy for handling scaling was to just clear the screen and start over.  This added a tremendous overhead not only to development (the code gets very complicated when everything is relative variables), but also to performance.  I discovered that’s the sucker way to do it, since Flex actually has the ability to do this built in.

Basically, we just have to apply a transformation that does the scaling to the objects.  Thankfully, applying a transformation to a container applies it to the children, so don’t worry about having to go crazy with every object.  I had a lot of problems applying the transformation directly to the Application, and found that it works much better on a Canvas.  As such, I simply wrap a Canvas around my entire visualization and apply the zoom to that.
To start off, declare the following two variables at the application level:
private var size:Point;

private var originalMatrix:Matrix;

and then set their default values in an applicationComplete function:

size = new Point(1190, 925);

originalMatrix = canvas.transform.matrix;

The size is the relative point.  I stick it in a Point just as a convenient way to keep the Height/Width in a single object.  That represents your target resolution that you’re developing for, and any resolution lower will need to scale in relation to that one.

The originalMatrix holds the default starting value of the transformation.  I found that if I didn’t store this up front, the applying the transformations on top of each other would result in some whacky behavior (example, the window is being actively resized lower and higher by grabbing the edge and dragging it).

Also in your applicationComplete function, add an event listener to the resize event so you know when the window size changes:

addEventListener(ResizeEvent.RESIZE, zoom);
And also add a manual call to the zoom function so that the transformation is applied to the initial loaded window in the event it’s smaller off the bat:

And finally, the magical zoom function itself:

private function zoom(evt:ResizeEvent):void {

canvas.transform.matrix = originalMatrix;

var affineTransform:Matrix = canvas.transform.matrix;

var scale:Number = Math.min(width/size.x, height/size.y, 1);

affineTransform.scale(scale, scale);

canvas.transform.matrix = affineTransform;


So, the first line resets the transformation to the original one.  I found that if I didn’t do this, things would get a little messed up.  Next, we create a new Transformation Matrix, calculate what the scale should be, and rescale it accordingly.

The scale value should be the minimum of the difference in width and height and then use the same value for both.  If you scale the width and height at different ratios, then you’ll get stretching.  I also throw a 1 in there because if someone has a larger resolution that I developed for, I don’t want to zoom in.  They can just enjoy some nice empty space on the sides.  Likewise, you could throw a Math.max around that if you want to set a minimum ratio, because eventually they’ll zoom all the way down to a single pixel.

The original blog post I found that contained these tips was http://www.gasi.ch/blog/zooming-in-flash-flex/ and I adapted my code from there.  I couldn’t get his example to work exactly for me and had to make a few modifications to get my output.  I’m not sure if I was just doing something wrong or if there have been behavioral changes to the Flex framework since he wrote that post 4 years ago.

You may also like...