package flare.vis.controls { import flare.util.Displays; import flash.display.InteractiveObject; import flash.display.Stage; import flash.events.Event; import flash.events.MouseEvent; /** * Interactive control for panning and zooming a "camera". Any sprite can * be treated as a camera onto its drawing content and display list * children. The PanZoomControl allows you to manipulate a sprite's * transformation matrix (the transform.matrix property) to * simulate camera movements such as panning and zooming. To pan and * zoom over a collection of objects, simply add a PanZoomControl for * the sprite holding the collection. * *
	 * var s:Sprite; // a sprite holding a collection of items
	 * new PanZoomControl().attach(s); // attach pan and zoom controls to the sprite
	 * 
*

Once a PanZoomControl has been created, panning is performed by * clicking and dragging. Zooming is performed either by scrolling the * mouse wheel or by clicking and dragging vertically while the control key * is pressed.

* *

By default, the PanZoomControl attaches itself to the * stage to listen for mouse events. This works fine if there * is only one collection of objects in the display list, but can cause * trouble if you want to have multiple collections that can be separately * panned and zoomed. The PanZoomControl constructor takes a second * argument that specifies a "hit area", a shape in the display list that * should be used to listen to the mouse events for panning and zooming. * For example, this could be a background sprite behind the zoomable * content, to which the "camera" sprite could be added. One can then set * the scrollRect property to add clipping bounds to the * panning and zooming region.

*/ public class PanZoomControl extends Control { private var px:Number, py:Number; private var dx:Number, dy:Number; private var mx:Number, my:Number; private var _drag:Boolean = false; private var _hit:InteractiveObject; private var _stage:Stage; /** The active hit area over which pan/zoom interactions can be performed. */ public function get hitArea():InteractiveObject { return _hit; } public function set hitArea(hitArea:InteractiveObject):void { if (_hit != null) onRemove(); _hit = hitArea; if (_object && _object.stage != null) onAdd(); } /** * Creates a new PanZoomControl. * @param hitArea a display object to use as the hit area for mouse * events. For example, this could be a background region over which * the panning and zooming should be done. If this argument is null, * the stage will be used. */ public function PanZoomControl(hitArea:InteractiveObject=null):void { _hit = hitArea; } /** @inheritDoc */ public override function attach(obj:InteractiveObject):void { super.attach(obj); if (obj != null) { obj.addEventListener(Event.ADDED_TO_STAGE, onAdd); obj.addEventListener(Event.REMOVED_FROM_STAGE, onRemove); if (obj.stage != null) onAdd(); } } /** @inheritDoc */ public override function detach():InteractiveObject { onRemove(); _object.removeEventListener(Event.ADDED_TO_STAGE, onAdd); _object.removeEventListener(Event.REMOVED_FROM_STAGE, onRemove); return super.detach(); } private function onAdd(evt:Event=null):void { _stage = _object.stage; if (_hit == null) { _hit = _stage; } _hit.addEventListener(MouseEvent.MOUSE_DOWN, onMouseDown); _hit.addEventListener(MouseEvent.MOUSE_WHEEL, onMouseWheel); } private function onRemove(evt:Event=null):void { _hit.removeEventListener(MouseEvent.MOUSE_DOWN, onMouseDown); _hit.removeEventListener(MouseEvent.MOUSE_WHEEL, onMouseWheel); } private function onMouseDown(event:MouseEvent) : void { if (_stage == null) return; if (_hit == _object && event.target != _hit) return; _stage.addEventListener(MouseEvent.MOUSE_UP, onMouseUp); _stage.addEventListener(MouseEvent.MOUSE_MOVE, onMouseMove); px = mx = event.stageX; py = my = event.stageY; _drag = true; } private function onMouseMove(event:MouseEvent) : void { if (!_drag) return; var x:Number = event.stageX; var y:Number = event.stageY; if (!event.ctrlKey) { dx = dy = NaN; Displays.panBy(_object, x-mx, y-my); } else { if (isNaN(dx)) { dx = event.stageX; dy = event.stageY; } var dz:Number = 1 + (y-my)/100; Displays.zoomBy(_object, dz, dx, dy); } mx = x; my = y; } private function onMouseUp(event:MouseEvent) : void { dx = dy = NaN; _drag = false; _stage.removeEventListener(MouseEvent.MOUSE_UP, onMouseUp); _stage.removeEventListener(MouseEvent.MOUSE_MOVE, onMouseMove); } private function onMouseWheel(event:MouseEvent) : void { var dw:Number = 1.1 * event.delta; var dz:Number = dw < 0 ? 1/Math.abs(dw) : dw; Displays.zoomBy(_object, dz); } } // end of class PanZoomControl }