Friday, June 27, 2008

Animation in-depth with Silverlight 2.0 Beta – Part One

Download Sample Code

In-Depth analysis of animation with Silverlight 2.0 Beta.

Contents [hide]

Introduction to Animation in Silverlight

Animation allows us to create attractive user interfaces. Animation is used to apply dazzling effects such as spin a logo or video, make text scroll, make images grow when the mouse is over them etc. Animation is much like varying the property value over time as far as Silverlight 2.0 is concerned. This will be clear if someone takes a closer look at the animated stuff done in Silverlight/WPF applications. For example, it is possible to make an element grow by increasing its Width and Height or changing its Color value or its opacity in a specified duration.

The Size, Color and Value of an element can be varied over a period of time by setting the duration in the Storyboard. Storyboard controls animations with a timeline, and provides object and property targeting information for its child animations. The real definition of animation is - "An illusion that is created by quickly cycling through a series of images". Our brain perceives the group of images as a single changing scene. In Television broadcast or Film this illusion is created using cameras that record a number of pictures - frames - each slightly different from the last displayed, in a specific amount of time by capturing them in a timeline.

Silverlight too has its timeline to specify what actions to perform at specified time intervals. In Silverlight all animations inherit from the Timeline object; therefore all animations are specialized type of Timelines. A timeline represents a segment of time. To specify the length of the segment, when it should start and stop, how many times it will repeat, how fast time progresses in that segment, Silverlight uses Timelines.

Before digging deep into animation, here are two sample programs to give you an idea on what animations do in Silverlight, or what can be done with animations. This is just a simple animated sample to show you the animation in action. You can modify this code to make it more effective. The sample program below animates two videos one after another:

Figure 1: Silverlight Animation in Action

Silverlight Animation in Action

The above is the first video in this application, which has been scaled, translated and rotated to get the animated effect. After this video finishes another one starts by spinnig over the first video. Check the picture of this animation:

Figure 2: The second animation in action

The second animation in action

You should be excited by now seeing the power of Silverlight animation. This application contains three MediaElements. Two of them have been animated and I have used the other for a five seconds video which shows the text “Powered by Silverlight” together with the Silverlight logo. The complete code is shown below, but don’t dig deeper into this before learning the basics, which will come next in this article.

