To learn about the perspective tools available in Silverlight 3, a good place to start is my friend Corey Schuman's post.

I had a conversation with Jesse Liberty at MIX09 this past week, and we talked about different techniques for animating various objects. Out of that conversation, I wanted to explore how to go about emulating a 3D object with the new Perspective Tools in Blend/Silverlight 3.

I spent some time putting together an application that simulates a 3D playing card rotation. If you have Silverlight 3 installed, you can see that application here. The project files are available at the end of this post.

The basic functionality for the project involves two images - a "card front" image, which in this case was the Jack of Clubs, and a "card back" image, which is a standard blue backed deck. The two images are aligned right on top of one another. The 3D perspective manipulations are enabled and bound to sliders as Corey describes in the article linked above. I added a bit of code in the code-behind file to "watch" for certain ranges and collapse the visibility of the "card back" image when the "card front" should be showing. There are two tricks involved with this.

First, the card face image is loaded and by default is scaled to -1X. This reverses the image. This is because the perspective tools actually flip around the image. If it was not scaled to -1X, the card front would actually show up backwards when it's rotated and made visible.

The second is the drop shadow bitmap effect I added. This one was a bit confusing - in Joe Stegman's session on the new features added to Silverlight 3, he made the point several times that bitmap effects were the last step before an object is written to the screen. If this were the case, the effects would not be affected by the perspective transforms, but they appear to be as the drop shadow flips along with the transform. At any rate, this was easy enough to solve. I grouped my card object into another canvas, and moved the effect off of the canvas containing the card images to the new container.

Let's take a look at some code. Following is the "CardEffectContainer" Canvas object, which is where the actual drop shadow effect is applied to the object. Inside of that is the "Jack_Clubs" Canvas object, which contains the Canvas.Projection used to make the card appear as though it is rotating. You can also see the "CardFront" and "CardBack" images. As I mentioned above, notice that the CardFront image is scaled -1X.

 
<Canvas x:Name="CardEffectContainer" Height="252" Width="185" Canvas.Left="300" Canvas.Top="110">
	<Canvas.Effect>
		<DropShadowEffect Opacity="0.5" BlurRadius="8"/>
	</Canvas.Effect>
	<Canvas x:Name="Jack_Clubs" Height="252" Width="185" IsHitTestVisible="False">
		<Canvas.Projection>
			<PlaneProjection x:Name="CardRotation" RotationX="0" RotationY="0" RotationZ="0"/>
		</Canvas.Projection>
		<Image x:Name="CardFront" Height="252" Width="185"
                             Source="cardFront.png" RenderTransformOrigin="0.5,0.5">
			<Image.RenderTransform>
				<TransformGroup>
					<ScaleTransform ScaleX="-1"/>
					<SkewTransform/>
					<RotateTransform/>
					<TranslateTransform/>
				</TransformGroup>
			</Image.RenderTransform>
		</Image>
		<Image x:Name="CardBack" Height="252" Width="185" Canvas.Left="0" Source="cardBack.png"/>
	</Canvas>
</Canvas>
 

After that, I have 3 Canvas object, each containing a Slider and associated TextBlock for display. Following is the listing for the SliderY control. Notice that the Value is using Binding to tie the Slider Control's Value to RotationY property in the "CardRotation" Canvas' PlaneProjection shown in the above listing. This makes it so I don't need to write any code in order to allow the Slider to affect the rotation value.

 
<Canvas x:Name="YRotationStuff" Height="37" Width="200" Canvas.Left="20" Canvas.Top="491">
	<Slider x:Name="SliderY" Width="200"
              Value="{Binding ElementName=CardRotation, Mode=TwoWay, Path=RotationY}"
              Minimum="0" Maximum="360" Canvas.Top="19"/>
	<TextBlock x:Name="SliderYValue" Text="Y Rotation: 0.00" TextWrapping="Wrap" Foreground="#FFFFFFFF"/>
</Canvas>
 

