Center Registration Point – Another Recipe for the Flex CookBook

Here’s a little recipe for scaling an image from a center registration point. Flash always allowed you to bounce the registration point wherever you wanted, but Flex expects it on the top left. If you want to resize an image from the center evenly, with a horizontal slider for example, you’ll like this one.

First the MXML. This layout is simple, it basically just has the small script with a function to accept a SilderEvent when the user changes the value of the horizontal slider. This function then uses the static functions (calcX() and calcY()) in the ImageResizer class to calculate the new math and tell your image how to resize properly from the center.

Your layout

<![CDATA[
			import mx.events.SliderEvent;
			import utils.DefaultValues;
			import utils.ImageResizer;			

			public function doChangeSize(event:SliderEvent):void{
				// get the value of the hSlider, round it and divide by 100 for consistency
				var multiplier:Number = Math.round(hSlider.value) / 100;
				//trace(multiplier);
				// new height, width, x, and y for image
				img.width = DefaultValues.maxImageWidth * multiplier;
				img.height = DefaultValues.maxImageHeight * multiplier;
				img.x = ImageResizer.calcX(container.x,container.width,img.width);
				img.y = ImageResizer.calcY(container.y,container.height,img.height);

				// to avoid a "jump in position", find out where the full image's x,y coords are when the slider is
				// at 100% and place the image at those coords within it's parent container
				trace(img.x + " " + img.y);
			}
		]]>
</sourcecode]

The comments should be self explanatory enough. The "view" consists of a Canvas to hold your components, the image itself and the horizontal slider. When the slider thumb changes, it calls the doChangeSize() function which will make use of the ImageResizer class' static functions.

<strong>ImageResizer class</strong>

[sourcecode language="actionscript3" toolbar="true"]

package utils
{
	import utils.DefaultValues;

	public class ImageResizer {
		/**
		 *  Calculate your new center registration point. calcX wants x, containerWidth, and w.
		 *  Those are your image's parent container x, and width, and then your image's width.
		 *
		 * */
		public static function calcX (x:Number,containerWidth:Number,w:Number) : Number {
			// half of image's parent containers width
			var newRegX:Number = Math.round((w / 2));
			// parent container's x minus your new registration X
			var newX:Number = Math.round(x - newRegX);
			// value for the default parent container's width divided by 2
			var center:Number = Math.round(DefaultValues.innerCanvasWidth/2);
			// return your new centered X
			return Math.round(newX += center);
		}
		public static function calcY (y:Number,containerHeight:Number,h:Number) : Number {
			var newRegY:Number = Math.round(h / 2);
			var newY:Number = Math.round(y - newRegY);
			var center:Number = Math.round(containerHeight / 2);
			return Math.round(newY += center);
		}
	}
}

calcX() and calcY() will accept the image’s parent container’s x, y, width and height, as well as the image’s width and height. The functions will then calculate a new registration point (x and y) based on the information given them. The comments in the code should be clear enough.

In this recipe, we make use of the DefaultValues class to hold some constants for us, like the image’s container width, and the image’s full width and height. It’s not imperative that we maintain this convention, but it does keep things separated and clean. This class does nothing more than maintain these values for us.

DefaultValues class


package utils {

	public final class DefaultValues {

		public static const innerCanvasWidth:int = 500;// width of your image's parent container
		public static const maxImageHeight:int=102;//height of full image
		public static const maxImageWidth:int=177;//width of full image
	}
}

Sample

You can now play with the slider and resize the image from it’s new centered registration point. Because we set the horizontal slider’s liveDragging property to true, and the thumbDrag property to update every change in the thumb’s position, we can resize the image in real time. There are many other uses for the ImageResizer class, this is just one quick and simple little recipe I thought I’d share. Manga!

Download source code
resizefromcenter.zip

Facebooktwitterlinkedin

