The Corona Labs Blog
Posted on . Written by

Flying rocketCorona SDK provides advanced animation capabilities through the Spritesheet API, but there are also features in place that allow you to more perform basic animations with existing display objects (to include animated sprite objects). Whether you need to rotate an object continuously, fade its opacity in and out, move it from one location to another, or any combination of the three—Corona’s transition library has you covered.

This tutorial will mainly cover the transition.to() function, which is what you’ll be using the most when using Corona’s transition library.

As a basic overview, transition.to() takes two parameters: the object to be animated, and a table that contains the ending properties (usually a combination of rotation, alpha, x, and y) of that object, the time you wish for the transition to take, and optionally a function you wish to be called once the transition is finished. Once the transition begins, the object’s will be “tweened” from its current values to the ones you specified.

If you haven’t spent a significant amount of time developing an app—especially a game—in Corona, I’ll go ahead and tell you that you’ll probably be using this function quite a bit—it’s incredibly useful. Fortunately, as you’ll soon find out, it’s also incredibly easy.

Fading an Object

We’ll start with something very basic. We’re going to create an object, and simply have it fade to an invisible state. As you may already know, the alpha property of a display object is responsible for an object’s opacity (e.g. how transparent or opaque the object is), so that’s the property we’ll pass into the transition.to() function to tween it from 1.0 (completely opaque) to 0 (completely transparent).

In the following example, we’ll set the entire transition to last for 3 seconds (3000 milliseconds).


local obj = display.newImage( "image.png" )

-- center the object
obj.x = display.contentWidth*0.5
obj.y = display.contentHeight*0.5

-- fade object to completely transparent
transition.to( obj, { time=3000, alpha=0 } )

The above code block basically takes our object and “tweens” it’s alpha property from 1.0 (the default alpha value upon object creation) to 0 in 3000 milliseconds (3 seconds).

Simple as that! Now, let’s say you want to remove the object as soon as the object finishes fading. We can specify a function to be called as soon as the transition is finished by using the onComplete parameter.


local obj = display.newImage( "image.png" )

-- center the object
obj.x = display.contentWidth*0.5
obj.y = display.contentHeight*0.5

local function removeObject( object )
object:removeSelf()
obj = nil -- nil out original reference (upvalue)
end

-- fade object to completely transparent
transition.to( obj, { time=3000, alpha=0, onComplete=removeObject } )

NOTE: Observe how the object that the transition goes to can be easily accessed from the first argument to the onComplete listener function. This can be useful if you’re using a single listener for multiple transitions (on different objects), or if the object in question is out of scope of your listener function.

Moving Objects

Something that’s really great about the transition.to() function is that you’re not limited to “tweening” a single property at a time. Let’s take the first code example, and modify it to have the object move from the top-left of the screen to the bottom-right as it fades away.


local obj = display.newImage( "image.png" )

-- set starting position (top-left of screen)
obj.x, obj.y = 0, 0

local function removeObject( object )
object:removeSelf()
obj = nil -- nil out original reference (upvalue)
end

-- some variables to be used in the transition
local end_x = display.contentWidth
local end_y = display.contentHeight

-- fade object to completely transparent and move the object as well
transition.to( obj, { time=3000, alpha=0, x=end_x, y=end_y, onComplete=removeObject } )

Easing Library

Corona’s Easing Library can be used in conjunction with the transition.to() function to manipulate the behavior of transitions via the transition property. By default, this transition property is set to easing.linear, which is a steady, constant tween that’s evenly spaced from the beginning of the transition to the end.

It’s a little difficult to describe how each transition property behaves, so I highly recommend taking a look at the Transition1 and Transition2 sample apps that come packaged with the Corona SDK in the following folders:

  • /SampleCode/Graphics/Transition1
  • /SampleCode/Graphics/Transition2

Studying the sample code and playing with the different transition types (and display object properties that can be tweened) is a great way to not only learn, but also solidify all the knowledge you’ve gained about Corona’s transition library.

Canceling Transitions

In the event you need to stop a transition mid-way through, you can do so by using the transition.cancel() function. However, this function requires that you store a reference to an id of the specific transition you need to cancel.

So before we cancel any transitions, we need to “store” the id of the transition into a variable. Here’s the first code example, modified to store the returned id of the transition into a variable called transition_id.


local obj = display.newImage( "image.png" )

-- center the object
obj.x = display.contentWidth*0.5
obj.y = display.contentHeight*0.5

-- fade object to completely transparent
local transition_id = transition.to( obj, { time=3000, alpha=0 } )

Now, we can cancel the transition at any time by passing that stored id as the first parameter to transition.cancel(). In the following example, we’ll cancel the transition after 1.5 seconds.


local obj = display.newImage( "image.png" )

-- center the object
obj.x = display.contentWidth*0.5
obj.y = display.contentHeight*0.5

-- fade object to completely transparent
local transition_id = transition.to( obj, { time=3000, alpha=0 } )