I'm not going to show the X and Z rotation sliders here in this post, but they look just like the Y Slider code shown above. In my code behind, I needed to watch the rotations in order to hide the card back at the appropriate times. This was done by setting up two event listeners as shown below.

 
SliderY.ValueChanged += new RoutedPropertyChangedEventHandler(Slider_ValueChanged);
SliderX.ValueChanged += new RoutedPropertyChangedEventHandler(Slider_ValueChanged);
 

Notice that both listeners call the same event handler - Slider_ValueChanged - which is shown following. This event handler starts out by updating the text visible to the user. I orignally had this property bound but wanted to do some formatting on the text, so pushed it to the code behind.

After that, you'll see a series of "if" statements that test ranges of values to determine the rotational state of the card. For example, the first statement checks to see if the card has been rotated along the X axis (tilting the card forward or backwards) more than 90 degrees, or less than 270 degrees.

That condition would result in the card front being visible (to visualize this, hold a playing card straight out in front of you and rotate it towards you - when you pass 90 degrees, you can see the card front, when you pass 270 degrees, you're viewing the card back again).

Since we're dealing with multiple axes here, we also need to check the Y rotation value. If the card has been rotated along the Y axis less than 90 degrees, or more than 270 degrees, then we know that the X and Y values are in a range where the card front should be visible, and we collapse the CardBack image from view. Easy, right?

You can see the other set of tests used to determine the card front visibility in the listing.

 
private void Slider_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
{
    SliderXValue.Text = "X Rotation: " + String.Format("{0:0.00}", SliderX.Value);
    SliderYValue.Text = "Y Rotation: " + String.Format("{0:0.00}", SliderY.Value);
 
    if (SliderX.Value > 90 && SliderX.Value < 270 && (SliderY.Value < 90 || SliderY.Value > 270))
    {
        CardBack.Visibility = Visibility.Collapsed;
    }
 
    else if (SliderX.Value > 90 && SliderX.Value < 270 && (SliderY.Value > 90 && SliderY.Value < 270))
    {
        CardBack.Visibility = Visibility.Visible;
    }
 
    else if ((SliderX.Value < 90 || SliderX.Value > 270) && (SliderY.Value < 90 || SliderY.Value > 270))
    {
        CardBack.Visibility = Visibility.Visible;
    }
 
    else if ((SliderX.Value < 90 || SliderX.Value > 270) && (SliderY.Value > 90 || SliderY.Value < 270))
    {
        CardBack.Visibility = Visibility.Collapsed;
    }
}
 

That handles X and Y rotations, but what about Z? The Z-axis runs directly into your screen and would spin the card like a propeller (assuming the transform origin were in the center as it is here). Since the visibility of the card front is handled when the X and Y sliders are manipulated, all we need to do is update the feedback we're giving the user when the Z-slider is manipulated (remember that the value is bound through the XAML, so no code is necessary to update the actual Z rotation).

This is done with a simple event listener:

 
SliderZ.ValueChanged += new RoutedPropertyChangedEventHandler(SliderZ_ValueChanged);
 

and its associated handler, shown here:

 
private void SliderZ_ValueChanged(object sender, RoutedPropertyChangedEventArgs e)
{
    SliderZValue.Text = "Z Rotation: " + String.Format("{0:0.00}", SliderZ.Value);
}
 

From that, you get a fairly convincing 3D object rotation that was created from just a couple of images. Of course, a playing card is flat, so it's easy to manipulate in this manner. If you have all of the Silverlight 3 bits installed and would like the grab the project file, it's here.

I've spent the last week or so working with Ryan Loghry, a good friend of mine that is an illustrator, putting together a Nintendo "Duck Hunt"-like game in Silverlight (no, I did not include the dog). I'm really pleased with the results, and it was a good opportunity to learn more about how Illustrator/Blend/Visual Studio/Silverlight all work together. Ryan created the illustrations, and then essentially handed me completed XAML files with which to work. Sound effects were pulled from sound effects CDs, and a couple of them were borrowed from Duke Nuke'm. The complete package is about 775K in size.

I may take the opportunity to detail some of the challenges in future blog entries as time permits. This was a really fun project to watch come together.

The game can be seen/played here (make sure your speakers are on!):
http://designwithsilverlight.com/csharp/birdhunt/default.html

Also, if you're interested in seeing more of Ryan's excellent illustration work, you can find him here:
http://ryanloghry.com/

Hi Dave. =)

