Color, Scale & Rotation
We should remember that one of the key differences between working with visual elements
in Flash as opposed to working with visual elements in Silverlight is that Silverlight
stores the visual definitions in XAML. When we start dealing with issues of Scale
and Rotation, this difference becomes more apparent; certain properties are
implicit in Flash whereas they need to be explicitly defined in the XAML for Silverlight.
Scale and Rotation are two such properties. Let's see how this works.
Consider this simple Flash example:
Generated with this code:
Before we can do a similar procedure in Silverlight, we would need to explicitly
define a RenderTransform on the visual element and name the properties on our control
Tile like so:
We could have used LayoutRoot directly as the visual canvas, but we're going to
add a couple more elements to this User Control later, so we left LayoutRoot visually
empty and defined a Canvas child named bkg to be what we actually see. Notice
the RenderTransformOrigin is a way to move the equivalent of the registration
point for this Canvas as well.
The explicit definition of the scale and rotation transformations are found in Lines
10 through 15 above. Since we want to apply more than one transform, we need to
wrap them up in a TransformGroup. The ScaleTransform on Line 12 is named
scale and the RotateTransform is named rotation. Since we will be
controlling them in code, their initial values are all set to have no discernable
effect at the moment.
The code that adds the controls on Page.xaml looks very similar to our ActionScript
logic, but with a couple key differences to note:
At Line 29, rotation is the name we gave the RotationTransform we defined
in Tile, but we still need to be specific and set the Angle of this transform,
since rotation is just the name we gave it and not the property itself. The
same goes for the ScaleX and ScaleY values for our ScaleTransform we named scale
on Line 30. Its also important to notice that full scale in ActionScript 2.0 has
a value of 100, while full scale in Silverlight has a value of 1.0. So we are dealing
with decimals from 0.0 to 1.0 here, not values from 0 to 100. Lines 31 through 34
give a preview of setting color in Silverlight by using a SolidColorBrush,
but we will discuss this further next.
If we ran our Silverlight rebuild, it would look something like this right now:
But let's start turning this project into something a little more fun. I'm going
to make the following changes to Page.xaml and Tile.xaml, adjusting
their sizes a bit:
I changed the size of bkg in Tile.xaml and also added an Ellipse that
would sit outside the influence of our RenderTransforms. The RenderTransforms apply
to the Canvas they are under. In this case, the Canvas is our visual element bkg
and not the local root of this control, LayoutRoot.
Let's beef up the code behind file Tile.xaml.cs to have it accept a rotation
target value and a Storyboard object to animate it procedurally. After the same
X and Y definitions we've been using in every project, the rest of the code for
Tile.xaml.cs looks like this:
On Page.xaml.cs, we want to fill out the UserControl_Loaded() handler
we call from Page.xaml and also create a Storyboard object that will constantly
update the targetAngle and scale values for each Tile. All of the
code except the actual Storyboard animation logic looks like this:
Line 28 is the code that is setting the background color of each bkg canvas
in Tile.xaml. We've already seen a few XAML brushes in previous lessons.
But if you want to manipulate an object's fill or background with a solid color
in code, you use a SolidColorBrush. If you want fine control over the color
directly, you can then use the Color.FromArgb() function to set the color
to anything you like. Whereas Flash typically breaks down a color into its three
byte components (red, green, and blue), Silverlight exposes a fourth byte for the
alpha, a. Therefore, Color.FromArgb() expects fourparameters; alpha,
red, green, and blue. These parameters must explicitly
be of type byte. That's why I need to cast them as such with "(byte)"
before calculating the values. If you follow the logic above in Line 28 through
Line 31, you'll see how the X and Y properties of each Tile are tweaking the green
and blue byte values, giving us a full table of color samples.
Also notice what we're doing in Line 35 and Lines 42 through Line 47. Since Silverlight
doesn't provide an _xmouse and _ymouse property by default for us
to use in our animation logic, we need to duplicate this functionality by having
a local Point object updated whenever the mouseMoved() is called.
With this defined, we can now query this mouse object from anywhere to find
out the current location of the cursor relative to Page.xaml. We will use
this in the main Storyboard's animation loop like this:
Using a little math magic, we determine the distance of each Tile from the
cursor and use this to set each ScaleX and ScaleY value directly (Lines
53 - 55, 58 and 59). We can also use some more trigonometry to determine the angle
from each Tile's origin to the current location of the mouse and have it
rotate to point to it. If the distance is less than 200 pixels, the Tiles will flicker
and spin to point to the mouse. As the mouse moves farther away, the Tiles that
are no longer in the effected area flicker and loop back.
Here is the Silverlight result:
A Variation on the Theme
There is another transform I should probably mention, TranslateTransform. All the
lessons we have created so far have us directly setting the X and Y properties of
our User Controls in a Canvas, so a need to move (or translate) them with a RenderTransform
has come up. But here is a simple variation of the above example that can give us
a substantially different experience. Instead of using a dynamic solid color for
each Tile's bkg, we want to use a picture pic3.png we have added to our solution
as an ImageBrush, painting each Tile according to it's X and Y location. This means
we will need to shift the ImageBrush position for each Tile dynamically as well,
and here is where a TranslateTransform can come in handy.
Here is how we changed Tile.xaml to make this happen:
The actual ScaleTransform and RotateTransform or even code behind Tile.xaml.cs haven't
changed, but now bkg has an ImageBrush defined as its Fill, which can be offset
by using a TranslateTransform.
Here are the changes in Page.xaml.cs that take advantage of this:
We tweak the ImageBrush to move in the opposite direction of where we place the
Tile, so that its position on the Canvas and the appropriate section of the Source
image line up.
Most importantly, though, I also add a strategic PNG Image with a transparent region
in the middle over the whole Tiles collection to visually constrain the area I want
to focus on while decorate the background as well.
This top Image helps sell the whole effect with some subtle shadow work and edges,
all done in Photoshop. It adds depth and realism to the experience and makes the
whole thing seem a bit richer.
Here is the finished Silverlight variation:
While this lesson was on Color, Scale and Rotation, the key line of code used the
trigonometry function Math.Atan2() to get us the target angle values we wanted.
In our next lesson, we'll explore this and other trigonometry functions more closely.
Additional Resources
There are several great Flash developers that have done much over the years to make
Math and Trigonometry more accessible to interactive designers. Here are few books
and blogs by some of my favorites: