On this page we discuss the simple Timeline code that creates the 3D text flipping effect. For the sake of clarity, we skip portions relevant to formatting TextFields via TextFormat.
In essence, we build a Sprite, 'textFlipper', that has two Sprite children, 'flipperFront' and 'flipperBack'. It is manipulating the latter two that creates the effect. In our applet, we add two TextFields (frontField and backField) as children of flipperFront and flipperBack but you can add Bitmaps or MovieClips instead.
At the beginning of the script, we declare the relevant variables:
var frontField:TextField;
var backField:TextField;
var textFlipper:Sprite;
var flipperFront:Sprite;
var flipperBack:Sprite;
var flipperWidth:Number=300;
var flipperHeight:Number=150;
var mesArray:Array=[];
var mesCount:int=0;
var isMoving:Boolean=false;
'mesArray' is an array of Strings that provide text when textFlipper flips. 'mesCount' keeps track which message is currently displayed. 'isMoving' is set to 'true' when textFlipper rotates. The variables are not initialized until the last of the timers that create book's animation calls, upon its completion, the function 'setUpFlipper'. The latter, in turn, calls 'setUpTextFlipper' which constructs textFlipper.
function setUpFlipper():void {
setUpTextFlipper();
flipperFront.addEventListener(MouseEvent.CLICK,frontClicked);
flipperBack.addEventListener(MouseEvent.CLICK,backClicked);
this.addEventListener(Event.ENTER_FRAME,onEnter);
}
In setUpTextFlipper we are skipping the parts relevant to formatting and the Strings in mesArray.
function setUpTextFlipper():void {
...........................
mesArray=[...String messages...]
frontField=new TextField();
backField=new TextField();
frontField.width=flipperWidth;
frontField.height=flipperHeight;
backField.width=flipperWidth;
backField.height=flipperHeight;
textFlipper=new Sprite();
this.addChild(textFlipper);
flipperFront=new Sprite();
flipperBack=new Sprite();
flipperFront.addChild(frontField);
flipperBack.addChild(backField);
textFlipper.addChild(flipperFront);
textFlipper.addChild(flipperBack);
//The TextFields will be placed below in such a way that
//the registration point of textFlipper will fall in its center.
// Note the textFlipper is positioned in such a way
//that its center coincides with the projectionCenter set by
//the last timer.
textFlipper.x=220;
textFlipper.y=275;
textFlipper.z=0;
//Here is an important point: both TextFields, frontField and backField,
//are positoned in such a way that the registation point of flipperFront,
//flipperBack, and textFlipper are in their (common) center. If you were
//positioning Bitmaps, in flipperFront and flipperBack, you would want
//to follow the same idea.
frontField.x=-flipperWidth/2;
frontField.y=-flipperHeight/2;
backField.x=-flipperWidth/2;
backField.y=-flipperHeight/2;
//Another important point: in order for flipperBack to show up
//with the correct orientation after rotation, we initially
//rotate it by 180 degrees about the x-axis. You would do the same
//if Bitmaps or other DisplayObjects were placed in the 'flipper'.
flipperBack.rotationX=180;
...........................
frontField.text=mesArray[mesCount];
backField.text="";
//Part of the flipping effect consists of toggling the visibilty
//of each part of the textFlipper.
flipperBack.visible=false;
flipperFront.visible=true;
}
/*
After the user clicks on flipperBack or flipperFront, isMoving is set to 'true'.
Then, when ENTER_FRAME is fired textFlipper is rotated about the x-axis.
To figure out which of the sides should be visible, we use the cosine
of the angle of rotation (converted to radians). When cosine of the angle
is positve, flipperFront should be visible. Otherwise, flipperBack should be
visible. The condition containing the 0.1 value is there to prevent flickering
when visibility changes. The same mechanism would work if instead of TextFields
flipperFront and flipperBack contained other display objects.
Rotation about the x-axis can be replaced by the rotation about the y-axis.
Simply replace all instances of rotationX below by rotationY, and rotationX inside
the function setUpTextFlipper
by rotationY.
*/
function onEnter(e:Event):void {
if(isMoving){
var textRot:Number=textFlipper.rotationX;
var curNormal:Number;
textFlipper.rotationX=(textRot-10)%360;
curNormal=Math.cos(textFlipper.rotationX*Math.PI/180);
if(curNormal>0.1){
flipperFront.visible=true;
flipperBack.visible=false;
}
else if(curNormal<-0.1){
flipperFront.visible=false;
flipperBack.visible=true;
}
else {
flipperFront.visible=false;
flipperBack.visible=false;
}
//The next condition stops rotation after 180 degrees.
if((textFlipper.rotationX%180)==0){
isMoving=false;
}
}
}
/*
When either side of the 'flipper' is clicked, isMoving is set to 'true'
and the next message is assigend to the TextField that is about to show.
*/
function frontClicked(e:MouseEvent):void {
if(isMoving==false){
mesCount=(mesCount+1)%mesArray.length;
backField.text=mesArray[mesCount];
isMoving=true;
}
}
function backClicked(e:MouseEvent):void {
if(isMoving==false){
mesCount=(mesCount+1)%mesArray.length;
frontField.text=mesArray[mesCount];
isMoving=true;
}
}
Download
- Download all files corresponding to the applet: book3d.zip