Silverlight

Silverlight Animation Builder and Chainer

For the last couple of weeks I’ve been mucking around with dynamic animations in Silverlight 2, and I found them quite tiring and verbose to write. I also found it time consuming to link them together in to a series – to run animation a, then b, then c and so on.

So I decided to do something about it, and this is the result.

High Quality (view in browser and download WMV).

I created a new set of classes collectively called the AnimationChainer. It allows you to quickly create animations using a convenient syntax. You will find creating animations with this set of classes is much nicer than the manual way. These animations can then be easily chained together to create more advanced behavior.

The basic overview of the AnimationChainer is:

  • Create groups of animations
  • Add groups to a main animation controller
  • Groups contain multiple animations
  • Animations can be delayed
  • Groups can run in serial or parallel mode (run all animations at once etc)
  • You can make the manager run all groups at once or again, serially (so run a group, which has 3 animations, then run the next group and so on)
  • You can fire callbacks on completion
  • After a manager has started animating, you can control behavior if you add more animations to it.
  • Cancel animations

Unlike some other animation systems out there already in Silverlight 2, this one uses real Timeline based animations and storyboards. A group becomes a storyboard basically, and the animations are added to it (each one can be cumulatively delayed by more and more).

<Sample Code>

Silverlight Animation Chainer Source and Sample

</Sample Code>

Important: Before this sample will work, copy the Windows Vista sample images in to AnimationHelper\Images

Live Demo

Note: Press F5 in between each demo to reset the app to the original state:)

The demo has four examples in it:

The first example demonstrates the ability to cut off an executing animation and start a new one. This happens whenever the user moves the mouse. It also shows delayed start for the groups, each letter starts slightly after the one before it.

The second example throws the Windows Vista sample images in to random positions and random rotations on the screen. It does this 10 times for each set of images. This demonstrates serial animations in groups – only one image per group comes out at a time, but parallel groups, i.e. 10 groups send out one image each at a time.

The third example is a wrap panel which uses keyframe animations to “hop” in content.

The fourth example is the same wrap panel, but this time the animations “pop” in. Animated wrap panels are cool! πŸ™‚

These demonstrate only the tip of the iceburg on what is possible with this class.

I don’t get groups and stuff

So what can you do with this? You could make it so that things first zoom in to view, then they might wiggle a little, then zoom out. The Zoom in would be a ScaleY and ScaleX animation, so you would put these in the same group so they would run together. You would put the second part, the wiggle in to another group so you could make it run only after the first animation had completed.

Fluent Interface

Coding against this class couldn’t be easier.

To create the zoom in animation I spoke of you could do this:

//Set the transform group (you don't have to do this if you element comes from XAML and already has a TransformGroup)
TransformGroup tg = new TransformGroup();
element.RenderTransform = tg;

//Add a scale transform, and set it to scale X and Y to 0
ScaleTransform st = new ScaleTransform();
                
tg.Children.Add(st);

st.SetValue(ScaleTransform.ScaleXProperty, (double)0);
st.SetValue(ScaleTransform.ScaleYProperty, (double)0);

//Create the chainer manager to house the animation groups
AnimationChainManager cm2 = new AnimationChainManager(true); //true here means that each group will run in serial mode, one after the other.

cm2.Add() //Adds a new animation group
	.DoubleAnimation() //Adds a new DoubleAnimation to the group
	.Target(st) //Sets the animation target to the ScaleTransform
	.Property(ScaleTransform.ScaleXProperty) //Sets the Target Dependency Property
	.Duration(new TimeSpan(0, 0, 0, 0, 100)) //Sets the time span of the animation
	.From(0) //Sets the starting position of the animation
	.To(1) //Sets the ending position of the animation
	.Queue() //Adds this animation to the queue (not required, but best practice)
	.DoubleAnimation() //Creates a second animation for the other scale axis
	.Property(ScaleTransform.ScaleYProperty) //Sets the Target Dependency Property. Note we didn't have to set the Target again!
	.From(0) //Sets the starting point and so on.
	.To(1)
	.Queue();


cm2.Begin(false, false); //Start animations with parameters: Don't wait for previous anims to finish, and don't cancel any that are already running

See, easy as! Watch the video to see this in action (download and play with the sample project too).

Supported animation types

The classes support DoubleAnimation, ColorAnimation and PointAnimation. It also supports the key frame based variants of these three types.

List of available commands

  • .Serial – Run the animations within the group serially
  • .Target – The DependencyObject to animate against
  • .Property – The DependencyProperty to animation
  • .DoubleAnimation – New DoubleAnimation
  • .DoubleAnimationK – New DoubleAnimationUsingKeyFrames
  • .PointAnimation – New PointAnimation
  • .PointAnimationK – New PointAnimationUsingKeyFrames
  • .ColorAnimation – New ColorAnimation
  • .ColorAnimationK – New ColorAnimationUsingKeyFrames
  • .KeyFrame – Adds a keyframe to the KeyFrame animation. The offset is cumulative (added on to the last offset)
  • .Context – Some context to fire back with the completion event and callback
  • .From – From value for all animation types
  • .To – To value for all animation types
  • .Duration – The length of this animation
  • .Offset – The offest from the start of the group to start this animation
  • .CompleteAction – Action callback
  • .Reverse – Replay the animation backwards on completion
  • .Queue – Adds the animation to the queue

