//////////////////////////////////////////////////////////////////////////////// // // 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:
* *tweenEnd
: Dispatched when the tween effect ends. tweenUpdate
: Dispatched every time a TweenEffect
* class calculates a new value.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:
Zoom.zoomWidthFrom
* and Zoom.zoomWidthTo
property.Zoom.zoomHeightFrom
* and Zoom.zoomHeightTo
property.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;
}
}
}
}