|Issue 58 October 10 2008|
Hi my fellow Revolutionaries. I had quite a few requests recently to write up more tutorials. Some of you have asked me "Well, animationEngine is cool, but I am not into games, so what would I use it for?" Rest assured! You too can spice up your Revolution made applications using animationEngine. Let me give you a few examples.
Animation, if used decently, can catch the eye. You might often find a situation, where you need to grow or shrink a stack. Of course you could just set the rect of the stack, or remember its topLeft, set the width and height and restore the topLeft. If you want a stack's expanded height to be 300 and its reduced size to be 150, a code snippet might look like this:
In a button:
on mouseUp lock screen -- a flag custom property that alternates between true and -- false set the cExpanded of this stack to not the cExpanded of this \ stack -- remember the topLeft local tTopLeft put the topLeft of this stack into tTopLeft -- expand or shrink the stack, depending on the custom property if the cExpanded of this stack then set the height of this stack to 300 else set the height of this stack to 150 end if -- finally restore the topLeft set the topLeft of this stack to tTopLeft unlock screen end mouseUp
Works, but looks soooo Os 9 or Win 98, no? We will spice this up using some animationEngine magic now. We make use of some of the easing function AE provides. Make sure stack animationEngine is open and in use. To have access to the animationEngine feature set, you will need the line
start using stack "animationEngine"
at a reasonable place in your project. A good place can be the openStack or preOpenStack handler.
Now apply this script to the button or download the demostack here:
on mouseUp pMouseBtnNo -- a flag custom property that alternates between true and -- false set the cExpanded of this stack to not the cExpanded of \ this stack -- expand or shrink the stack, depending on the custom -- property if the cExpanded of this stack then doResize 150,300,the milliseconds,600 else doResize 300,150,the milliseconds,600 end if end mouseUp on doResize \ pOriginalHeight,pDestinationHeight,pStartTime,pDuration local tElapsed,tHeight,tTopLeft lock screen put the milliseconds - pStartTime into tElapsed if tElapsed<pDuration then put round(aeOvershootEaseOut \ (pOriginalHeight,pDestinationHeight,pDuration,tElapsed)) \ into tHeight put the topleft of this stack into tTopLeft set the height of this stack to tHeight set the topleft of this stack to tTopleft send "doResize" && \ pOriginalHeight,pDestinationHeight,pStartTime, \ Duration to me in 40 millisecs else put the topleft of this stack into tTopLeft set the height of this stack to pDestinationHeight set the topleft of this stack to tTopleft end if unlock screen end doResize
Click the button and wait until it has finished, now click again. Not so bad, aye? You can easily choose another effect. Replace the word aeOvershootEaseOut with aeBounceEaseOut. Apply the script and test again. Also try replacing that whole line with the following:
put round(aeEaseOut \ (pOriginalHeight,pDestinationHeight,pDuration,tElapsed,2)) into \ tHeight
put round(aeEaseInOut \ (pOriginalHeight,pDestinationHeight,pDuration,tElapsed,2)) into \ tHeight
Play with the pDuration parameter and see how it affects the animation.
Growing and shrinking is not bad, so what more fun can you have with a stack and AnimationEngine?
Move the stack to the topLeft corner of your screen and open the messagebox.
aeMoveTo the long ID of this stack,the screenLoc,1000,"bounce"
and hit return
aeMoveTo the long ID of this stack,100,100,1000,"overshoot" aeMoveTo the long ID of this stack,the screenLoc,1000,"inOut"
Of course that does not only work for stacks, but also for any control or group of controls you can have on a card. This can let you slide in controls depending on context, looks nice and keeps the users focus on the controls that just moved in.
Another example I´d like to give is based on a topic that came up in the Revolution forums recently.
The situation was as follows. A user wanted to create a limited drag and drop function. He wanted to be able to drag a field into a colored square and if the label of the field matches the color of that square make it undraggable, otherwise make it snap back to its original position. A situation that screams for animationEngine.
I have set up a small demo stack.
It requires animationEngine to be loaded and in use. Open up animationEngine and type
start using stack "animationEngine"
into the messagebox.
The stack contains 1 button,3 fields and 3 graphics. The lockText property of the fields must be true, the traversalOn should be false. Each field owns a custom property called cInitialPosition, which holds the location the field should snap back to. The script in the reset button calls a handler "resetGame" in the card script.
The card owns this script:
--> all handlers -- quick AE drag and drop Demo on openCard resetGame end openCard on resetGame if "animationEngine" is not among the lines of the \ stacksinuse then if "animationEngine" is among the lines of \ revLoadedStacks("application") then start using stack "animationEngine" else answer "This stack needs animationEngine to run!" exit resetGame end if end if aeLockMoves aeMoveTo the long id of fld "fld,red",the cInitialPosition \ of fld "fld,red",200,"inout" aeMoveTo the long id of fld "fld,green",the \ cInitialPosition of fld "fld,green",200,"inout" aeMoveTo the long id of fld "fld,yellow",the \ cInitialPosition of fld "fld,yellow",200,"inout" aeUnlockMoves set the constrainRectangular of fld "fld,red" to the rect \ of this card set the constrainRectangular of fld "fld,green" to the rect \ of this card set the constrainRectangular of fld "fld,yellow" to the \ rect of this card end resetGame on constrainRectangularExit local tColor put item 2 of the short name of the target into tColor if the mouseloc is within the rect of grc ("grc,"&tColor) \ then set the constrainRectangular of the target to empty aeMoveTo the long id of the target,the loc of grc \ ("grc,"&tColor),300,"overshoot" else aeMoveTo the long id of the target,the cInitialPosition \ of the target,350,"bounce" end if end constrainRectangularExit
So, how does this work?
If you set the constrainRectangular property of an object, animationEngine starts listening to mouseEvents: mouseDown, mouseUp, mouseRelease and mouseMove are the events performed in the drag operation. In order not to interfere with those messages and yet be able to react on the events involved, I introduced some callback messages, that are being sent to the target object. I did this because I always forget to pass messages, yet want to do something useful when a drag starts or ends, or even while a drag is being performed.
These callbacks are being sent and can be trapped in the targets script or higher up the messagepath, without passing them and thus breaking the operation:
constrainRectangularInit -> mouseDown
That´s it for this time, I hope you find this useful. If you want to download and try out animationEngine yourself, you can get it here, its free to try!