Tuesday, November 27, 2007

Silverlight 1.0 Animation: Checkerboard, blinds, and comb

This post describes how to create a Silverlight 1.0 based checkerboard, blinds, and comb animation. The effect is added to my animation library so you can reuse the effect using a single line of code. You can download the complete source from here. Below is a working sample.



Recipe:

To create your own checkerboard animation using my animation library download the source code and include Animator.jas and XamlObjectFactory.js in your project and reference them in your Silverlight host page. To create an across checkerboard effect, use the following line:

SilverlightRecipes.Animator.checkerAcross('CheckerAcross', sender.findName('ToAnimate'), '1', 10, 10);

The first parameter is a unique ID for the animation storyboard. The ID is required because storyboards added to a UIElment resources must be named. The second parameter is the animation target. The target can be any UIElement. The third parameter is the animation duration in seconds. The third parameter is the number of horizontal checkers, and the fourth parameter is the number of vertical checkers.

To create a top down checkerboard animation use the following line of code:

SilverlightRecipes.Animator.checkerDown('CheckerDown', sender.findName('ToAnimate'), '1', 10, 10);

To create a vertical blinds animation use the following line of code:

SilverlightRecipes.Animator.blindsV('BlindsV', sender.findName('ToAnimate'), '1', 10);

To create a horizontal blinds animation use the following line of code:

SilverlightRecipes.Animator.blindsH('BlindsH', sender.findName('ToAnimate'), '1', 10);

To create a vertical comb animation use the following line of code:

SilverlightRecipes.Animator.combV('CombV', sender.findName('ToAnimate'), '1', 20);

To create a horizontal comb animation use the following line of code:

SilverlightRecipes.Animator.combH('CombH', sender.findName('ToAnimate'), '1', 20);

The idea behind these animations is to use multiple wipe animations with different clippings, start times, and durations to generate the desired effect. For example the clipping for 2*2 checkerboard is a PathGeometry with four PathFigure instances to represent each rectangle of the checkerboard:

<PathGeometry>

<PathGeometry.Figures>

<PathFigure IsClosed="True" StartPoint="0,0" >

<PathFigure.Segments>

<LineSegment Point="0,0" />

<LineSegment Point="0,135" />

<LineSegment Point="0,135" />

</PathFigure.Segments>

</PathFigure>

<PathFigure IsClosed="True" StartPoint="0,135" >

<PathFigure.Segments>

<LineSegment Point="0,135" />

<LineSegment Point="0,270" />

<LineSegment Point="0,270" />

</PathFigure.Segments>

</PathFigure>

<PathFigure IsClosed="True" StartPoint="200,0" >

<PathFigure.Segments>

<LineSegment Point="200,0" />

<LineSegment Point="200,135" />

<LineSegment Point="200,135" />

</PathFigure.Segments>

</PathFigure>

<PathFigure IsClosed="True" StartPoint="200,135" >

<PathFigure.Segments>

<LineSegment Point="200,135" />

<LineSegment Point="200,270" />

<LineSegment Point="200,270" />

</PathFigure.Segments>

</PathFigure>

</PathGeometry.Figures>

</PathGeometry>

And the animation is done by a single storyboard with three PointAnimation instances to generate the wipe effect. The following is an example for a 2*2 checkerboard animation:

<Storyboard Duration="00:00:01" Name="CheckerAcross" >

<PointAnimation Duration="00:00:0.5" BeginTime="00:00:0.5" Storyboard.TargetName="ToAnimate" Storyboard.TargetProperty="(UIElement.Clip).(PathGeometry.Figures)[0].(PathFigure.Segments)[0].(LineSegment.Point)" To="200,0" />

<PointAnimation Duration="00:00:0.5" BeginTime="00:00:0.5" Storyboard.TargetName="ToAnimate" Storyboard.TargetProperty="(UIElement.Clip).(PathGeometry.Figures)[0].(PathFigure.Segments)[1].(LineSegment.Point)" To="200,135" />