When adding multiple animations to the same group, you don’t need to keep setting properties like Target and Duration – the system remembers them and does it automatically for you.

The Manager

The manager can be configured to behave in different ways.

  • Play groups sequentially (altered in constructor)
  • Toggle cancel groups current playing and start newly added groups
  • Toggle wait for current groups to finish before playing newly added groups
  • Begin method starts playing the groups
  • GroupDelay method allows you to set a group to wait before animating.

How does it work

Basically this set of classes is just a wrapper for the animation classes. Each group is converted in to a StoryBoard when it’s told to run πŸ™‚ Download the code and check it out for yourself.

Another example – keyframes

This sample is from the main project available for download. It makes the elements of the wrap panel jump on to the screen.

cm2.Add()
.DoubleAnimationK() //Create the keyframe animation
.Target(tt) //Set its target to the TranslateTransform created earlier
.Property(TranslateTransform.YProperty) //Tell it to do the YProperty
.KeyFrame(-height, new TimeSpan(0, 0, 0, 0, 1)) //Add first keyframe
.KeyFrame(-300, new TimeSpan(0, 0, 0, 0, 100)) //Add the second keyframe at 101ms
.KeyFrame(0, new TimeSpan(0, 0, 0, 0, 100)) //Add third keyframe, at 201ms (1 + 100 + 100)
.Queue() //Add to queue
.DoubleAnimationK() //Repeat for other axis
.Property(TranslateTransform.XProperty)
.KeyFrame(-(width + realLeft), new TimeSpan(0, 0, 0, 0, 0))
.KeyFrame(-300,new TimeSpan(0, 0, 0, 0, 100))
.KeyFrame(0,new TimeSpan(0, 0, 0, 0, 100))
.Queue();       

13 thoughts on “Silverlight Animation Builder and Chainer

  1. Just saw your tweet, which brought me here.

    I’ve been working on a general purpose procedural animation framework for a while (I ported my javascript framework from couple years back to Silverlight). I used that to build declarative effects and transitions.

    Daniel then saw it and we discussed building a fluent API on top of the same core (http://www.dimebrain.com/2008/12/composing-animations-with-silverlightfx.html). No doubt there is similarity, and I think you’re both coming from the same perspective.

    You might be interested in checking out Silverlight.FX as well – the core framework I am building. If you do, I’d love to hear what you think…

    Anyway, certainly cool stuff!

  2. Hey Nikhil,

    I hope my tweet caused no offence πŸ™‚

    Fluent API’s are very cool I agree – for a developer like myself I love being able to quickly whip up dynamic animations.

    One of the features I love about my framework is end of animation callback which I can then Lambda in another animation. I’m about to post a little article on using a newer version of my lib to make a snow like effect. Each time a flake reaches the end of its animation, the callback ransomises another animation.

    Also I have automatic chaining: you can ask animations to queue up, run simultaneously etc.. you can also tell new animations to wait until current anims end, or clear out existing running animations and automatically hand off to the new animation.

    I subscribe to your blog and have been following Silverlight.FX – I’ve not had an in depth play however… next I get a chance I shall πŸ™‚

    Cheers,

    Jordan.

  3. Hi Jordan,

    A while back on OzSilverlight I wrote about the third party ecosystem and how the exciting vendors wouldn’t be thinking about grids or charts, but of transitions and animations and layout and navigation. What you have above is very inspiring and really shows the capabilities of SL as a framework. Awesome job. Great idea and nicely executed.

    I’d love to see you take this to the next level. I think you could reduce the syntax and make it even clearer. Instead of creating the animations by their type, perhaps focus on real-world abstractions?

    For example, instead of constructing the AnimationChainManager directly, you could use an extension method to initialize it, and create simplified extensions that build on the framework you have above. It might look like this:

    myImage.Grow().To(300, 600).Over(1.Second()).Begin();

    This would deduce the type of the element, add a scale transform/transform group if it didn’t already exist, and setup the animation.

    You could then couple this with a jQuery style way of managing events. For example:

    myImage.OnClick(m => m.Grow().To(300, 600).Over(1.Second().Begin());

    Groups themselves could be an implementation detail. For example:

    myImage.Grow().To(300, 600).AndAlso.FadeIn().Then.Wait(3.Seconds()).Then.FadeOut().Over(1.Seconds());

    “AndAlso” would group the animation on the left with the one on the right, and “Then” would begin a new group (as I don’t expect you would have nested groups?).

    I really like what you’ve done here, thanks for sharing.

    Paul

  4. Mate, that is a fantastic idea!

    First – I couldn’t agree more on the chart/grid/this and that control.

    I love the extension method idea… It shouldn’t be too difficult as my fluent interface already has the “jQuery” style object being passed around. The beauty is that DependencyObject could probably be extended to get things moving on any object.

    Move, Fade, etc.. all the favouries… love it.

  5. No offence πŸ™‚
    I am just finding that searching on twitter leads to interesting links (relatively new to the whole twitter and tweeting thing)

    Blogging and experimenting is such a competition sometimes – thats why on the team we need to call shotgun sometimes on who is blogging what πŸ™‚

Comments are closed.