One of my biggest issues with the Flex Viewstack element is the lack of built-in transitions to choose from. So in this demonstration, I will show you how to create your own. We will first create a reusable animation factory class that we can call from anywhere in our application. Then, I will demonstrate how we can put the animation factory to work by creating a slide transition for a viewstack component. Note that Flex has a built in Slide object, and if you don’t mind mixing up your ActionScript behaviors with your MXML display, then that is also a viable option worth looking into for accomplishing the same end result. Just like anything you’ll find on this site though, we want to build abstraction and reusability into our code.
Step 1: Create the Animation Factory
Our first step is to create the animation factory class. We can instantiate this object from anywhere in an application and call the animateTo method, passing it the x, y position that we want it to end at, and the duration in milliseconds that we want the animation to last.
ObjectSlider.as
package {
import flash.display.DisplayObject;
import flash.events.Event;
import flash.events.TimerEvent;
import flash.utils.Timer;
import flash.utils.getTimer;
/**
* @author Dan Orlando
*/
public class ObjectSlider
{
// The object being animated
private var _target:DisplayObject;
// Each time an animation starts we record its start time
private var _startTime:Number;
// The duration of the animation in milliseconds
private var _duration:Number;
// Record the target's start position
private var _startX:Number;
private var _startY:Number;
// Record the difference between the starting and ending positions
private var _deltaX:Number;
private var _deltaY:Number;
public function ObjectSlider() { }
// Starts an animation that moves the target object from its current
// position to the specified new position over a specified period of time.
public function animateTo(target:DisplayObject, toX:Number,
toY:Number, duration:Number):void {
//store where the target was when the animation started
_target = target;
_startX = _target.x;
_startY = _target.y;
// calculate difference between target's start position
// and final destination
_deltaX = toX - _target.x;
_deltaY = toY - _target.y;
_startTime = getTimer();
//store how long the animation should take
this._duration = duration;
//update target's position each time a screen update occurs
_target.addEventListener(Event.ENTER_FRAME, enterFrameHandler);
}
// Updates the target's position on each ENTER_FRAME event
private function enterFrameHandler(event:Event):void {
var elapsed:Number = getTimer()- _startTime;
var percentDone:Number = elapsed/_duration;
if (percentDone < 1) {
updatePosition(percentDone);
}
else {
updatePosition(1);
_target.removeEventListener(Event.ENTER_FRAME, enterFrameHandler);
}
}
// Sets the position of the target object
private function updatePosition(percentDone:Number):void {
_target.x = _startX + (_deltaX * percentDone);
_target.y = _startY + (_deltaY * percentDone);
}
}
}
Step 2: Create the MXML component
For our MXML display component, we will create a simple canvas that initializes a “helper” class that contains all of the behavior and functionality for the component in ActionScript. Note that when we instantiate the helper class, we pass it the current instance of our canvas component as a display object argument using the this keyword. We also place two left and right arrow buttons here, which we will use to move the viewstack back and forward by changing the selectedIndex value of the ViewStack when one of the buttons is clicked.
CustomCanvas.mxml
<?xml version="1.0" encoding="utf-8"?>
<mx:Canvas xmlns:mx="http://www.adobe.com/2006/mxml" width="750" height="550"
creationComplete="init()">
<mx:Script>
<![CDATA[
import com.danorlando.util.ViewStackHelper;
private var _helper:ViewStackHelper;
private function init():void {
_helper = new ViewStackHelper(this);
}
]]>
</mx:Script>
// place additional mxml components that should not be part
// of the viewstack here, like buttons, labels, etc...
<mx:HBox x="693" y="56" horizontalGap="5">
<mx:Button x="64" y="13" id="backBtn" styleName="iconButtonBack"
width="16" height="16"/>
<mx:Button x="85" y="13" id="forwardBtn" styleName="iconButtonForward"
width="16" height="16"/>
</mx:HBox>
</mx:Canvas>
ViewStackHelper.as
package com.danorlando.util
{
import com.danorlando.display.components.CustomCanvas;
import com.danorlando.effects.animation.ObjectSlider;
import flash.display.DisplayObject;
import flash.events.MouseEvent;
import mx.containers.ViewStack;
public class ViewStackHelper
{
private var _parent:CustomCanvas;
private var _viewStack:ViewStack;
private var _currentIndex:Number;
private var _slider:ObjectSlider = new ObjectSlider();
public function ViewStackHelper(canvas:Canvas) {
_parent = object;
addListeners();
createChildren();
}
// add event listeners to the forward and back buttons
public function addListeners():void {
_parent.backBtn.addEventListener(MouseEvent.CLICK, viewStackBack);
_parent.forwardBtn.addEventListener(MouseEvent.CLICK, viewStackForward);
}
// create and add the viewstack and it's children
public function createChildren():void {
_viewStack = new ViewStack();
_parent.addChild(_viewStack);
_viewStack.selectedIndex = 0;
_viewStack.x = 39;
_viewStack.y = 72;
_viewStack.width = 675;
_viewStack.height = 402;
var vsChild1:Canvas = new Canvas();
_viewStack.addChild(vsChild1);
// add other components and params to the 1st index/child here
var vsChild2:Canvas = new Canvas();
_viewStack.addChild(vsChild2);
// add additional components and params to the 2nd index/child here
var vsChild3:Canvas = new Canvas();
_viewStack.addChild(vsChild3);
// add additional components and params to the 3rd index/child here
// get the first child that is about to be displayed in the viewstack
var curChild:DisplayObject = _viewStack.getChildAt(_currentIndex);
// set the x-position of the child to the x-pos of the viewstack + 600
curChild.x = _viewStack.x + 600;
// now slide the child into view
_slider.animateTo(curChild, _viewStack.x-40, 0, 500);
}
// event handler for the back button in the parent container
private function viewStackBack(event:Event):void {
if(_currentIndex > 0) {
_currentIndex = _currentIndex - 1;
var newChild:DisplayObject = _viewStack.getChildAt(_currentIndex);
// set the x-position of the new child 600 pixels to the right
newChild.x = _viewStack.x + 600;
// use the animateTo method of our animation factory to slide the new
// child into position
_slider.animateTo(newChild, _viewStack.x-40, 0, 500);
// set the new index
_viewStack.selectedIndex = _currentIndex;
// set the forward button to visible if it is hidden
if (!_parent.forwardBtn.visible) {
_parent.forwardBtn.visible = true
}
// if the _currentIndex property is 0, then hide the back button
if (_currentIndex == 0) {
_parent.backBtn.visible = false;
}
}
}
// event handler for the forward button
private function viewStackForward(event:Event):void {
// get the highest index value of the viewstack
var lastIdx:Number = _viewStack.numChildren-1;
if(_currentIndex < lastIdx) {
_currentIndex = _currentIndex + 1;
var newChild:DisplayObject = _viewStack.getChildAt(_currentIndex);
// place the incoming child 600px to the left
newChild.x = _viewStack.x - 600;
// slide the new child into position using our animation factory
_slider.animateTo(newFaq, _viewStack.x-40, 0, 500);
// set the new index of the viewstack
_viewStack.selectedIndex = _currentIndex;
// make the back button visible if it is hidden
if(!_parent.backBtn.visible) {
_parent.backBtn.visible = true }
// hide the forward button if we are at the last index
if (_currentIndex == lastIdx) {
_parent.forwardBtn.visible = false;
}
}
}
}
}
Ok, so we’ve got a few things going on here in the helper class. The first thing we do is set the Canvas object that was passed in the constructor to our local (i.e. private) canvas property which is of type CustomCanvas. We then add the event listeners to the forward and back buttons of the parent container – which is the Canvas object that was passed in the constructor – with the addListeners method. We then instantiate the ViewStack and add its children with the createChildren method. You will see the use of our animation factory with the call to _slider.animateTo. Notice that we are initially setting the position of the child off to the left or the right, depending on which button was clicked, then we slide the child into place. Lastly, in order to ensure that we never get a runtime error because the forward button was clicked when we were already viewing the last child, or the back button was clicked when we were already on the first child, we simply set the visible property to false for the respective button based on said conditions.
Possibly Related Posts:
Posted by Dan Orlando on October 14th, 2008 :: Filed under
Tutorials,
User ExperienceTags ::
Actionscript,
animation,
component,
effects,
Flex,
navigation,
transition,
tutorials,
viewstack