Listing 1: Page.xaml

  1. <UserControl
  2. xmlns="http://schemas.microsoft.com/client/2007"
  3. xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  4. x:Class="SilverlightVideoAnimation3.Page"
  5. Loaded="Page_Loaded"
  6. Width="1024" Height="680"
  7. xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
  8. xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
  9. mc:Ignorable="d">
  10. <UserControl.Resources>
  11. <Storyboard x:Name="VideoTimeline">
  12. <DoubleAnimationUsingKeyFrames
  13. Storyboard.TargetName="mediaElement"
  14. Storyboard.TargetProperty="(UIElement.Opacity)"
  15. BeginTime="00:00:00">
  16. <SplineDoubleKeyFrame KeyTime="00:00:00" Value="0"/>
  17. <SplineDoubleKeyFrame KeyTime="00:00:02" Value="1"/>
  18. DoubleAnimationUsingKeyFrames>
  19. <DoubleAnimationUsingKeyFrames
  20. Storyboard.TargetName="mediaElement"
  21. Storyboard.TargetProperty="(UIElement.RenderTransform).
  22. (TransformGroup.Children)[0].(ScaleTransform.ScaleX)"
  23. BeginTime="00:00:00">
  24. <SplineDoubleKeyFrame KeyTime="00:00:02" Value="1"/>
  25. <SplineDoubleKeyFrame KeyTime="00:00:03" Value="1.5"/>
  26. <SplineDoubleKeyFrame KeyTime="00:00:07" Value="1.5"/>
  27. <SplineDoubleKeyFrame KeyTime="00:00:09" Value="3.594"/>
  28. DoubleAnimationUsingKeyFrames>
  29. <DoubleAnimationUsingKeyFrames
  30. Storyboard.TargetName="mediaElement"
  31. Storyboard.TargetProperty="(UIElement.RenderTransform)
  32. .(TransformGroup.Children)[0].(ScaleTransform.ScaleY)"
  33. BeginTime="00:00:00">
  34. <SplineDoubleKeyFrame KeyTime="00:00:02" Value="1"/>
  35. <SplineDoubleKeyFrame KeyTime="00:00:03" Value="1.5"/>
  36. <SplineDoubleKeyFrame KeyTime="00:00:07" Value="1.5"/>
  37. <SplineDoubleKeyFrame KeyTime="00:00:09" Value="3.594"/>
  38. DoubleAnimationUsingKeyFrames>
  39. <DoubleAnimationUsingKeyFrames
  40. Storyboard.TargetName="mediaElement"
  41. Storyboard.TargetProperty="(UIElement.RenderTransform)
  42. .(TransformGroup.Children)[3].(TranslateTransform.X)"
  43. BeginTime="00:00:00">
  44. <SplineDoubleKeyFrame KeyTime="00:00:03" Value="0"/>
  45. <SplineDoubleKeyFrame KeyTime="00:00:04" Value="56"/>
  46. <SplineDoubleKeyFrame KeyTime="00:00:05.9000000" Value="-135"/>
  47. <SplineDoubleKeyFrame KeyTime="00:00:07" Value="-196"/>
  48. <SplineDoubleKeyFrame KeyTime="00:00:09" Value="-62"/>
  49. DoubleAnimationUsingKeyFrames>
  50. <DoubleAnimationUsingKeyFrames Storyboard.TargetName="mediaElement"
  51. Storyboard.TargetProperty="(UIElement.RenderTransform)
  52. .(TransformGroup.Children)[3].(TranslateTransform.Y)"
  53. BeginTime="00:00:00">
  54. <SplineDoubleKeyFrame KeyTime="00:00:03" Value="0"/>
  55. <SplineDoubleKeyFrame KeyTime="00:00:04" Value="1"/>
  56. <SplineDoubleKeyFrame KeyTime="00:00:05.9000000" Value="168"/>
  57. <SplineDoubleKeyFrame KeyTime="00:00:07" Value="-41"/>
  58. <SplineDoubleKeyFrame KeyTime="00:00:09" Value="59.5"/>
  59. DoubleAnimationUsingKeyFrames>
  60. <DoubleAnimationUsingKeyFrames Storyboard.TargetName="mediaElement"
  61. Storyboard.TargetProperty="(UIElement.RenderTransform)
  62. .(TransformGroup.Children)[2].(RotateTransform.Angle)"
  63. BeginTime="00:00:00">
  64. <SplineDoubleKeyFrame KeyTime="00:00:05.9000000" Value="0"/>
  65. <SplineDoubleKeyFrame KeyTime="00:00:07" Value="360"/>
  66. DoubleAnimationUsingKeyFrames>
  67. <DoubleAnimationUsingKeyFrames Storyboard.TargetName="mediaElement1"
  68. Storyboard.TargetProperty="(UIElement.RenderTransform)
  69. .(TransformGroup.Children)[3].(TranslateTransform.X)"
  70. BeginTime="00:00:00">
  71. <SplineDoubleKeyFrame KeyTime="00:00:30" Value="-465"/>
  72. <SplineDoubleKeyFrame KeyTime="00:00:32" Value="-545"/>
  73. <SplineDoubleKeyFrame KeyTime="00:00:35" Value="-518.5"/>
  74. DoubleAnimationUsingKeyFrames>
  75. <DoubleAnimationUsingKeyFrames Storyboard.TargetName="mediaElement1"
  76. Storyboard.TargetProperty="(UIElement.RenderTransform)
  77. .(TransformGroup.Children)[0].(ScaleTransform.ScaleX)"
  78. BeginTime="00:00:00">
  79. <SplineDoubleKeyFrame KeyTime="00:00:30" Value="1"/>
  80. <SplineDoubleKeyFrame KeyTime="00:00:32" Value="1"/>
  81. <SplineDoubleKeyFrame KeyTime="00:00:35" Value="4.993"/>
  82. DoubleAnimationUsingKeyFrames>
  83. <DoubleAnimationUsingKeyFrames Storyboard.TargetName="mediaElement1"
  84. Storyboard.TargetProperty="(UIElement.RenderTransform)
  85. .(TransformGroup.Children)[0].(ScaleTransform.ScaleY)"
  86. BeginTime="00:00:00">
  87. <SplineDoubleKeyFrame KeyTime="00:00:30" Value="1"/>
  88. <SplineDoubleKeyFrame KeyTime="00:00:32" Value="1"/>
  89. <SplineDoubleKeyFrame KeyTime="00:00:35" Value="5.364"/>
  90. DoubleAnimationUsingKeyFrames>
  91. <DoubleAnimationUsingKeyFrames Storyboard.TargetName="mediaElement1"
  92. Storyboard.TargetProperty="(UIElement.RenderTransform)
  93. .(TransformGroup.Children)[3].(TranslateTransform.Y)"
  94. BeginTime="00:00:00">
  95. <SplineDoubleKeyFrame KeyTime="00:00:30" Value="0"/>
  96. <SplineDoubleKeyFrame KeyTime="00:00:32" Value="0"/>
  97. <SplineDoubleKeyFrame KeyTime="00:00:35" Value="177"/>
  98. DoubleAnimationUsingKeyFrames>
  99. <DoubleAnimationUsingKeyFrames Storyboard.TargetName="mediaElement1"
  100. Storyboard.TargetProperty="(UIElement.RenderTransform)
  101. .(TransformGroup.Children)[2].(RotateTransform.Angle)"
  102. BeginTime="00:00:00">
  103. <SplineDoubleKeyFrame KeyTime="00:00:30" Value="0"/>
  104. <SplineDoubleKeyFrame KeyTime="00:00:32" Value="0"/>
  105. <SplineDoubleKeyFrame KeyTime="00:00:35" Value="360"/>
  106. DoubleAnimationUsingKeyFrames>
  107. Storyboard>
  108. UserControl.Resources>
  109. <Grid x:Name="LayoutRoot" >
  110. <Grid.Background>
  111. <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
  112. <GradientStop Color="#FFFFFFFF" Offset="0.554"/>
  113. <GradientStop Color="#FF282828" Offset="0"/>
  114. <GradientStop Color="#FF595858" Offset="1"/>
  115. LinearGradientBrush>
  116. Grid.Background>
  117. <MediaElement HorizontalAlignment="Stretch"
  118. Margin="281.837005615234,137.998992919922,432.832000732422,0"
  119. VerticalAlignment="Top" Opacity="1" Source="Media/racecar.wmv"
  120. d:LayoutOverrides="HorizontalAlignment, Height" x:Name="mediaElement"
  121. RenderTransformOrigin="0.5,0.5" Height="93.998">
  122. <MediaElement.RenderTransform>
  123. <TransformGroup>
  124. <ScaleTransform/>
  125. <SkewTransform/>
  126. <RotateTransform/>
  127. <TranslateTransform/>
  128. TransformGroup>
  129. MediaElement.RenderTransform>
  130. MediaElement>
  131. <MediaElement Height="81.126" HorizontalAlignment="Right" Margin="0,0,0,0"
  132. VerticalAlignment="Top" Width="144" Source="Media/Halo.wmv"
  133. d:LayoutOverrides="Height" Position="00:00:16"
  134. RenderTransformOrigin="0.5,0.5" x:Name="mediaElement1">
  135. <MediaElement.RenderTransform>
  136. <TransformGroup>
  137. <ScaleTransform/>
  138. <SkewTransform/>
  139. <RotateTransform/>
  140. <TranslateTransform/>
  141. TransformGroup>
  142. MediaElement.RenderTransform>
  143. MediaElement>
  144. <MediaElement x:Name="MediaElement3" Height="98"
  145. HorizontalAlignment="Right"
  146. Margin="0,0,17,137" VerticalAlignment="Bottom"
  147. Width="127" Source="Media/poweredby_silverlight.wmv"
  148. Position="00:00:05">
  149. MediaElement>
  150. Grid>

