Let's review different visual effects while displaying an image, from simple to more sophisticated. In this serie, we'll discuss animation, clipping area and opacity mask. Here, a first post dedicated to clipping.
The image used in this post is a painting from Alfred Sisley (1839-1899), a French impressionnist painter, though British citizen (he asked for French citizenship but died before the Administration took a decision, due to missing papers - nothing is never new in this world).
Project / source code
The effect is based on a clipping area. Nothing is displayed outside the clipping area, here an ellipse. We give a name (ellGeo) to this ellipse and animate the RadiusX and RadiusY properties. When stb1.Begin() is executed, RadiusX jumps from 0 to 400 in two seconds. Simultaneously, RadiusY jumps from 0 to 300 (to be sure that the 400x300 rectangle is fully inside the ellipse).
<Image x:Name="img" Source="Sisley.jpg" Width="400" Height="300" >
<Image.Clip>
<EllipseGeometry x:Name="ellGeo" Center="200, 150" RadiusX="400" RadiusY="300" />
</Image.Clip>
<Image.Resources>
<Storyboard x:Name="stb1">
<DoubleAnimation Storyboard.TargetName="ellGeo" Storyboard.TargetProperty="RadiusX"
From="0" To="400" Duration="0:0:2" />
<DoubleAnimation Storyboard.TargetName="ellGeo" Storyboard.TargetProperty="RadiusY"
From="0" To="300" Duration="0:0:2" />
</Storyboard>
</Image.Resources>
</Image>
Instead of giving a name to EllipseGeometry, we could write :
<Image x:Name="img" Source="Sisley.jpg" Width="400" Height="300" >
<Image.Clip>
<EllipseGeometry Center="200, 150" RadiusX="400" RadiusY="300" />
</Image.Clip>
<Image.Resources>
<Storyboard x:Name="stb1">
<DoubleAnimation Storyboard.TargetName="img"
Storyboard.TargetProperty="(UIElement.Clip).(EllipseGeometry.RadiusX)"
From="0" To="400" Duration="0:0:2" />
<DoubleAnimation Storyboard.TargetName="img"
Storyboard.TargetProperty="(UIElement.Clip).(EllipseGeometry.RadiusY)"
From="0" To="300" Duration="0:0:2" />
</Storyboard>
</Image.Resources>
</Image>
Fine for the EllipseGeometry, thanks to its two properties (RadiusX and RadiusY) that are of double type. Not so easy with RectangleGeometry that has a property (Rect) with four values ! It's easier to use the PathGeometry.
Project / source code
As PathGeometry, we define one PathFigure (closed figure starting at upper-left corner) and made of three line segments (remember, it's a closed figure). Each line ending point is specified in the Point property. of course of type Point. Our animation will now be based on PointAnimation :
<Image x:Name="img" Source="Sisley.jpg" Width="400" Height="300" >
<Image.Clip>
<PathGeometry>
<PathFigure StartPoint="0,0">
<LineSegment x:Name="ls1" Point="400, 0" />
<LineSegment x:Name="ls2" Point="400, 300" />
<LineSegment Point="0, 300" />
</PathFigure>
</PathGeometry>
</Image.Clip>
<Image.Resources>
<Storyboard x:Name="stb2">
<PointAnimation Storyboard.TargetName="ls1" Storyboard.TargetProperty="Point"
From="0, 0" To="400, 0" Duration="0:0:2" />
<PointAnimation Storyboard.TargetName="ls2" Storyboard.TargetProperty="Point"
From="0, 300" To="400, 300" Duration="0:0:2" />
</Storyboard>
</Image.Resources>
</Image>
Since a path can be made of a succession of paths, we can do more complex animations. For instance :
Project / source code
The clipping path is made of five triangles, all having in common the image's center :
The animation is now made of five animations in succession, each elementary triangle being drawn in one second. The initial Point values in the lsxB LineSegment are such that the image is initially invisible. The values are changed in the corresponding PointAnimation :
<Image x:Name="img" Source="Sisley.jpg" Width="400" Height="300" >
<Image.Clip>
<PathGeometry>
<PathGeometry.Figures>
<PathFigure StartPoint="200,150">
<LineSegment x:Name="ls1A" Point="200, 0" />
<LineSegment x:Name="ls1B" Point="200, 0" />
</PathFigure>
<PathFigure StartPoint="200,150" >
<LineSegment x:Name="ls2A" Point="400, 0" />
<LineSegment x:Name="ls2B" Point="400, 0" />
</PathFigure>
<PathFigure StartPoint="200,150" >
<LineSegment x:Name="ls3A" Point="400, 300" />
<LineSegment x:Name="ls3B" Point="400, 300" />
</PathFigure>
<PathFigure StartPoint="200,150" >
<LineSegment x:Name="ls4A" Point="0, 300" />
<LineSegment x:Name="ls4B" Point="0, 300" />
</PathFigure>
<PathFigure StartPoint="200,150" >
<LineSegment x:Name="ls5A" Point="0, 0" />
<LineSegment x:Name="ls5B" Point="0, 0" />
</PathFigure>
</PathGeometry.Figures>
</PathGeometry>
</Image.Clip>
<Image.Resources>
<Storyboard x:Name="stb3">
<PointAnimation Storyboard.TargetName="ls1B" Storyboard.TargetProperty="Point"
From="200, 0" To="400, 0" Duration="0:0:1" />
<PointAnimation BeginTime="0:0:1" Storyboard.TargetName="ls2B"
Storyboard.TargetProperty="Point" From="400, 0" To="400, 300"
Duration="0:0:1" />
<PointAnimation BeginTime="0:0:2" Storyboard.TargetName="ls3B"
Storyboard.TargetProperty="Point" From="400, 300" To="0, 300"
Duration="0:0:1" />
<PointAnimation BeginTime="0:0:3" Storyboard.TargetName="ls4B"
Storyboard.TargetProperty="Point" From="0, 300" To="0, 0"
Duration="0:0:1" />
<PointAnimation BeginTime="0:0:4" Storyboard.TargetName="ls5B"
Storyboard.TargetProperty="Point" From="0, 0" To="200, 0"
Duration="0:0:1" />
</Storyboard>
</Image.Resources>
</Image>
To restart an animation (just before executing st3.Begin), we need to reinitialize the clipping path, to have the image disappearing :
ls1A.Point = ls1B.Point = new Point(200, 0);
ls2A.Point = ls2B.Point = new Point(400, 0);
ls3A.Point = ls3B.Point = new Point(400, 300);
ls4A.Point = ls4B.Point = new Point(0, 300);
ls5A.Point = ls5B.Point = new Point(0, 0);
Instead of writing
<PointAnimation BeginTime="0:0:3" Storyboard.TargetName="ls4B"
Storyboard.TargetProperty="Point" From="0, 300" To="0, 0"
Duration="0:0:1" /> >
it's possible to write (remember, it's the fourth PointAnimation and we animate the second LineSegment) :
<PointAnimation BeginTime="0:0:3" Storyboard.TargetName="img"
Storyboard.TargetProperty="(UIElement.Clip)
.(PathGeometry.Figures)[3]
.(PathFigure.Segments)[1]
.(LineSegment.Point)"
From="0, 300" To="0, 0" Duration="0:0:1" />
As said Lewis Carroll in "Alice in Wonderland" : "I could have thought of a much more complicated way of doing it, said the Red Queen, immensely proud".
In a forthcoming post, we will show more sophisticated animations, still based on the clipping area.