9 Comments

  1. Aleks

    It has a bug. If i do not move triangle, but i click on scroll line the picture will be not resized

  2. Dave (Post author)

    Hi Aleks,
    I actually never put in the code for the track click as it’s just a quick sample to show the resize from center. But if you want the pic to change on track click, just add this line to the HSlider in the ResizeFromCenter.mxml (around line 33 or anywhere inside the HSlider).

    change=”doChangeSize(event)”

    problem solved πŸ™‚

  3. Taniguchi, J.

    Hi, I’m just starting with Flex Builder 3 and after reading a couple of books, I’m trying to build my own RIA. Everything was wonderful and I was happy with the results until I got stuck with a Canvas problem. I got a canvas(A) within another canvas(B) which B can be scaled using a Slider. It worked fine but the thing is, to center the scale it making me mad. I’ve been searching the net for a week and I couldn’t find anything to help me figure it out. My code works but when I try to slide from 200% to 10%, to position from B gets all mess up.
    My code:
    /* ———————– */
    private function alignDoc():void {

    if(_doc.width > _wrap.width){
    _doc.x = 30;
    _wrap.horizontalScrollBar.setStyle(“thumbWidth”, 100);
    _wrap.horizontalScrollPosition = _wrap.horizontalScrollBar.width / 2;
    }
    if(_doc.height > _wrap.height){
    _doc.y = 30;
    _wrap.verticalScrollPosition = _wrap.verticalScrollBar.height / 2;
    }

    if(_doc.width < _wrap.width) {
    _doc.x = (_wrap.width – (_doc.width + 8)) /2;
    }
    if(_doc.width < _wrap.width){
    _doc.y = (_wrap.height – _doc.height) /2;
    }
    }
    /* ———————– */

    I wonder… it is a bug on Flex or in my head…? πŸ™‚

    I appreciate ANY help… Thanks!

  4. Dave (Post author)

    @Taniguchi, J.
    Can’t stay mad at Flex πŸ™‚ Flex is your friend.

    It’s probably just the math. Without seeing more of your code, I can’t say for sure. But I did notice in the code you pasted:

    if(_doc.width < _wrap.width) { _doc.x = (_wrap.width - (_doc.width + 8)) /2; } if(_doc.width < _wrap.width){ _doc.y = (_wrap.height - _doc.height) /2; } You have two conditionals to check if(_doc.width < _wrap.width){} Did you mean to check height on the second conditional? Either way, if you do your conditionals and use the static calcX and calcY methods in the ImageResizer class, it'll correctly position the Canvas in its parent.

  5. Taniguchi, J.

    Hi Dave, thanks for replying!

    The code it’s just it πŸ™‚ Allow me to explain it:
    _wrap = Canvas
    _doc = Canvas inside _wrap ( _wrap.addChild(_doc) )

    if(_doc.width > _wrap.width){
    //> add 30px to _doc.x and adjust the scrollbar position to 50%, like this:
    _doc.x = 30;
    _wrap.horizontalScrollPosition = _wrap.horizontalScrollBar.width / 2;
    }
    if(_doc.height > _wrap.height){
    //> add 30 px to y and fix the vertical scrollbar (center just like the horizontal. The point is, when you zoom it, the scrollbar should be placed always at the middle so you can scroll to the left and right.
    _doc.y = 30;
    _wrap.verticalScrollPosition = _wrap.verticalScrollBar.height / 2;
    }

    if(_doc.width this line makes the _doc be centere inside the _wrap. The +8 its the pixels relative to the shadow effect which I already excluded.
    _doc.x = (_wrap.width / 2) – (_doc.width / 2);
    }
    if(_doc.width this line also do the centering.
    _doc.y = (_wrap.height / 2) – (_doc.height / 2);
    }
    }

    So, as you see, its a really straight forwards and simple function. The way I see it, it supposed to work but it don’t. The last 2 IFs works fine because the _doc is smaller than _wrap. There is no scrollbar to fix and A/2 – B/2 works just fine. My best guess its that the first 2 IFs are wrong.
    Or maybe the problem is with the slider. I tested the same function using 2 buttons and autoRepeat and everything worked OK. Then, I tested the slider values and I found out that when I change the slider from one side to the another (-10 to 200) by clicking the track or by slinding it really fast, the slider will always return different values. Before you ask, I put the slider.value inside a int().

    Thanks and sorry for the looong text πŸ™‚

  6. Taniguchi, J.

    By the way, you said: ” – Either way, if you do your conditionals and use the static calcX and calcY methods in the ImageResizer class, it’ll correctly position the Canvas in its parent.”

    Could you give me an example?

  7. Dave (Post author)

    Well to center the _doc inside the _wrap Canvas, you’d likely use this:

    if(_doc.width > _wrap.width){
    _doc.width = ImageResizer.calcX(_wrap.x,_wrap.width,_doc.width);
    _doc.height = ImageResizer.calcY(_wrap.y,_wrap.height,_doc.height);

    }

    Keep in mind one thing though, if you’re setting the scroll position, you’re probably also forcing the _doc Canvas to change its position without knowing how it will interact with the ImageResizer static functions.

    Right now, you’re changing _doc.x explicitly and you’re setting the scroll position. So really, you just told Flex to change _doc.x twice.

    You’ll either have to remove that explicit setting of _doc.x and use the calcX calcY functions instead (I would try that first and comment the rest out as a test) or just make sure you’re not changing _doc.x two times by just tracing _doc.x right where you set the scroll bar position.

    To sum it up, I think the problem you’re having is by changing scroll position explicitly, Flex is changing _doc.x within its parent _wrap Canvas AND you’re explicitly setting _doc.x at the same time, so they’re fighting each other.

    Let me know how the test goes πŸ™‚ but that should work fine.

  8. Taniguchi, J.

    Hey Dave, your example worked just fine but… (there’re always a but πŸ™‚ ) it displays only 1/4 of the _doc when zoomed at 200%.
    So I tried a different approach. Insted of using slider (which is messing up the values), I’m using 2 buttons and listening for the buttonDown and setting autoRepeat to true. I figured if I get the value by pushing a button, it will return EXACLY the value I set, ex. +1 or +10. That way, I make sure the value is static. So, I tested this new approach and it worked just as I expect it to work. It re-scale the canvas and keep the scrollbar exacly at the center which allows you to scroll to the left and right, up and down. The “zoom effect” will always be focused on the center πŸ™‚

    The function:

    private function alignDoc():void {

    if(_doc.width > _wrap.width){
    _doc.x = 0;
    _wrap.horizontalScrollPosition = (_doc.width / 2) - (_wrap.width / 2);
    }
    if(_doc.height > _wrap.height){
    _doc.y = 0;
    _wrap.verticalScrollPosition = (_doc.height / 2) - (_wrap.height / 2);
    }
    if(_doc.width < _wrap.width) {
    _doc.x = (_wrap.width / 2) - (_doc.width / 2);
    }
    if(_doc.height < _wrap.height){
    _doc.y = (_wrap.height / 2) - (_doc.height / 2);
    }
    }

    I also called this function from creationComplete on the application tag which fix the center of the _doc when the application loads. I’ll try to implement this concept into your class to see what I can accomplish πŸ™‚

    Thanks for your time and sorry for making so many questions.
    Best regards!

  9. Dave (Post author)

    Cool, glad it worked πŸ™‚

    If you’re setting the _doc x and y, width and height, but not scaling the _wrap, the _wrap canvas will cut off the _doc canvas’s overlap by default. But it sounds like you got it working well.

    Let me know if you come up with something cool based on the ImageResizer class.

Comments are closed.