Listing 2: Page.xaml.cs

  1. using System;
  2. using System.Windows;
  3. using System.Windows.Controls;
  4. using System.Windows.Documents;
  5. using System.Windows.Ink;
  6. using System.Windows.Input;
  7. using System.Windows.Media;
  8. using System.Windows.Media.Animation;
  9. using System.Windows.Shapes;
  10. namespace SilverlightVideoAnimation3
  11. {
  12. public partial class Page : UserControl
  13. {
  14. public Page()
  15. {
  16. // Required to initialize variables
  17. InitializeComponent();
  18. }
  19. private void Page_Loaded(object sender, EventArgs e)
  20. {
  21. VideoTimeline.Begin();
  22. }
  23. }
  24. }

I’m not explaining this code here because earlier I’ve suggested you to take a look at the basics of the animation, before digging into this. You’ll see more in the video animation section of this article.

Animation types

Silverlight provides two types of animation:

  • From/To/By animations.
  • key-frame animations.

Classes that are derived from Timeline provide animation functionality. Here's the inheritance hierarchy:

  • System.Object
  • System.Windows.DependencyObject
  • System.Windows.Media.Animation.Timeline
  • System.Windows.Media.Animation.ColorAnimation
  • System.Windows.Media.animation.ColorAnimation.ColorAnimationUsingKeyFrames
  • System.Windows.Media.Animation.DoubleAnimation
  • System.Windows.Media.Animation.DoubleAnimationUsingKeyFrames
  • System.Windows.Media.Animation.ObjectAnimationUsingKeyFrames
  • System.Windows.Media.Animation.PointAnimation
  • System.Windows.Media.Animation.PointAnimationUsingKeyFrames
  • System.Windows.Media.Animation.Storyboard

