In my previous post we made a simple Popfly Silverlight display block. Now we can do something a little more interesting. All of these examples will use the block description from the previous post, so be sure to take a look.
Making a Slideshow
We can make a simple slideshow by fading from one picture to the next. Take a look at the "HelloSilverlight-3" block:
function HelloWorld()
{
HelloWorld.initializeBase(this);
this.index = -1; // index of image being animated
}
HelloWorld.prototype.getXaml = function()
{
// make a 640x480 canvas
return '<Canvas Width="640" Height="480"/>';
};
HelloWorld.prototype.addImage = function(url)
{
// start loading the image
this.addPendingImage(url);
};
HelloWorld.prototype.imageLoaded = function(image)
{
// get our canvas
this.canvas = this.root.children.getItem(0);
// xaml for the image display and animation
var xaml =
'<Image Name="image" Opacity="0" Width="640" Height="480" Stretch="UniformToFill">'
+' <Image.Resources>'
+' <Storyboard Name="fadeIn" Duration="0:0:5">'
+' <DoubleAnimation Storyboard.TargetName="image" Storyboard.TargetProperty="Opacity" To="1" Duration="0:0:1"/>'
+' </Storyboard>'
+' <Storyboard Name="fadeOut" Duration="0:0:1">'
+' <DoubleAnimation Storyboard.TargetName="image" Storyboard.TargetProperty="Opacity" To="0" Duration="0:0:1"/>'
+' </Storyboard>'
+' </Image.Resources>'
+'</Image>';
// create the object and set the image source
var object = this.content.createFromXaml(xaml, true);
object.source = image.source;
// call the onCompleted handler when the image is done animating
object.findName("fadeIn").addEventListener("completed", delegate(this, this.onCompleted));
// add the image to our canvas
this.canvas.children.add(object);
// if this is the first image, start the animation
if(this.index == -1)
{
this.showNextImage();
}
};
HelloWorld.prototype.showNextImage = function()
{
// get the next image
this.index++;
if(this.index >= this.canvas.children.count)
{
this.index = 0;
}
var image = this.canvas.children.getItem(this.index);
// zoom the image in
image.findName("fadeIn").begin();
};
HelloWorld.prototype.onCompleted = function(sender)
{
// start fading the image out
sender.findName("fadeOut").begin();
// show the next image
this.showNextImage();
};
HelloWorld.registerClass('HelloWorld', Popfly.Blocks.SilverBase);
HelloSilverlight-3 Code
We added a few things here to the previous example here. The getXaml call lets us say how big of a canvas we want to work with because it gets automatically added to the root canvas of our Silverlight control . Note that this will automatically get scaled to fit the user's browser optimally. In the imageLoaded callback we added a bunch of xaml to do the fading in and out. We then create an object using that xaml, get a callback to see when the image has faded in, add the image to the canvas, and show it.
The showNextImage function figures out which image is next and starts fading it in. Once the fadeIn has completed, the onCompleted function starts fading it out and starts fading the next one in. Lather, rinse, repeat! Straightforward enough? Is the xaml a little confusing? Let’s break it down:
<Image Name="image" Opacity="0" Width="640" Height="480" Stretch="UniformToFill">
Here we declare our image in xaml with the name "image" so we can easily reference it in the animations, and set the opacity to 0 so it starts out hidden. We also give it a height and width and ask Silverlight to fill that space with our image. That's what "UniformToFill" basically means.
<Storyboard Name="fadeIn" Duration="0:0:5"
<DoubleAnimation Storyboard.TargetName="image" Storyboard.TargetProperty="Opacity" To="1" Duration="0:0:1"/>
</Storyboard>
The two storyboards are similar. One fades the image in, the other fades it out. Note the duration of the fadeIn storyboard is 5 seconds but the DoubleAnimation inside it only lasts 1 second. This is so we can actually see the image for a full 4 seconds before the onCompleted function in our code is called, and we start to fade the image back out.
If you are wondering how the fadeIn storyboard actually calls the onCompleted function in the code, the answer is simple. We add a listener for the storyboard's completed event and make it call our function, like so:
// call the onCompleted handler when the image is done animating
object.findName("fadeIn").addEventListener("completed", delegate(this, this.onCompleted));
Animating Slideshow
The Ken Burns Effect might be familiar to you; it's basically zooming in and moving the pictures around to make them seem more alive. Let's extend the previous example to do this. (HelloSilverlight-4)
function HelloWorld()
{
HelloWorld.initializeBase(this);
this.index = -1; // index of image being animated
}
HelloWorld.prototype.getXaml = function()
{
// make a 400x400 canvas and clip everything to it
return '<Canvas Width="640" Height="480" Clip="M0,0 H640 V480 H0z"/>';
};
HelloWorld.prototype.addImage = function(url)
{
// start loading the image
this.addPendingImage(url);
};
HelloWorld.prototype.imageLoaded = function(image)
{
// get our canvas
this.canvas = this.root.children.getItem(0);
// pick a random spot to zoom the image to / from
var left = Math.random()*-100;
var top = Math.random()*-100;
var zoomFrom, zoomTo;
if(Math.random()>0.5)
{
zoomFrom = Math.random()*0.4 + 1;
zoomTo = 1;
}
else
{
zoomFrom = 1;
zoomTo = Math.random()*0.4 + 1;
}
// xaml for the image display and animation
var xaml =
'<Image Name="image" Opacity="0" Width="740" Height="580" Stretch="UniformToFill">'
+' <Image.Resources>'
+' <Storyboard Name="fadeIn" Duration="0:0:5">'
+' <DoubleAnimation Storyboard.TargetName="image" Storyboard.TargetProperty="Opacity" To="1" Duration="0:0:1"/>'
+' </Storyboard>'
+' <Storyboard Name="zoom">'
+' <DoubleAnimation Storyboard.TargetName="translate" Storyboard.TargetProperty="X" From="'+ top +'" To="'+ left +'" Duration="0:0:6"/>'
+' <DoubleAnimation Storyboard.TargetName="translate" Storyboard.TargetProperty="Y" From="'+ left +'" To="'+ top +'" Duration="0:0:6"/>'
+' <DoubleAnimation Storyboard.TargetName="scale" Storyboard.TargetProperty="ScaleX" From="'+ zoomFrom +'" To="'+ zoomTo +'" Duration="0:0:6"/>'
+' <DoubleAnimation Storyboard.TargetName="scale" Storyboard.TargetProperty="ScaleY" From="'+ zoomFrom +'" To="'+ zoomTo +'" Duration="0:0:6"/>'
+' </Storyboard>'
+' <Storyboard Name="fadeOut" Duration="0:0:1">'
+' <DoubleAnimation Storyboard.TargetName="image" Storyboard.TargetProperty="Opacity" To="0" Duration="0:0:1"/>'
+' </Storyboard>'
+' </Image.Resources>'
+' <Image.RenderTransform>'
+' <TransformGroup>'
+' <TranslateTransform Name="translate"/>'
+' <ScaleTransform Name="scale"/>'
+' </TransformGroup>'
+' </Image.RenderTransform>'
+'</Image>';
// create the object and set the image source
var object = this.content.createFromXaml(xaml, true);
object.source = image.source;
// call the onCompleted handler when the image is done animating
object.findName("fadeIn").addEventListener("completed", delegate(this, this.onCompleted));
// add the image to our canvas
this.canvas.children.add(object);
// if this is the first image, start the animation
if(this.index == -1)
{
this.showNextImage();
}
};
HelloWorld.prototype.showNextImage = function()
{
// get the next image
this.index++;
if(this.index >= this.canvas.children.count)
{
this.index = 0;
}
var image = this.canvas.children.getItem(this.index);
// zoom the image in
image.findName("fadeIn").begin();
image.findName("zoom").begin();
};
HelloWorld.prototype.onCompleted = function(sender)
{
// start fading the image out
sender.findName("fadeOut").begin();
// show the next image
this.showNextImage();
};
HelloWorld.registerClass('HelloWorld', Popfly.Blocks.SilverBase);
HelloSilverlight-4 Code
There's only a few additional lines in this example. We pick a random spot, figure out whether to zoom in or out, create the animation and transforms for it, and then run!
Where Can You Go From Here?
Here's some things you could add to the block:
- More animation effects (rotating, squashing, coloring)
- Captions and descriptions
- Sound effects
- Show multiple pictures side-by-side
- Frames
- Mouse interactivity
Check out some of the other display blocks, such as "photoPile" for more ideas...