Expanding a bit upon my previous entry on animated clipping paths, here is an example that may have a more practical application - using an animated clipping path to create glowing edge effects. Mind you, it's not the prettiest example in the world, but it illustrates my point.

For this project, I used two images. One is the outline of the state of Oregon, and the second is the same outline with a different color and a 10px outer glow filter applied in Photoshop (it's a little hard to see the glow on the second image here on a white background).

base image

glow image

As you might expect, the base layer is brought into Blend as an image object, as is the glow layer. Both were set to the same dimensions (590x481), and the same position so that they overlay. I then created a canvas object the same size as the images named "clip".

For the glow effect, I want it to go from the bottom left of the image to the top right, so I added a rectangle that was 10px wide, rotated it around 45 degrees or so, and made sure it was tall enough to span the entire outline of Oregon at the widest point. With the rectangle positioned at the lower left of my outline image and selected on the Objects palette, I Ctrl+clicked the "clip" canvas, right-clicked, and selected Path/Make Clipping Path.

The result is shown in the image below. The glow layer is placed inside the clipping canvas, and will look like it has disappeared since it falls outside of the clipping area.

screen 1

Like the prior example, animation on the clipping path is used to finish the effect. I created a new timeline called "glow", moved the timeline marker to 2, then used the direct selection tool to pick all four control points that make up the rectangle. I then used the arrow keys (+ Shift) to move the rectangle to the top right of the outline.

The last thing to do for this example was to play the timeline, so I added an event handler to the root canvas:

<Canvas
xmlns="http://schemas.microsoft.com/client/2007"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Width="800" Height="600"
x:Name="rootCanvas" Background="#FF9B9B9B"
Loaded="canvasLoaded">

And then added the following function to one of the JavaScript files in the project:

function canvasLoaded(sender){
sender.findName("glow").Begin();
}

You can view the example project, or download a zip.

The ability to perform vertex animation on clipping paths opens up some interesting possibilities effects-wise. I spent some time playing around with animated clipping paths, and came up with an example project to illustrate a technique that mimics an EKG readout.

Start by creating a new project in Blend (December preview). Since an EKG is wide, but not very tall, set the canvas width to 800, and height to 285.

In the sample project, I changed the name of the canvas to "rootCanvas" because I use that as my standard naming convention.

Next, add a rectangle that is the same size as the root canvas, select No Fill on the brushes palette, and add a black 1px stroke to the rectangle. This creates a border for the root canvas.

At this point, my project looked like this:

<Canvas
 xmlns="http://schemas.microsoft.com/client/2007"
 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
 Width="800" Height="285"
 x:Name="rootCanvas">
 <Rectangle Width="800" Height="285" Stroke="#FF000000" x:Name="canvasStroke"/>
</Canvas>

For the EKG pattern, you can Google "EKG" and find a suitable image over which you will trace. Once you have one, bring it into Blend on it's own layer, and lock the layer to avoid inadvertantly moving it. Create a path object and trace over the EKG shape in the reference graphic. The path used in this example had no fill, and a 1.5px thick stroke that was colored #FF357414. Once you're done tracing, you can remove the reference image and use the direct select tool to tweak the points in your path if necessary. In the example project, the path is named "heartbeatPath". Your project should look something like this:

screen 1

To add a clipping mask, add a new canvas to the project named "clip", and make it the same size as the root canvas. Add a rectangle object that is 800x10 to the clip canvas and position it at -10 left. This will position the rectangle just outside of the clipping canvas. The intention here is to make a very small, off-canvas clipping region that will clip the EKG path object once it is animated.

screen 2

To create the clipping area, click the rectangle, and then while holding the Ctrl key, click the "clip" canvas object. With both selected, right-click the selection on the Objects and Timeline palette and select Path/Make Clipping Path. 

In order for the clipping region to affect the EKG path, the heartbeatPath object needs to be in the clipping canvas. This can be done by dragging and dropping the heartbeatPath object onto the clip object. Once this is done, the heartbeat will disappear because it is being clipped.

The next step is to create the animation to simulate the heartbeat. In Blend, select the New Storyboard button, then click OK on the Create Storyboard dialog that opens (the default values are fine).

The animation for the clipping region is a very simple, linear animation. In the timeline area, move the yellow current frame marker to 2. On the toolbar, click on the direct selection tool, and then click the "clip" object on the objects palette.

Select the leftmost two points on the rectangle, by first clicking the top one, and then Ctrl-clicking the bottom one. Both should be selected. Use either the right arrow key on the keyboard, or the Shift + right arrow combination to move the points to the right until they align with the right edge of the root canvas.

screen 3

The animation is done, so close the storyboard.

This particular animation, should repeat, so edit the storyboard XAML by adding the "RepeatBehavior" property.

<Storyboard x:Name="Storyboard1"> becomes <Storyboard x:Name="Storyboard1" RepeatBehavior="Forever">

At this point, opening the project results in... nothing.

The storyboard doesn't automatically play when the page is opened. To do this, you can use triggers in the XAML, but I have become accustomed to doing it via JavaScript - if there are a lot of storyboards running, I find it easier to catch problems when I am controlling them from script. If your preference is to use triggers, that's fine too.

Open the default.html file in your favorite editor, and add the following code beneath the other JavaScript references:

 <script type="text/javascript" src="Silverlight.js" mce_src="Silverlight.js"></script>
 <script type="text/javascript" src="Default_html.js" mce_src="Default_html.js"></script>
 <script type="text/javascript" src="Page.xaml.js" mce_src="Page.xaml.js"></script>
 <script type="text/javascript" language="javascript">
 <!--//
  function canvasLoaded(sender) {
   sender.findName("Storyboard1").begin();
  }
 //-->
 </script>

Also, be sure to update the silverlightControlHost height and width style to match the dimensions of the root canvas.

This script locates the storyboard named "Storyboard1", and tells it to begin. The function is called "canvasLoaded", but it has not yet been linked to an event, meaning nothing will call the script and cause it to do its thing. That is the last thing you will need to do to the XAML file, so head back over to Blend and edit the rootCanvas XAML to include the "Loaded" event:

<Canvas
 xmlns="http://schemas.microsoft.com/client/2007"
 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
 Width="800" Height="285"
 x:Name="rootCanvas"
 Loaded="canvasLoaded">

Opening the default.html file in a browser at this point should produce something similar to this.

From a high level, there's a lot of flexibility in this example. The path color can easily be changed by changing the color of the stroke. The path can have an image inserted behind it to act as a backdrop, or a fill color can be added to the rectangle to put some color behind the EKG pulse. With some simple modifications to the timeline, it's easy to access the keyframes by script, and by manipulating those values over time, cause the pulse to speed up.

Download the example.

Photo Gallery Wall

An attractive image gallery containing 16 images. Images and their captions (if configured) can be viewed by clicking. If no images are selected, the gallery will automatically switch to slideshow mode after 10 seconds. Includes a how-to document that provides an overview of the creation process using Blend and detailed description of functionality! View     Download (706K ZIP)

LCD Clock Thumbnail

An LCD clock that shows the time, day, and date. The included tutorial will help you find out how this LCD clock was built in Expression Blend, and then learn how to make it display the time and date utilizing the Javascript date object.

View    Download (2.5MB ZIP)