blob: 0c4e29c10c9124c06e7cffdd996125f675673250 [file] [log] [blame]
////////////////////////////////////////////////////////////////////////////////
//
// 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 spark.transitions
{
import flash.display.BlendMode;
import flash.display.DisplayObjectContainer;
import flash.events.Event;
import flash.geom.Point;
import mx.core.UIComponent;
import mx.core.mx_internal;
import mx.effects.Fade;
import mx.effects.IEffect;
import mx.effects.Parallel;
import spark.components.Group;
import spark.effects.Scale;
import spark.primitives.BitmapImage;
use namespace mx_internal;
/**
* The ZoomViewTransition class performs a zoom in or out transition for views.
* It performs its transition by zooming out the existing view to reveal
* the new view, or by zooming in the new view to cover the existing view.
*
* <p>The default duration of a ZoomViewTransition is 350ms.
* Also, by default it transitions the control bar and view content
* as one as if <code>transitionControlsWithContent</code> is <code>true</code>. </p>
*
* <p><strong>Note:</strong>Create and configure view transitions in ActionScript;
* you cannot create them in MXML.</p>
*
* @see ZoomViewTransitionMode
*
* @langversion 3.0
* @playerversion AIR 2.5
* @productversion Flex 4.5
*/
public class ZoomViewTransition extends ViewTransitionBase
{
//--------------------------------------------------------------------------
//
// Constructor
//
//--------------------------------------------------------------------------
/**
* Constructor.
*
* @langversion 3.0
* @playerversion AIR 2.5
* @productversion Flex 4.5
*/
public function ZoomViewTransition()
{
super();
// Default duration of 350 yields a smoother result.
duration = 350;
// Default to transitioning control bars with our views.
transitionControlsWithContent = true;
}
//--------------------------------------------------------------------------
//
// Variables
//
//--------------------------------------------------------------------------
/**
* @private
* Property bag used to save any start view properties that
* are then restored after the transition is complete.
*/
private var startViewProps:Object;
/**
* @private
* Property bag used to save any end view properties that
* are then restored after the transition is complete.
*/
private var endViewProps:Object;
/**
* @private
*/
private var transitionGroup:Group;
/**
* @private
*/
private var savedCacheAsBitmap:Boolean;
/**
* @private
*/
private var scaleEffect:Scale;
/**
* @private
*/
private var targetSnapshot:BitmapImage;
/**
* @private
* Stores the location of the cached navigator in the global coordinate space
* so that the transition can properly position it when added to the display list.
*/
private var targetSnapshotGlobalPosition:Point = new Point();
/**
* @private
*/
private var cachedNavigatorGroup:Group;
//--------------------------------------------------------------------------
//
// Properties
//
//--------------------------------------------------------------------------
//---------------------------------
// minimumScale
//---------------------------------
private var _minimumScale:Number = .25;
/**
* Specifies the minimum scale of the zoomed view (represents when the
* view is first visible when zooming in or last visible when zooming
* out).
*
* @default .25
*
* @langversion 3.0
* @playerversion AIR 2.5
* @productversion Flex 4.5
*/
public function get minimumScale():Number
{
return _minimumScale;
}
/**
* @private
*/
public function set minimumScale(value:Number):void
{
_minimumScale = value;
}
//---------------------------------
// mode
//---------------------------------
private var _mode:String = "out"; // avoid deprecation warning for ZoomViewTransitionMode.OUT;
[Inspectable(category="General", enumeration="in,out", defaultValue="out")]
/**
* Specifies the type of zoom transition to perform.
*
* @default ZoomTransitionMode.OUT
*
* @langversion 3.0
* @playerversion AIR 2.5
* @productversion Flex 4.5
*/
public function get mode():String
{
return _mode;
}
/**
* @private
*/
public function set mode(value:String):void
{
_mode = value;
}
//--------------------------------------------------------------------------
//
// Methods
//
//--------------------------------------------------------------------------
/**
* @private
*
* @langversion 3.0
* @playerversion AIR 2.5
* @productversion Flex 4.5
*/
override public function captureStartValues():void
{
// Suppress the default action bar transition, not really
// appropriate for the zoom.
actionBarTransitionMode = ViewTransitionBase.ACTION_BAR_MODE_NONE;
super.captureStartValues();
var oldVisibility:Boolean = endView.visible;
endView.visible = false;
cachedNavigator = getSnapshot(targetNavigator, 0, cachedNavigatorGlobalPosition);
endView.visible = oldVisibility;
}
/**
* @private
*
* @langversion 3.0
* @playerversion AIR 2.5
* @productversion Flex 4.5
*/
override public function captureEndValues():void
{
super.captureEndValues();
// Set targetSnapshot to the snapshot that we will be
// transitioning in or out.
if (consolidatedTransition)
{
if (mode == "out") // avoid deprecation warning for ZoomViewTransitionMode.OUT
{
targetSnapshot = cachedNavigator;
targetSnapshotGlobalPosition = cachedNavigationGroupGlobalPosition.clone();
}
else
{
targetSnapshot = getSnapshot(targetNavigator.skin, 0, targetSnapshotGlobalPosition);
}
}
else
{
if (mode == "out") // avoid deprecation warning for ZoomViewTransitionMode.OUT
{
targetSnapshot = getSnapshot(startView, 0, targetSnapshotGlobalPosition);
}
else
{
targetSnapshot = getSnapshot(endView, 0, targetSnapshotGlobalPosition);
}
}
}
/**
* @private
*
* @langversion 3.0
* @playerversion AIR 2.5
* @productversion Flex 4.5
*/
override protected function createViewEffect():IEffect
{
// Add a group to contain targetSnapshot.
transitionGroup = new Group();
transitionGroup.includeInLayout = false;
addComponentToContainer(transitionGroup, DisplayObjectContainer(navigator) as UIComponent);
// Disable layout and visibility of our start view as necessary
if (startView)
{
startViewProps = {includeInLayout:startView.includeInLayout,
visible:startView.visible};
startView.includeInLayout = false;
if (mode == "out") // avoid deprecation warning for ZoomViewTransitionMode.OUT
startView.visible = false;
}
// Disable layout and visibility of our start end as necessary
if (endView)
{
endViewProps = {includeInLayout:endView.includeInLayout,
visible:endView.visible};
endView.includeInLayout = false;
if (mode == "in") // avoid deprecation warning for ZoomViewTransitionMode.IN
endView.visible = false;
}
if (targetSnapshot)
addCachedElementToGroup(transitionGroup, targetSnapshot, targetSnapshotGlobalPosition);
transitionGroup.validateNow();
// Initialize our target's transform center.
transitionGroup.transformX = endView.width / 2;
transitionGroup.transformY = endView.height / 2;
// Ensure our alpha is initialized to 0 prior to the start
// of our transition so that the view isn't displayed briefly
// after validation.
if (mode == "in") // avoid deprecation warning for ZoomViewTransitionMode.IN
transitionGroup.alpha = 0;
// Set our blendMode to 'normal' for performance reasons.
transitionGroup.blendMode = BlendMode.NORMAL;
return createZoomEffect(transitionGroup);
}
/**
* @private
*
* @langversion 3.0
* @playerversion AIR 2.5
* @productversion Flex 4.5
*/
override protected function createConsolidatedEffect():IEffect
{
// If we have no cachedNavigator then there is not much we can do.
if (!cachedNavigator && mode == "out") // avoid deprecation warning for ZoomViewTransitionMode.OUT
return null;
// Add a group to contain our snapshot view of the original navigator.
cachedNavigatorGroup = new Group();
cachedNavigatorGroup.includeInLayout = false;
// On zoom out, place the cachedNavigator above the targetNavigator
var index:int = getComponentChildIndex(targetNavigator, targetNavigator.parent as UIComponent);
if (mode == "out") // avoid deprecation warning for ZoomViewTransitionMode.OUT
index++;
addComponentToContainerAt(cachedNavigatorGroup, DisplayObjectContainer(targetNavigator).parent as UIComponent, index);
cachedNavigator.includeInLayout = false;
addCachedElementToGroup(cachedNavigatorGroup, cachedNavigator, cachedNavigatorGlobalPosition);
// Add our temporary transition group to our target navigator's parent
// so we can make it and the original navigator siblings.
if (mode == "out") // avoid deprecation warning for ZoomViewTransitionMode.OUT
{
// We'll be zooming out our cachedNavigatorGroup.
transitionGroup = cachedNavigatorGroup;
}
else
{
transitionGroup = new Group();
transitionGroup.includeInLayout = false;
// We'll be zooming in our snapshot of the new navigator. Host our
// snapshot and make sure it's rendered.
cachedNavigatorGroup.addElement(transitionGroup);
addCachedElementToGroup(transitionGroup, targetSnapshot, targetSnapshotGlobalPosition);
cachedNavigatorGroup.validateNow();
// Hide our real navigator.
endViewProps = {visible:targetNavigator.skin.visible}
targetNavigator.skin.visible = false;
}
transitionGroup.validateNow();
// Initialize our target's transform center.
transitionGroup.transformX = cachedNavigator.getLayoutBoundsWidth(true) / 2 + targetNavigator.getLayoutBoundsX(true);
transitionGroup.transformY = cachedNavigator.getLayoutBoundsHeight(true) / 2 + targetNavigator.getLayoutBoundsY(true);
// Ensure our alpha is initialized to 0 prior to the start
// of our transition so that the view isn't displayed briefly
// after validation.
if (mode == "in") // avoid deprecation warning for ZoomViewTransitionMode.IN
transitionGroup.alpha = 0;
// Set our blendMode to 'normal' for performance reasons.
transitionGroup.blendMode = BlendMode.NORMAL;
return createZoomEffect(transitionGroup);
}
/**
* @private
*
* @langversion 3.0
* @playerversion AIR 2.5
* @productversion Flex 4.5
*/
override protected function cleanUp():void
{
if (!consolidatedTransition)
{
if (startView)
{
startView.includeInLayout = startViewProps.includeInLayout;
startView.visible = startViewProps.visible;
}
if (endView)
{
endView.includeInLayout = endViewProps.includeInLayout;
endView.visible = endViewProps.visible;;
}
if (transitionGroup)
removeComponentFromContainer(transitionGroup, UIComponent(DisplayObjectContainer(navigator)));
}
else
{
if (cachedNavigatorGroup)
removeComponentFromContainer(cachedNavigatorGroup, UIComponent(DisplayObjectContainer(targetNavigator).parent));
if (endViewProps)
targetNavigator.skin.visible = endViewProps.visible;
}
transitionGroup = null;
cachedNavigator = null;
cachedNavigatorGroup = null;
endViewProps = null;
startViewProps = null;
scaleEffect.removeEventListener("effectUpdate", scaleEffectUpdateHandler);
scaleEffect = null;
super.cleanUp();
}
//--------------------------------------------------------------------------
//
// Private Methods
//
//--------------------------------------------------------------------------
/**
* @private
* Shared helper routine which serves as our effect factory for both standard
* and consolidated transitions.
*/
protected function createZoomEffect(zoomTarget:Object):IEffect
{
// This will be a composite control, initialized with the appropriate target
// depending on the transition mode (in/out).
var parallel:Parallel = new Parallel;
parallel.target = zoomTarget;
// Create fade effect to gradually fade our zoom target, we don't fade for the
// duration of the effect as this degrades overall transition performance, we
// simply fade near the point of first appearance or disappearance.
var fadeEffect:Fade = new Fade();
fadeEffect.duration = (mode == "in") ? // avoid deprecation warning for ZoomViewTransitionMode.IN
duration * .4 : duration * .6;
if (mode == "out") // avoid deprecation warning for ZoomViewTransitionMode.OUT
fadeEffect.startDelay = duration * .4;
fadeEffect.alphaTo = (mode == "out") ? // avoid deprecation warning for ZoomViewTransitionMode.OUT
0 : 1;
fadeEffect.alphaFrom = (mode == "out") ? // avoid deprecation warning forZoomViewTransitionMode.OUT
1 : 0;
// Create scale effect to zoom in/our our target from or to our
// specified minimum scale.
scaleEffect = new Scale();
scaleEffect.duration = duration;
scaleEffect.easer = easer;
scaleEffect.scaleXFrom = scaleEffect.scaleYFrom =
(mode == "out") ? // avoid deprecation warning for ZoomViewTransitionMode.OUT
1 : minimumScale;
scaleEffect.scaleXTo = scaleEffect.scaleYTo =
(mode == "out") ? // avoid deprecation warning for ZoomViewTransitionMode.OUT
minimumScale : 1;
scaleEffect.addEventListener("effectUpdate", scaleEffectUpdateHandler);
parallel.addChild(fadeEffect);
parallel.addChild(scaleEffect);
return parallel;
}
/**
* @private
* Ensures transform matrix is updated even if layout is disabled.
*/
private function scaleEffectUpdateHandler(e:Event):void
{
transitionGroup.validateDisplayList();
}
}
}