When using animations, several of the following properties are needed:

  • AutoReverse: Reverses the animation if it has ended (i.e., moves the element back to where it started).
  • Duration: The duration of an animation, using the syntax hh:mm:ss (hours, minutes, seconds).
  • From: The start value for the animation.
  • To: The end value for the animation.
  • By: A relative value indicating by how much to change the animation (an alternative approach for using To).
  • RepeatBehavior: What to do if the animation has ended; you can provide a (total) duration, a number of times to repeat, or mark it Forever if the animation should repeat endlessly.
  • Storyboard.TargetName: The name of the element that needs to be animated (therefore, we needed to assign a name).
  • Storyboard.TargetProperty: The property of the element that needs to be animated.

To programmatically control the animation, the following methods are used:

  • Begin: Initiates the Storyboard.
  • Pause: Pauses the Storyboard.
  • Resume: Resumes a paused Storyboard.
  • Stop: Stops the Storyboard.
  • Seek: Skips to a specific part of the Storyboard animation.

A key-frame animation animates between a series of values specified using key-frame objects. Key-frame animations are more powerful than From/To/By animations because any number of target values can be specified and we can even control their interpolation method. We’ll see the power of key-frame animation in a moment.

ColorAnimationUsingKeyFrames animates the color of a SolidColorBrush or GradientBrush, DoubleAnimationUsingKeyFrames animates the width and height of FrameworkElements, PointAnimationUsingKeyFrames animates the center position of an EllipseGeometry, ObjectAnimationUsingKeyFrames animates the Fill property from one GradientBrush to another.

In key-fame animations a certain stage in the application must be reached. For example, an object needs to have specific positions. When a key-frame is reached a certain object value must be met. Every key frame needs at least two values or attributes:

  • KeyTime: At what time the key-frame will come into effect.
  • Value: The value that needs to be reached at a given time.

In a Timeline where you have a keyframe after half second with a value of 20, and another one after 1 and half seconds with value of 40, the assigned value of these two frames will be animated between them. How this value is animated from 20 to 40 will be defined by the second keyframe. There are different ways to interpolate the value, and the second keyframe needs to confirm which of these methods is used. Silverlight currently supports three methods: Linear, Discrete and Spline. Here’s an example of key frame animation. This example uses three rectangles to demonstrate Spline, Discrete and Linear key-frames. To create it, open Microsoft Expression Blend 2.5 March Preview.

Figure 3: Opening Expression Blend

Opening Expression Blend

Change the Grid’s background to LinearGradientBrush. In this case Grid is our root element. Create three rectangles inside the section. Change also the background of the rectangles to LinearGradientBrush. By now, you should have change your visual interface to something like the following.

Figure 4: Visual Interface for Silverlight KeyFrameAnimations

Visual Interface for Silverlight KeyFrameAnimations

Add a Storyboard by clicking the + button which resides under the objects and timeline tab. Select rectangle1 in the Objects and Timeline panel. Translate the x property to 50 when Playhead position is in 0:00.000. Again translate the x property to 544 and the Playhead position will be in 0:01.000. At last set the x property to 0 again when the Playhead position is 0:03.000. Now select rectangle2, move the timeline where Playhead position is 0:00.300 and translate the x property of the TranslateTransform that will be applied to our second rectangle. Set the x value to 544 when the Playhead position is 0:01.000 and set it to 0 when Playhead position is 0:03.000. Do the same for the rectangle3. After completing this step you should have a timeline like the following.

Figure 5: KeyFrameAnimations Timeline

KeyFrameAnimations Timeline

This will generate the following Xaml behind the scenes, in the section:

Listing 3: Page.xaml

  1. <Storyboard x:Name="Storyboard1">
  2. <DoubleAnimationUsingKeyFrames Storyboard.TargetName="rectangle1"
  3. Storyboard.TargetProperty="(UIElement.RenderTransform)
  4. .(TransformGroup.Children)[3].(TranslateTransform.X)"
  5. BeginTime="00:00:00">
  6. <SplineDoubleKeyFrame KeyTime="00:00:00" Value="0"/>
  7. <SplineDoubleKeyFrame KeyTime="00:00:01" KeySpline="0.6,0 0.9,0" Value="544"/>
  8. <SplineDoubleKeyFrame KeyTime="00:00:03" KeySpline="0.6,0 0.9,0" Value="0"/>
  9. DoubleAnimationUsingKeyFrames>
  10. <DoubleAnimationUsingKeyFrames Storyboard.TargetName="rectangle2"
  11. Storyboard.TargetProperty="(UIElement.RenderTransform)
  12. .(TransformGroup.Children)[3].(TranslateTransform.X)"
  13. BeginTime="00:00:00">
  14. <SplineDoubleKeyFrame KeyTime="00:00:00.3000000" Value="282"/>
  15. <SplineDoubleKeyFrame KeyTime="00:00:01" Value="540"/>
  16. <SplineDoubleKeyFrame KeyTime="00:00:03" Value="0"/>
  17. DoubleAnimationUsingKeyFrames>
  18. <DoubleAnimationUsingKeyFrames Storyboard.TargetName="rectangle3"
  19. Storyboard.TargetProperty="(UIElement.RenderTransform)
  20. .(TransformGroup.Children)[3].(TranslateTransform.X)"
  21. BeginTime="00:00:00">
  22. <SplineDoubleKeyFrame KeyTime="00:00:00.3000000" Value="284"/>
  23. <SplineDoubleKeyFrame KeyTime="00:00:01" Value="542"/>
  24. <SplineDoubleKeyFrame KeyTime="00:00:03" Value="0"/>
  25. DoubleAnimationUsingKeyFrames>
  26. Storyboard>

Now change the to in the last section of the DoubleAnimationUsingKeyFrames. Discrete key frames, like DiscreteDoubleKeyFrame, create sudden jumps between values. For example, if the rectangle is at the starting position, it can suddenly appear at the 500 position. The animated property won’t change until the key frame's key time is reached, at which point the animated property goes suddenly to the target value.

Change the to in the middle DoubleAnimationUsingKeyFrames section. Add the KeySpline property in and your code should look like this:

  1. <Storyboard x:Name="Storyboard1">
  2. <DoubleAnimationUsingKeyFrames Storyboard.TargetName="rectangle1"
  3. Storyboard.TargetProperty="(UIElement.RenderTransform)
  4. .(TransformGroup.Children)[3].(TranslateTransform.X)"
  5. BeginTime="00:00:00">
  6. <SplineDoubleKeyFrame KeyTime="00:00:00" Value="50"/>
  7. <SplineDoubleKeyFrame KeyTime="00:00:01" KeySpline="0.6,0 0.9,0" Value="544"/>
  8. <SplineDoubleKeyFrame KeyTime="00:00:03" KeySpline="0.6,0 0.9,0" Value="0"/>
  9. DoubleAnimationUsingKeyFrames>
  10. <DoubleAnimationUsingKeyFrames Storyboard.TargetName="rectangle2"
  11. Storyboard.TargetProperty="(UIElement.RenderTransform)
  12. .(TransformGroup.Children)[3].(TranslateTransform.X)"
  13. BeginTime="00:00:00">
  14. <LinearDoubleKeyFrame KeyTime="00:00:00.3000000" Value="10.2"/>
  15. <LinearDoubleKeyFrame KeyTime="00:00:01" Value="544"/>
  16. <LinearDoubleKeyFrame KeyTime="00:00:03" Value="0"/>
  17. DoubleAnimationUsingKeyFrames>
  18. <DoubleAnimationUsingKeyFrames Storyboard.TargetName="rectangle3"
  19. Storyboard.TargetProperty="(UIElement.RenderTransform)
  20. .(TransformGroup.Children)[3].(TranslateTransform.X)"
  21. BeginTime="00:00:00">
  22. <DiscreteDoubleKeyFrame KeyTime="00:00:00.3000000" Value="10"/>
  23. <DiscreteDoubleKeyFrame KeyTime="00:00:01" Value="544"/>
  24. <DiscreteDoubleKeyFrame KeyTime="00:00:03" Value="0"/>
  25. DoubleAnimationUsingKeyFrames>
  26. Storyboard>

Run the application and check the difference between them.

Principles of animation

Here I’m going to present the principles of animation extracted from the book MS Expression Blend Bible. It’s quite an effective concept to bring liveliness to your animated application.

Adding realism is a key factor when creating an eye catching animation. To me, as a developer of Silverlight applications, everyone should follow the principles of animation to add realism and effectiveness when applying animation to their application. As far as storytelling is concerned animation, for example when developing a game your animation may proceed like this:

  • Creating a script for storyboard.
  • Record voices, Sound effects and background music, and layer them together to create a soundtrack.
  • Designing visuals in more detail.
  • Animate those visuals.

If someone wants to put emphasis on adding interest and realism behind animation, the following principles should be kept in mind:

Squash and stretch

When an object is squashed, its volume should remain equal to the volume it has when it is not squashed. Squashing and stretching a bouncing ball can add realism, appeal or both. When two objects bump together, they both squash inward at the point of impact and stretch in a perpendicular direction, unless they are made of a completely rigid material. Then they regain their original shape. It’s important as an animator to always give the squashed and stretched shape the same volume as the original shape. The object shouldn’t appear to gain any mass.

Timing and motion

This is where user’s attentions are attracted and gives sense of weight, importance and anticipation to an object. Timing and motion are an integral part of every animation. Timing and motion can give a sense of the weight of an object. When an object takes a long time to get going and to stop, it gives the impression that it’s heavier than something that takes less time. User's attention can be attracted using motion in two ways: A moving object or character can attract the attention if everything else in the scene is still and a still object can attract attention if everything else in the scene is moving.

Staging

Plays the big part when the attention of the audience to the area of the screen containing the information is purposely brought. In animation, staging is the process by which the eye is drawn to the part of the screen that the animator wants to emphasize. For example, if a character lifts an eyebrow to indicate that he’s got a plan, you may want to show the action in a close-up so that the audience notices it.

Overlapping action and follow-through

It's handy when motion is given a practical or appearance of reality. Starting one action before another action is complete. Action that continues to occur after an action has mostly completed. For example, follow-through occurs when a ball is caught by a hand and the hand moves back to indicate that the ball pushed the hand back. Or when a walking character takes a step and his hand continues to swing.

Arcs

Animating motions along arcs, because most objects in nature don't move along a perfect straight line, this is when arcs are animated along motions. In the natural world, living things usually don’t move in straight lines. When we walk, we move up and down in an arc as we take a step, then straighten up and take the next. All animals move in an arc motion, some more than others. And the trees move with the wind in an arc. Arcs occur both visually and in time. Water in a stream is sometimes slow, sometimes fast. Wind blows, and then diminishes. Rabbits hop, and then stop. Varying the timing and the visual motion of your animation in a natural way can enhance the feeling that you may want it to convey.

These are the elements of animation that must be structured in an organized way in order to give attractiveness and realism no matter what platforms you are working on.

Summary

In the first part of this series we presented the main concepts of animations with Silverlight. We talked about animation types and discussed the key animation principles that you must keep in mind when developing rich applications. In the next part, we’ll dive deep into Storyboards.