//////////////////////////////////////////////////////////////////////////////// // // Licensed to the Apache Software Foundation (ASF) under one or more // contributor license agreements. See the NOTICE file distributed with // this work for additional information regarding copyright ownership. // The ASF licenses this file to You under the Apache License, Version 2.0 // (the "License"); you may not use this file except in compliance with // the License. You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //////////////////////////////////////////////////////////////////////////////// package mx.effects.effectClasses { import flash.events.Event; import flash.events.MouseEvent; import mx.core.mx_internal; import mx.effects.EffectManager; import mx.events.FlexEvent; use namespace mx_internal; /** * The ZoomInstance class implements the instance class for the Zoom effect. * Flex creates an instance of this class when it plays a Zoom effect; * you do not create one yourself. * *

Every effect class that is a subclass of the TweenEffect class * supports the following events:

* * * *

The event object passed to the event listener for these events is of type TweenEvent. * The TweenEvent class defines the property value, which contains * the tween value calculated by the effect. * For the Zoom effect, * the TweenEvent.value property contains a 2-item Array, where:

* * * @see mx.effects.Zoom * @see mx.events.TweenEvent * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ public class ZoomInstance extends TweenEffectInstance { include "../../core/Version.as"; //-------------------------------------------------------------------------- // // Constructor // //-------------------------------------------------------------------------- /** * Constructor. * * @param target The Object to animate with this effect. * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ public function ZoomInstance(target:Object) { super(target); } //-------------------------------------------------------------------------- // // Variables // //-------------------------------------------------------------------------- /** * @private */ private var origScaleX:Number; /** * @private */ private var origScaleY:Number; /** * @private */ private var origX:Number; /** * @private */ private var origY:Number; /** * @private */ private var newX:Number; /** * @private */ private var newY:Number; /** * @private */ private var scaledOriginX:Number; /** * @private */ private var scaledOriginY:Number; /** * @private */ private var origPercentWidth:Number; /** * @private */ private var origPercentHeight:Number; /** * @private */ private var _mouseHasMoved:Boolean = false; /** * @private */ private var show:Boolean = true; //-------------------------------------------------------------------------- // // Properties // //-------------------------------------------------------------------------- //---------------------------------- // captureRollEvents //---------------------------------- /** * Prevents the rollOut and rollOver events * from being dispatched if the mouse has not moved. * Set this value to true in situations where the target * toggles between a big and small state without moving the mouse. * * @default false * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ public var captureRollEvents:Boolean; //---------------------------------- // originX //---------------------------------- /** * Number that represents the x-position of the zoom origin, * or registration point. * The default value is target.width / 2, * which is the center of the target. * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ public var originX:Number; //---------------------------------- // originY //---------------------------------- /** * Number that represents the y-position of the zoom origin, * or registration point. * The default value is target.height / 2, * which is the center of the target. * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ public var originY:Number; //---------------------------------- // zoomHeightFrom //---------------------------------- /** * Number that represents the scale at which to start the height zoom, * as a percent between 0.01 and 1.0. * The default value is 0.01, which is very small. * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ public var zoomHeightFrom:Number; //---------------------------------- // zoomHeightTo //---------------------------------- /** * Number that represents the scale at which to complete the height zoom, * as a percent between 0.01 and 1.0. * The default value is 1.0, which is the object's normal size. * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ public var zoomHeightTo:Number; //---------------------------------- // zoomWidthFrom //---------------------------------- /** * Number that represents the scale at which to start the width zoom, * as a percent between 0.01 and 1.0. * The default value is 0.01, which is very small. * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ public var zoomWidthFrom:Number; //---------------------------------- // zoomWidthTo //---------------------------------- /** * Number that represents the scale at which to complete the width zoom, * as a percent between 0.01 and 1.0. * The default value is 1.0, which is the object's normal size. * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ public var zoomWidthTo:Number; //-------------------------------------------------------------------------- // // Overridden methods // //-------------------------------------------------------------------------- /** * @private */ override public function initEffect(event:Event):void { super.initEffect(event); if (event.type == FlexEvent.HIDE || event.type == Event.REMOVED) { show = false; } } /** * @private */ override public function play():void { super.play(); applyPropertyChanges(); if (isNaN(zoomWidthFrom) && isNaN(zoomWidthTo) && isNaN(zoomHeightFrom) && isNaN(zoomHeightTo) ) { if (show) { // This is a "show" effect, so zoom from zero to original // size. (If neither show nor hide effect is specified, // guess "show") zoomWidthFrom = zoomHeightFrom = 0; zoomWidthTo = target.scaleX; zoomHeightTo = target.scaleY; } else { // This is a "hide" effect, so zoom down to zero. zoomWidthFrom = target.scaleX; zoomHeightFrom = target.scaleY; zoomWidthTo = zoomHeightTo = 0; } } else { // if only height zoom is specified, then we leave the width zoom alone if (isNaN(zoomWidthFrom) && isNaN(zoomWidthTo)) { zoomWidthFrom = zoomWidthTo = target.scaleX; } // if only width zoom is specified, then we leave the height zoom alone else if (isNaN(zoomHeightFrom) && isNaN(zoomHeightTo)) { zoomHeightFrom = zoomHeightTo = target.scaleY; } if (isNaN(zoomWidthFrom)) { // If no "from" amount is specified, use the current zoom. zoomWidthFrom = target.scaleX; } else if (isNaN(zoomWidthTo)) { // If no "to" amount is specified, choose a "to" amount of // either 1.0 or 0, but make sure "from" and "to" are different zoomWidthTo = (zoomWidthFrom == 1.0) ? 0 : 1.0; } if (isNaN(zoomHeightFrom)) { // If no "from" amount is specified, use the current zoom. zoomHeightFrom = target.scaleY; } else if (isNaN(zoomHeightTo)) { // If no "to" amount is specified, choose a "to" amount of // either 1.0 or 0, but make sure "from" and "to" are different zoomHeightTo = (zoomHeightFrom == 1.0) ? 0 : 1.0; } } // Guard against bogus input and divide-by-zero if (zoomWidthFrom < 0.01) zoomWidthFrom = 0.01; if (zoomWidthTo < 0.01) zoomWidthTo = 0.01; if (zoomHeightFrom < 0.01) zoomHeightFrom = 0.01; if (zoomHeightTo < 0.01) zoomHeightTo = 0.01; // Remember the original appearance origScaleX = target.scaleX; origScaleY = target.scaleY; newX = origX = target.x; newY = origY = target.y; // Use the center position if no origin has been specified. if (isNaN(originX)) { scaledOriginX = target.width / 2; } else { // Origin position adjusted for scale scaledOriginX = originX * origScaleX; } if (isNaN(originY)) { scaledOriginY = target.height / 2; } else { // Origin position adjusted for scale scaledOriginY = originY * origScaleY; } scaledOriginX = Number(scaledOriginX.toFixed(1)); scaledOriginY = Number(scaledOriginY.toFixed(1)); // If the object is flexible, set it to not be flexible for the // duration of the zoom. It'll still flex when the zoom is complete, // but we'll avoid hanging the player (bug 100035). origPercentWidth = target.percentWidth; if (!isNaN(origPercentWidth)) target.width = target.width; // Set width to be expressed in pixels origPercentHeight = target.percentHeight; if (!isNaN(origPercentHeight)) target.height = target.height; // Create a tween to resize the object tween = createTween(this, [zoomWidthFrom,zoomHeightFrom], [zoomWidthTo,zoomHeightTo], duration); // Capture mouse events if (captureRollEvents) { target.addEventListener(MouseEvent.ROLL_OVER, mouseEventHandler, false); target.addEventListener(MouseEvent.ROLL_OUT, mouseEventHandler, false); target.addEventListener(MouseEvent.MOUSE_MOVE, mouseEventHandler, false); } } /** * @private */ override public function onTweenUpdate(value:Object):void { // If we're in the middle of a three-frame instantiation, let it // finish before we start changing objects. Otherwise, we'll // dirty measurements after every frame, and the three-frame // process will never get past the "measure" step. //if (suspendBackgroundProcessing) //UIComponent.useLayoutManager &&) // return; // Tell the EffectManager not to listen to the "move" event. // Otherwise, moveEffect="Move" would cause a new Move effect // to be create with each onTweenUpdate. EffectManager.suspendEventHandling(); // Check if we have been moved since the last time we updated if (Math.abs(newX - target.x) > 0.1) { origX += Number(target.x.toFixed(1)) - newX; } if (Math.abs(newY - target.y) > 0.1) { origY += Number(target.y.toFixed(1)) - newY; } target.scaleX = value[0]; target.scaleY = value[1]; var ratioX:Number = value[0] / origScaleX; var ratioY:Number = value[1] / origScaleY; var newOriginX:Number = scaledOriginX * ratioX; var newOriginY:Number = scaledOriginY * ratioY; newX = scaledOriginX - newOriginX + origX; newY = scaledOriginY - newOriginY + origY; newX = Number(newX.toFixed(1)); newY = Number(newY.toFixed(1)); // Adjust position relative to the origin target.move(newX,newY); // Set a flag indicating that LayoutManager.validateNow() should // be called after we're finished processing all the effects for // this frame. // Zero-duration effects have no tween, so if !tween, tell // our superclass to layout and it will happen after we're done. if (tween) tween.needToLayout = true; else needToLayout = true; EffectManager.resumeEventHandling(); } /** * @private */ override public function onTweenEnd(value:Object):void { // If object's size was originally specified using percentages, // then restore percentages now. That way, the object will // resize when its parent is resized. if (!isNaN(origPercentWidth)) { var curWidth:Number = target.width; target.percentWidth = origPercentWidth; // Setting an object to have percentage widths will set its // actual width to undefined. If the parent's autoLayout is // false, setting its actual width to undefined will cause it // to be rendered as with a width and height of zero. To // avoid that situation, set its _width and _height explicitly. if (target.parent && target.parent.autoLayout == false) target._width = curWidth; } if (!isNaN(origPercentHeight)) { var curHeight:Number = target.height; target.percentHeight = origPercentHeight; if (target.parent && target.parent.autoLayout == false) target._height = curHeight; } super.onTweenEnd(value); if (hideOnEffectEnd) { EffectManager.suspendEventHandling(); target.scaleX = origScaleX; target.scaleY = origScaleY; target.move(origX, origY); EffectManager.resumeEventHandling(); } } /** * @private */ override public function finishEffect():void { // Remove the event listeners if (captureRollEvents) { target.removeEventListener(MouseEvent.ROLL_OVER, mouseEventHandler, false); target.removeEventListener(MouseEvent.ROLL_OUT, mouseEventHandler, false); target.removeEventListener(MouseEvent.MOUSE_MOVE, mouseEventHandler, false); } super.finishEffect(); } //-------------------------------------------------------------------------- // // Methods // //-------------------------------------------------------------------------- /** * @private */ private function applyPropertyChanges():void { // Apply the properties in the following priority: // scaleX/scaleY, width/height, visible var values:PropertyChanges = propertyChanges; if (values) { var useSize:Boolean = false; var useScale:Boolean = false; if (values.end["scaleX"] !== undefined) { zoomWidthFrom = isNaN(zoomWidthFrom) ? target.scaleX : zoomWidthFrom; zoomWidthTo = isNaN(zoomWidthTo) ? values.end["scaleX"] : zoomWidthTo; useScale = true; } if (values.end["scaleY"] !== undefined) { zoomHeightFrom = isNaN(zoomHeightFrom) ? target.scaleY : zoomHeightFrom; zoomHeightTo = isNaN(zoomHeightTo) ? values.end["scaleY"] : zoomHeightTo; useScale = true; } if (useScale) return; if (values.end["width"] !== undefined) { zoomWidthFrom = isNaN(zoomWidthFrom) ? getScaleFromWidth(target.width) : zoomWidthFrom; zoomWidthTo = isNaN(zoomWidthTo) ? getScaleFromWidth(values.end["width"]) : zoomWidthTo; useSize = true; } if (values.end["height"] !== undefined) { zoomHeightFrom = isNaN(zoomHeightFrom) ? getScaleFromHeight(target.height) : zoomHeightFrom; zoomHeightTo = isNaN(zoomHeightTo) ? getScaleFromHeight(values.end["height"]) : zoomHeightTo; useSize = true; } if (useSize) return; if (values.end["visible"] !== undefined) show = values.end["visible"]; } } /** * @private */ private function getScaleFromWidth(value:Number):Number { return value / (target.width / Math.abs(target.scaleX)); } /** * @private */ private function getScaleFromHeight(value:Number):Number { return value / (target.height / Math.abs(target.scaleY)); } //-------------------------------------------------------------------------- // // Methods // //-------------------------------------------------------------------------- /** * @private */ private function mouseEventHandler(event:MouseEvent):void { // The purpose of this logic is to prevent the situation // where the target toggles between the mouseOver and mouseOut // effects when the mouse is placed at certain positions. // Now we stop sending mouseOut and mouseOver events if the // mouse has not moved. if (event.type == MouseEvent.MOUSE_MOVE) { _mouseHasMoved = true; } else if (event.type == MouseEvent.ROLL_OUT || event.type == MouseEvent.ROLL_OVER) { if (!_mouseHasMoved) event.stopImmediatePropagation(); _mouseHasMoved = false; } } } }