<PointAnimation Duration="00:00:0.5" BeginTime="00:00:0.5" Storyboard.TargetName="ToAnimate" Storyboard.TargetProperty="(UIElement.Clip).(PathGeometry.Figures)[0].(PathFigure.Segments)[2].(LineSegment.Point)" To="0,135" />

<PointAnimation Duration="00:00:0.5" BeginTime="00:00:00" Storyboard.TargetName="ToAnimate" Storyboard.TargetProperty="(UIElement.Clip).(PathGeometry.Figures)[1].(PathFigure.Segments)[0].(LineSegment.Point)" To="200,135" />

<PointAnimation Duration="00:00:0.5" BeginTime="00:00:00" Storyboard.TargetName="ToAnimate" Storyboard.TargetProperty="(UIElement.Clip).(PathGeometry.Figures)[1].(PathFigure.Segments)[1].(LineSegment.Point)" To="200,270" />

<PointAnimation Duration="00:00:0.5" BeginTime="00:00:00" Storyboard.TargetName="ToAnimate" Storyboard.TargetProperty="(UIElement.Clip).(PathGeometry.Figures)[1].(PathFigure.Segments)[2].(LineSegment.Point)" To="0,270" />

<PointAnimation Duration="00:00:0.5" BeginTime="00:00:00" Storyboard.TargetName="ToAnimate" Storyboard.TargetProperty="(UIElement.Clip).(PathGeometry.Figures)[2].(PathFigure.Segments)[0].(LineSegment.Point)" To="400,0" />

<PointAnimation Duration="00:00:0.5" BeginTime="00:00:00" Storyboard.TargetName="ToAnimate" Storyboard.TargetProperty="(UIElement.Clip).(PathGeometry.Figures)[2].(PathFigure.Segments)[1].(LineSegment.Point)" To="400,135" />

<PointAnimation Duration="00:00:0.5" BeginTime="00:00:00" Storyboard.TargetName="ToAnimate" Storyboard.TargetProperty="(UIElement.Clip).(PathGeometry.Figures)[2].(PathFigure.Segments)[2].(LineSegment.Point)" To="200,135" />

<PointAnimation Duration="00:00:0.5" BeginTime="00:00:0.5" Storyboard.TargetName="ToAnimate" Storyboard.TargetProperty="(UIElement.Clip).(PathGeometry.Figures)[3].(PathFigure.Segments)[0].(LineSegment.Point)" To="400,135" />

<PointAnimation Duration="00:00:0.5" BeginTime="00:00:0.5" Storyboard.TargetName="ToAnimate" Storyboard.TargetProperty="(UIElement.Clip).(PathGeometry.Figures)[3].(PathFigure.Segments)[1].(LineSegment.Point)" To="400,270" />

<PointAnimation Duration="00:00:0.5" BeginTime="00:00:0.5" Storyboard.TargetName="ToAnimate" Storyboard.TargetProperty="(UIElement.Clip).(PathGeometry.Figures)[3].(PathFigure.Segments)[2].(LineSegment.Point)" To="200,270" />

</Storyboard>

Wednesday, November 7, 2007

Easy Silverlight 1.0 Animations: Wheel

This post describes how to create a PowerPoint like wheel animation using Silverlight 1.0. The effect is added to my animation library so you can reuse the effect using a single line of code. You can download the complete source from here. Below is a working sample.



Recipe:

To create your own wheel animation using my animation library download the source code and include Animator.jas and XamlObjectFactory.js in your project and reference them in your Silverlight host page. To create a wheel with a single spin, use the following line:

SilverlightRecipes.Animator.wheel('WheelSingle', sender.findName('ToWheel'), '1');

The first parameter is a unique ID for the animation storyboard, the second parameter is the object to animate. It can be any UIElement like Canvas, Image, TextBlock, or MediaElement. The last parameter is the animation duration in seconds[.fractionalSeconds].

