Issue 58 * October 10 2008

Beyond Games
animationEngine in regular Applications

by Malte Brill

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.

animationEngine demo

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

or


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.

Type:

aeMoveTo the long ID of this stack,the screenLoc,1000,"bounce"  

and hit return
Next try:

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.

Squares Demo

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
constrainRectangularExit -> mouseUp, mouseRelease
constrainRectangularCallback -> mouseMove (while you drag the object)

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!

 

 

Main Menu What's New

Rev Mentor