timer.performWithDelay( 1500, function()
if transition_id then
transition.cancel( transition_id )
transition_id = nil
end
end, 1 )

You may have noticed that in the above example, I first checked to make sure the transition exists before canceling it. The example is simple enough that we know the transition does exist, however, as a best practice you should always check to make sure the transition exists before canceling it (or you’ll get an error).

Also, whenever you call transition.cancel(), be sure to nil-out the variable you used to store the transition id because a skeleton table will be left behind (similar to how one is left behind whenever you call the object:removeSelf() method).

WARNING: Do not remove currently transitioning objects!

If an object that is currently being tweened is removed, your app will crash. This commonly occurs as a result of scene changes. Prior to allowing a scene change to occur, it is best to call transition.cancel() on any potentially active transitions to prevent errors from occurring. A great place for this is within an “exitScene” event listener. More info here: Storyboard Scene Events Explained.

For more information on Corona’s transition library, including a few other useful functions, visit the Transitions API reference page.

12 Responses to “Applying Basic Animation with Transitions”

  1. buder

    Nice tutorial. I’ve been handling all my alpha/position animations through manually setting the properties which can get a bit cumbersome so I will have to start transitioning(no pun intended) to this.

    I do have one question, however. Does anyone knwo if it is possible to pause a transition? I couldn’t find anything on the API page that said you could.

    Reply
  2. W2MD

    Great tutorial.

    Buder, cancelling the transition will stop it and it would just take a little math to be able to calculate how to simulate completing it with a new transition. You just put in the same end point, but instead of the original amount of time, you put in

    (original time * distance left to go) / original total distance.

    That should make the object move at the same speed as in the original transition.

    Just a note to Jon: your easing library hyperlink is off. It should be something more like this: http://developer.anscamobile.com/reference/animation/easing

    Thanks,

    Matt
    W2MD

    Reply
  3. Nevin Flanagan

    It *is* possible, although not entirely recommended, to remove an object that is being transitioned, if your onComplete function is smart about verifying whether the object is still valid. Normally, the easiest way to do this is to check whether some property of the object (i usually use parent) is non-nil before proceeding to work on the object.

    Reply
  4. Joakim

    Where in the code do you pass the reference to the function? I cant see that?

    local function removeObject( object )
        object:removeSelf()
        obj = nil   — nil out original reference (upvalue)
    end

    regards Joakim

    Reply
  5. Mitchell

    I notice in the onComplete handler you set obj to nil. Seems to me that obj is local to the function and so should be removed when the function ends any away? Is there something in Lua that makes this not the case?

    Reply
  6. Star Crunch

    As an addendum to Nevin’s comment, you may also need those same checks if you’re doing something with the object in parallel (say, in a timer).

    From looking at some of the internal members, it seems the transition caches the start and end value and will re-inject the interpolated value back into the stripped-down table that was left over by removeSelf(). This may or may not be harmless, depending on how you use these resuscitated properties (and if you used non-interpolated members too, which will still be missing), but is another point in favor of using parent, since interpolating parent is almost certainly programmer error and should crash early on.

    It’s also worth mentioning that (at least as of the latest public version), it is possible to interpolate plain tables. I’ve actually found this quite handy.

    Unfortunately, if it wasn’t obvious from the above, I’ve had to learn how to deal with removing transitioning stuff. Between various outbursts of profanity, a plausible solution did come to mind, and I guess I’ll mention it here and submit it as a formal bug if no there are no obvious gotchas:

    - When you fire off transition.to(), you check the object.

    Case 1: If it’s a display object (recognized by metatable or laying exclusive claim to the parent property or whatever), indicate that in the transition object.

    Case 2: Otherwise, indicate instead that it’s NOT a display object.

    - Before an update, an object and its transition are compared, and if the “I’m a Case 1 object” invariant has been violated, silently remove it from the list of transitions (maybe also alerting a special case listener). Otherwise, update the object normally.

    Anything stand out as unworkable?

    Reply
  7. Jonathan Beebe

    @Joakim: The removeObject function is referenced within the transition.to() call as the onComplete (table parameter of the second argument).

    @Mitchell: In the onComplete handler, it is ‘object’ that is local to the function and is automatically nil’d out at the end of it. ‘obj’, however, is what I set to ‘nil’ and that is the up-value variable that was originally used to create the object.

    Reply
  8. iNSERT.CODE

    Nice tutorial. The onComplete call is a great feature. You can pass the object that is being transitioned to another function that handles another transition – thus creating more complex animations with two (or more) stages.

    To pass the object being transitioned to the next animation function from the onComplete, you will need to create an anonymous function within that call.

    I used this method for the Corona SDK tutorial I put online a few days ago here:

    Corona SDK Tutorial:Creating, Shuffling & Dealing A Deck of Cards

    The project demonstrates dealing the cards out to predetermined locations on the screen using transitions, and passing them to another function automatically (using onComplete) to pseudo-flip them with another transition.

    Reply

Leave a Reply

  • (Will Not Be Published)