To create 2 spin wheel animation use the following line:

SilverlightRecipes.Animator.wheel2('Wheel2', sender.findName('ToWheel'), '1');

To create 4 spin wheel use the following line:

SilverlightRecipes.Animator.wheel4('Wheel4', sender.findName('ToWheel'), '1');

To create 8 spin wheel use the following line:

SilverlightRecipes.Animator.wheel8('Wheel8', sender.findName('ToWheel'), '1');

This animation is done by creating a PathGeometry with multiple Figures. Each figure is a triangle drawn with two line segments and IsClosed property set to true. The two lines are initialized with the same coordinates. The following example shows the initial clip for a single spin wheel animation for a 400*270 Rectangle

<Rectangle.Clip>

<PathGeometry>

<PathGeometry.Figures>

<PathFigure IsClosed="True" StartPoint="200, 135">

<PathFigure.Segments>

<LineSegment Point="200, 0"/>

<LineSegment Point="200, 0"/>

</PathFigure.Segments>

</PathFigure>

<PathFigure IsClosed="True" StartPoint="200, 135">

<PathFigure.Segments>

<LineSegment Point="400, 0"/>

<LineSegment Point="400, 0"/>

</PathFigure.Segments>

</PathFigure>

<PathFigure IsClosed="True" StartPoint="200, 135">

<PathFigure.Segments>

<LineSegment Point="400, 270"/>

<LineSegment Point="400, 270"/>

</PathFigure.Segments>

</PathFigure>

<PathFigure IsClosed="True" StartPoint="200, 135">

<PathFigure.Segments>

<LineSegment Point="0, 270"/>

<LineSegment Point="0, 270"/>

</PathFigure.Segments>

</PathFigure>

<PathFigure IsClosed="True" StartPoint="200, 135">

<PathFigure.Segments>

<LineSegment Point="0, 0"/>

<LineSegment Point="0, 0"/>

</PathFigure.Segments>

</PathFigure>

</PathGeometry.Figures>

</PathGeometry>

</Rectangle.Clip>

Then using a PointAnimation the end point of the second line is moved to make the clipping rectangle grow. Once the PointAnimation is done, another animation is started to simulate the wheel effect. The following snippet is the Storyboard of one second single spin wheel:

<Storyboard Name="WheelSingle" Duration="00:00:01">

<PointAnimation BeginTime="00:00:00" Duration="00:00:0.125" Storyboard.TargetProperty="(UIElement.Clip).(PathGeometry.Figures)[0].(PathFigure.Segments)[1].(LineSegment.Point)" Storyboard.TargetName="ToWheel" To="400,0"/>

<PointAnimation BeginTime="00:00:0.125" Duration="00:00:0.25" Storyboard.TargetProperty="(UIElement.Clip).(PathGeometry.Figures)[1].(PathFigure.Segments)[1].(LineSegment.Point)" Storyboard.TargetName="ToWheel" To="400,270"/>

<PointAnimation BeginTime="00:00:0.375" Duration="00:00:0.25" Storyboard.TargetProperty="(UIElement.Clip).(PathGeometry.Figures)[2].(PathFigure.Segments)[1].(LineSegment.Point)" Storyboard.TargetName="ToWheel" To="0,270"/>

<PointAnimation BeginTime="00:00:0.625" Duration="00:00:0.25" Storyboard.TargetProperty="(UIElement.Clip).(PathGeometry.Figures)[3].(PathFigure.Segments)[1].(LineSegment.Point)" Storyboard.TargetName="ToWheel" To="0,0"/>

<PointAnimation BeginTime="00:00:0.875" Duration="00:00:0.125" Storyboard.TargetProperty="(UIElement.Clip).(PathGeometry.Figures)[4].(PathFigure.Segments)[1].(LineSegment.Point)" Storyboard.TargetName="ToWheel" To="200,0"/>

</Storyboard>