⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 landscape.as

📁 QS我也没用过
💻 AS
字号:
package qs.containers
{
	import mx.core.UIComponent;
	import flash.utils.Dictionary;
	import mx.core.Container;
	import flash.display.Sprite;
	import flash.display.Shape;
	import flash.geom.Rectangle;
	import qs.controls.LayoutAnimator;
	import qs.controls.LayoutTarget;
	import flash.geom.Point;
	import flash.geom.Matrix;
	import mx.core.UIComponentCachePolicy;
	import isNaN;

	[Style(name="paddingLeft", type="Number", inherit="no")]
	[Style(name="paddingTop", type="Number", inherit="no")]
	[Style(name="paddingRight", type="Number", inherit="no")]
	[Style(name="paddingBottom", type="Number", inherit="no")]
	[Style(name="zoomLimit", type="String", inherit="no")]
	
	[DefaultProperty("children")]
	public class Landscape extends UIComponent
	{
		// all children are placed inside the pane.  We can then adjust the pane's transform to 
		// get the correct pan and zoom.
		private var _contentPane:ContentPane;
		// the mask is applied to the pane to make sure it gets clipped correctly.
		private var _mask:Shape;
		// our trusty layout animator, which allows us to get animated layout without having to write 
		// any real animation code.
		private var _animator:LayoutAnimator;

		// the array of currently selected ancestors
		private var _selection:Array = [];
		
		//our scale and translation state.
		private var _tSX:Number = 1;
		private var _tSY:Number = 1;
		private var _tTX:Number = 0;
		private var _tTY:Number = 0;
		
		
		// a flag to indicate that we should skip animation and jump right to our target.
		private var _needImmediateUpdate:Boolean = true;
		
		// Constructor
		public function Landscape()
		{
			super();
			
			// setup our pane to hold all of our children.
			_contentPane = new ContentPane();
			_contentPane.landscape=this;
			_contentPane.cachePolicy = UIComponentCachePolicy.ON;
			
			// and the mask that will guarantee it stays within our bounds.
			_mask = new Shape();
			addChild(_mask);
			addChild(_contentPane);
			_contentPane.mask = _mask;
			
			// initialize the animated layout engine.
			_animator = new LayoutAnimator();
			_animator.layoutFunction = generateLayout;
			_animator.initializeFunction = initPaneTarget;
		}
		
		
		// to allow developers to declarative set our children, like they do with standard Flex containers, we're going to
		// declare a children array property. We'll go ahead and add all of these children directly to the pane. 
		// we should really override the getChild/setChild functions too, as a facade down to the pane. But I'll leave that as an exercise
		// for the reaser.
		private var _children:Array = [];
		public function set children(value:Array):void
		{
			// adding and removing a child can be expensive. It's probably common for someone to grab the children, add a new item to the end of the array, and reassign it, which
			// means most of our children wouldn't really be changing.  So to avoid removing everything then adding everything, 
			//  We'll use a dictionary to track what children get added.  We can then iterate over all of our children, and whichever weren't added, get removed.
			
			// we could maybe be a little more clever here by tracking as we add children whether or not the numChildren changes. But I'll leave that as an exercise for the reader ;)
			var d:Dictionary = new Dictionary();
			for(var i:int = 0;i<value.length;i++)	
			{
				d[value[i]] = true;
				_contentPane.addChild(value[i]);				
			}
			for(var i:int = _contentPane.numChildren-1;i>=0;i--)
			{
				if(d[_contentPane.getChildAt(i)] == undefined)
					_contentPane.removeChildAt(i);
			}
			_children = value.concat();
		}
		public function get children():Array
		{
			return _children.concat();
		}
		
		// we facade this property up on the assumption that different uses might want different speeds.
		public function set animationSpeed(value:Number):void
		{
			_animator.animationSpeed = value;
		}
		public function get animationSpeed():Number
		{
			return _animator.animationSpeed;
		}
		
		// sets the selection, but skips the animation and jumps directly to the final display parameters.
		public function jumpToSelection(value:Array):void
		{
			selection = value;
			_needImmediateUpdate = true;
			invalidateDisplayList();
		}
		
		// sets the selection. This property can be assigned an array of arbitrary descendents of the Landscape component.  All the values in this array _must_ be descendents. 
		// the landscape will calculate the scale/translation needed to display all the children in the array, and animate towards it.
		public function set selection(value:Array):void
		{
			_selection = (value as Array);

			calculateMatrixForDescendants(_selection);
			_animator.invalidateLayout();			
		}
				
		public function get selection():Array
		{
			return _selection;
		}
		
		
		// given an array of descendants, calculate the scale and translation factors for the content pane
		// that will display those children as large as possible, centered.
		private function calculateMatrixForDescendants(selections:Array):void
		{			
			if(selections == null || selections.length == 0)
			{
				_tSX = 1;
				_tSY = 1;
				_tTX = _tTY = 0;
			}
			else
			{
				// before we get started, we'll zero out the contentPane's transformMatrix. We don't want previous transforms
				// screwing up our calculations.
				var m:Matrix = _contentPane.transform.matrix;
				_contentPane.transform.matrix = new Matrix();

				// if the developer has set padding, we'll inset our view rect by those values. The inset rectangle is the target area we'll try
				// and display the selections in.
				var targetLeft:Number = getStyle("paddingLeft");
				if(isNaN(targetLeft))
					targetLeft= 0;
				var targetTop:Number = getStyle("paddingTop");
				if(isNaN(targetTop))
					targetTop = 0;
				var targetRight:Number = getStyle("paddingRight");
				if(isNaN(targetRight))
					targetRight = 0;
				var targetBottom:Number = getStyle("paddingBottom");
				if(isNaN(targetBottom))
					targetBottom = 0;
				
				var targetWidth:Number = unscaledWidth - targetLeft - targetRight;
				var targetHeight:Number = unscaledHeight - targetTop - targetBottom;

				var globalBounds:Rectangle;
				
				var items:Array = [];
				for(var i:int = 0;i<selections.length;i++)
				{
					// for each child, convert its bounds into global coordinates
					var target:Object = selections[i];
					var context:UIComponent;
					var tl:Point;
					var br:Point;
					if(target is UIComponent)
					{
						context = target as UIComponent;
						tl = context.localToGlobal(new Point(0,0));
						br = context.localToGlobal(new Point(context.width/context.scaleX,context.height/context.scaleY));
					}
					else if (target is Rectangle)
					{
						context = _contentPane;						
						tl = context.localToGlobal(target.topLeft);
						br = context.localToGlobal(target.bottomRight);
					}
					else
					{
						context = target.context;						
						tl = context.localToGlobal(target.bounds.topLeft);
						br = context.localToGlobal(target.bounds.bottomRight);
					}
					items.push(context);
	
					// union that rect into a running bounding box for all selections.
					if(i == 0)
					{
						globalBounds = new Rectangle(tl.x,tl.y,br.x - tl.x,br.y - tl.y);
					}
					else
					{
						globalBounds = globalBounds.union(new Rectangle(tl.x,tl.y,br.x - tl.x,br.y - tl.y));
					}
				}
				// now convert that global bounding box back into the Landscape component's coordinates.
				tl = globalToLocal(globalBounds.topLeft);
				br = globalToLocal(globalBounds.bottomRight);

				// figure out the scale/translation factors to display that bounding box in the available viewable area
				var w:Number = br.x - tl.x;
				var h:Number = br.y - tl.y;								
				var sX:Number = targetWidth/ w;
				var sY:Number = targetHeight/ h;
				
				// we want to choose the smaller of the two scales to guarantee everything is visible.
				// lastly, we don't want to zoom in past full size. This probably should be optional..it's entirely possible 
				// that the selections we're focused on are scaled down, so even if the contentPane is zoomed up larger,
				// the selections might not be scaled at full.  Probably should be an option...don't go past 1, don't let the computed 
				// scale of any focused child go past 1, or just scale as big as we can.
				var scale:Number  = Math.min(sX,sY);
				var zoomLimit:String = getStyle("zoomLimit");
				switch(zoomLimit)
				{
					case "none":
						break;
					case "100%":
					default:	
						scale = Math.min(scale,1);
				}
				
				_tSX = _tSY =  scale;
				_tTX = -(tl.x + w/2)*scale + targetLeft + targetWidth/2;
				_tTY = -(tl.y + h/2)*scale + targetTop + targetHeight/2;
				_contentPane.transform.matrix = m;
			}
		}
		
		// we measure like Canvas does...by default, we just take the bounding box of all of our children at their 
		// measured sizes, and return that as our measured bounds.
		override protected function measure():void
		{
			var maxW:Number = 0;
			var maxH:Number = 0;
			for(var i:int = 0;i<_contentPane.numChildren;i++)
			{
				var child:UIComponent = UIComponent(_contentPane.getChildAt(i));
				if (isNaN(child.percentHeight))
					maxW = Math.max(maxW, child.x + child.getExplicitOrMeasuredWidth());
				if (isNaN(child.percentHeight))
					maxH = Math.max(maxH,child.y+child.getExplicitOrMeasuredHeight());	

			}
			measuredWidth = maxW;
			measuredHeight = maxH;
			measuredMinWidth = 0;
			measuredMinHeight = 0;
			invalidateDisplayList();
		}
		
		private function initPaneTarget(target:LayoutTarget):void
		{			
		}

		// layout the animation targets. In this component we only animate a single target...the contentPane, so
		// this function is simple.
		private function generateLayout():void
		{
			var target:LayoutTarget = _animator.targetFor(_contentPane);

			trace("layout is " + _tSX + "," + _tSY + " : " + _tTX + "," + _tTY);
			target.scaleX = _tSX;
			target.scaleY = _tSY;
			target.x = _tTX;
			target.y = _tTY;
		}
		
		// layout the children. We're currently using a subset of the canvas layout...we just leave every child where it is,
		// and let each child size to it's measured or explicit size.
		override protected function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void
		{
			_mask.graphics.clear();
			_mask.graphics.beginFill(0);
			_mask.graphics.drawRect(0,0,unscaledWidth,unscaledHeight);
			
			var childWidth:Number;
			var childHeight:Number;
			
			for(var i:int = 0;i<_contentPane.numChildren;i++)
			{
				var child:UIComponent = UIComponent(_contentPane.getChildAt(i));
				if(isNaN(child.percentWidth))
					childWidth = child.getExplicitOrMeasuredWidth();
				else
					childWidth = child.percentWidth/100 * unscaledWidth;

				if(isNaN(child.percentHeight))
					childHeight = child.getExplicitOrMeasuredHeight();
				else
					childHeight = child.percentHeight/100 * unscaledHeight;
					
				trace("child size is " + childWidth +","+childHeight);
				child.setActualSize(childWidth, childHeight);
			}
			calculateMatrixForDescendants(_selection);
			
			// if someone wanted us to skip animation, we now are guaranteed to know what our viewable area is, so 
			// jump to the target values now.
			if(_needImmediateUpdate)
			{
				_needImmediateUpdate = false;
				_animator.updateLayoutWithoutAnimation();
			}
			else
				_animator.invalidateLayout();
		}
		
	}
}
	import mx.core.UIComponent;
	import qs.containers.Landscape;
	

class ContentPane extends UIComponent
{
	public var landscape:Landscape

	override protected function measure():void
	{
		landscape.invalidateDisplayList();
	}
}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -