Animate a Path filling in - Silverlight - silverlight

Given a closed Path of bezier curves, how would I go about animating it filling in. The fill would have to be non-linear - flowing around an acute angle rather than just a plane uncovering the filled color.
At this point, I'm assuming I'd have to use a WriteableBitmap and do it all myself. thoughts?

A RadialBrush originating from the center and spreading outwards often looks acceptable. See the "Coloring Pages" section of the Kinectimals website for an example.
Another implementation that might work would be to apply the shape to the Path's Clip property, then animate the StrokeThickness property to a very large value.
<Grid x:Name="LayoutRoot" Background="White">
<Grid.Resources>
<Storyboard x:Name="FillShape">
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(Shape.StrokeThickness)" Storyboard.TargetName="path">
<EasingDoubleKeyFrame KeyTime="0:0:0.7" Value="118">
<EasingDoubleKeyFrame.EasingFunction>
<CircleEase EasingMode="EaseOut"/>
</EasingDoubleKeyFrame.EasingFunction>
</EasingDoubleKeyFrame>
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</Grid.Resources>
<Path x:Name="path" Data="M118,128 C182,300 238,342 330,252 C422,162 358,-40.000271 270,37.999855 C182,115.99998 118,128 118,128 z" Margin="117.5,19.95,264.563,181.398" Stretch="Fill" Stroke="Black" UseLayoutRounding="False" Clip="M0.4999969,108.05005 C64.500069,280.04974 120.50012,322.04968 212.50023,232.04984 C304.50034,142.04999 240.50026,-59.949921 152.50015,18.050066 C64.500069,96.050049 0.4999969,108.05005 0.4999969,108.05005 z" StrokeThickness="0"/>
</Grid>

Related

How to dynamically size a rectangular path with animated stroke thickness?

I have a Rectangle and a Path defined by a RectangleGeometry:
<Grid>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<Rectangle Grid.Row="0" Stroke="Red" Width="{Binding RctWidth}"/>
<Path Grid.Row="1" Stroke="Red">
<Path.Data>
<RectangleGeometry Rect="0,0,50,10"/>
</Path.Data>
<Path.Triggers>
<EventTrigger RoutedEvent="Path.Loaded">
<BeginStoryboard>
<Storyboard TargetProperty="StrokeThickness">
<DoubleAnimation RepeatBehavior="Forever" From="1" To="3" Duration="0:0:0.5"/>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Path.Triggers>
</Path>
</Grid>
The rectangle changes its width dynamically according to the binding.
The rectangular Path has an animation applied on its StrokeThickness.
I want the rectangular Path to exactly match that rectangle in size, but in such a manner that the stroke thickness animation won't affect that (the thicker stroke should make the Path actually a little bit bigger than the Rectangle - that's the intended behavior).
How can I do that?
Note, that I cannot use the Stretch="Fill" property on the Path. In that case, the stroke thickness will grow only inside the Paths bounds, but I want to keep the default behavior of stroke's growing both in the inner and outer directions.
Furthermore, I cannot change the view model the Rectangle's width is bound to. It's an external component that I'm not allowed to modify.
I could get rid of that Rectangle actually. The important thing for me is the Path and its dynamically changing width.
As commented, the effect of stroke thickness growing only to the inside can be canceled by negative margins.
For an animation that changes the thickness from 1 to 3, the margin needs to change from 0 to -1 (compensate for half of the thickness change):
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="StrokeThickness" RepeatBehavior="Forever" From="1" To="3" Duration="0:0:0.5"/>
<ThicknessAnimation Storyboard.TargetProperty="Margin" RepeatBehavior="Forever" From="0" To="-1" Duration="0:0:0.5"/>
</Storyboard>
</BeginStoryboard>
With this, you can use your solution with Stretch="Fill", whatever it might look like.

Rotating button content custom button

I defined a custom button here:
<Button x:Class="Views.Controls.RefreshButton"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:Controls="clr-namespace:MahApps.Metro.Controls;assembly=MahApps.Metro"
xmlns:local="clr-namespace:Views.Controls"
mc:Ignorable="d"
d:DesignHeight="64"
d:DesignWidth="64"
Background="Transparent"
Foreground="WhiteSmoke">
<Controls:PackIconModern Width="40" Height="40" Kind="Refresh" Margin="0,-1.3" RenderTransformOrigin="0.5,0.5" />
<Button.RenderTransform>
<RotateTransform x:Name="RefreshButtonTransform" Angle="0"/>
</Button.RenderTransform>
<Button.Triggers>
<EventTrigger RoutedEvent="Button.Click">
<EventTrigger.Actions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="RefreshButtonTransform" Storyboard.TargetProperty="(RotateTransform.Angle)">
<SplineDoubleKeyFrame KeyTime="0:0:0" Value="0" />
<SplineDoubleKeyFrame KeyTime="0:0:1" Value="360" />
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
</EventTrigger.Actions>
</EventTrigger>
</Button.Triggers>
</Button>
I implemented this button into my main view.
But this results in rotating the whole button aroung the top left corner and not the middle point of the button.
You need to set RenderTransformOrigin to middle of the Button
<Button ... RenderTransformOrigin="0.5,0.5">
From MSDN
RenderTransformOrigin has a somewhat nonstandard use of the Point structure value, in that the Point does not represent an absolute location in a coordinate system. Instead, values between 0 and 1 are interpreted as a factor for the range of the current element in each x,y axis. For example, (0.5,0.5) will cause the render transform to be centered on the element, or (1,1) would place the render transform at the bottom right corner of the element.

How does the Indeterminate ProgressRing animation work?

I am trying to understand how the Progress Ring animation works. I want to create something similar for the Windows 7 progress bar, I can not use a third party style, I need to create it myself.
This is the style I am looking at as base for my work.
The template for the ProgressRing has a grid with 6 ellipses inside canvases.
Here one of the Canvases with the Ellipse
<Canvas RenderTransformOrigin=".5,.5">
<Canvas.RenderTransform>
<RotateTransform x:Name="E1R" />
</Canvas.RenderTransform>
<Ellipse x:Name="E1" Style="{StaticResource ProgressRingEllipseStyle}"
Width="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=TemplateSettings.EllipseDiameter}"
Height="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=TemplateSettings.EllipseDiameter}"
Margin="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=TemplateSettings.EllipseOffset}"
Fill="{TemplateBinding Foreground}"/>
</Canvas>
I understand that The RenderTransformOrigin=".5,.5" is set so the animation is aligned to the center of the container, so the ellipses rotate centered.
The animation itself has two parts, first changes on the opacity of the Ellipse over time.
<DoubleAnimationUsingKeyFrames
Storyboard.TargetName="E1"
Storyboard.TargetProperty="Opacity"
BeginTime="0">
<DiscreteDoubleKeyFrame KeyTime="0" Value="1" />
<DiscreteDoubleKeyFrame KeyTime="0:0:3.21" Value="1" />
<DiscreteDoubleKeyFrame KeyTime="0:0:3.22" Value="0" />
<DiscreteDoubleKeyFrame KeyTime="0:0:3.47" Value="0" />
</DoubleAnimationUsingKeyFrames>
And then changes in the angle:
<DoubleAnimationUsingKeyFrames
Storyboard.TargetName="E1R"
BeginTime="0"
Storyboard.TargetProperty="Angle">
<SplineDoubleKeyFrame KeyTime="0" Value="-110" KeySpline="0.13,0.21,0.1,0.7"/>
<SplineDoubleKeyFrame KeyTime="0:0:0.433" Value="10" KeySpline="0.02,0.33,0.38,0.77"/>
<SplineDoubleKeyFrame KeyTime="0:0:1.2" Value="93"/>
<SplineDoubleKeyFrame KeyTime="0:0:1.617" Value="205" KeySpline="0.57,0.17,0.95,0.75"/>
<SplineDoubleKeyFrame KeyTime="0:0:2.017" Value="357" KeySpline="0,0.19,0.07,0.72"/>
<SplineDoubleKeyFrame KeyTime="0:0:2.783" Value="439"/>
<SplineDoubleKeyFrame KeyTime="0:0:3.217" Value="585" KeySpline="0,0,0.95,0.37"/>
</DoubleAnimationUsingKeyFrames>
Is my understanding that this animations changes the angle overtime and with the KeySpline it tells WPF the points of the Bezier curve for the acceleration of the movement.
However, I fail to see how the Radius of the general animation gets decided.
With minor adjustments, you can make the animation on this style work with the WPF ProgressBar, and then if you set something like this:
<ProgressBar x:Name="Progress" Width="100" Height="100" Background="Aquamarine" IsIndeterminate="True">
You can see the Ellipses rotating around the 100x100 Square of the ProgressBar.
I don't get how it decides what Radius to use, I feel I am missing something very obvious. I would like to make the animation sun-subscribe to the 100x100 Box, instead of having the curve the animation follows go outside its boundaries. How can I achieve this? How is the Radius being determined?
I'm not familiar with animation, so I might just be making things worse. But since it's using rotation around the center of the square, the radius should be the distance from the original ellipse to the center. And it looks like its "set" here:
Width="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=TemplateSettings.EllipseDiameter}"
Height="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=TemplateSettings.EllipseDiameter}"
Margin="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=TemplateSettings.EllipseOffset}"
I'm guessing this puts the first ellipse in the corner, causing it to rotate outside of the square. You would want it set to one side to keep it in. I'm guessing that the Margin determines it's location.

VS2012 Setup like WPF window animated SizeToContent

I'm trying to achieve the same animation than VS2012 setup window, autosizing and centering on every content size change in a nice animated way.
The problem is that it can't be done purely by code as I don't know the final window size (for what I rely on SizeToContent="WidthAndHeight"), but letting SizeToContent="WidthAndHeight" by it's own does not allow me to animate the transition
Is there any way to do it?
I think the simplest way to achieve this is to use custom visual states within your window class. I made a small test project that you can download here: https://dl.dropboxusercontent.com/u/14810011/ResizingWindow.zip
You need Visual Studio 2012 to execute it.
The Main Window XAML looks like this:
<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:ResizingWindow"
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity" xmlns:ei="http://schemas.microsoft.com/expression/2010/interactions"
x:Name="Window" x:Class="ResizingWindow.MainWindow"
Title="MainWindow" Width="350" Height="300" WindowStyle="None" ResizeMode="NoResize" WindowStartupLocation="CenterScreen">
<Window.DataContext>
<local:MainWindowViewModel />
</Window.DataContext>
<Grid>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="ExtendedStates">
<VisualStateGroup.Transitions>
<VisualTransition GeneratedDuration="0:0:0.6">
<VisualTransition.GeneratedEasingFunction>
<CubicEase EasingMode="EaseOut"/>
</VisualTransition.GeneratedEasingFunction>
</VisualTransition>
</VisualStateGroup.Transitions>
<VisualState x:Name="Normal">
<Storyboard>
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Opacity)" Storyboard.TargetName="TextBlock">
<EasingDoubleKeyFrame KeyTime="0" Value="0"/>
</DoubleAnimationUsingKeyFrames>
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(FrameworkElement.Height)" Storyboard.TargetName="Window">
<EasingDoubleKeyFrame KeyTime="0" Value="300"/>
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<VisualState x:Name="Extended">
<Storyboard>
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(FrameworkElement.Height)" Storyboard.TargetName="Window">
<EasingDoubleKeyFrame KeyTime="0" Value="400"/>
</DoubleAnimationUsingKeyFrames>
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Opacity)" Storyboard.TargetName="TextBlock">
<EasingDoubleKeyFrame KeyTime="0" Value="1"/>
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Grid.RowDefinitions>
<RowDefinition Height="300"/>
<RowDefinition Height="100"/>
</Grid.RowDefinitions>
<Border Background="#FF6C6C6C">
<Grid>
<TextBlock HorizontalAlignment="Center" TextWrapping="Wrap" Text="Hey, I here is some really cool content." VerticalAlignment="Top" FontSize="32" FontFamily="Segoe UI Light" TextAlignment="Center" Margin="0,50,0,0"/>
<CheckBox Content="I want to see more" HorizontalAlignment="Center" VerticalAlignment="Bottom" Margin="0,0,0,15" IsChecked="{Binding ShowAdditionalContent}">
<i:Interaction.Behaviors>
<ei:DataStateBehavior Binding="{Binding ShowAdditionalContent}" Value="False" TrueState="Normal" FalseState="Extended"/>
</i:Interaction.Behaviors>
</CheckBox>
<Button Content="" HorizontalAlignment="Right" VerticalAlignment="Top" FontFamily="Segoe UI Symbol" FontSize="21.333" Style="{DynamicResource ButtonStyle}" Margin="0,5,5,0" Click="CloseMainWindow"/>
</Grid>
</Border>
<Border Grid.Row="1" Background="#FF383838">
<TextBlock x:Name="TextBlock" TextWrapping="Wrap" Text="You can see this, when the check box is activated." FontFamily="Segoe UI Light" FontSize="18.667" TextAlignment="Center" HorizontalAlignment="Center" VerticalAlignment="Center" Foreground="Silver"/>
</Border>
</Grid>
</Window>
The aspects you have to notice are the following:
The main window consists of a grid whose second row is hidden by default. This is achieved by setting the window height to 300 while the grid actually uses 400 logical units. One could also calculate this height dynamically during runtime, but for this simple example, this is not necessary.
The second row becomes visible when the "Extended" visual state is activated. This is actually done using the check box which updates the corresponding view model and the attached DataStateBehavior (this is part of the Blend SDK) that responds to it. When the state is changed, this behavior ensures that the corresponding visual state is activated, i.e. "Normal" when the checkbox is unchecked and "Extended" when it is checked.
The WindowStyle is set to None and the ResizeMode is set to NoResize. This ensures that no border is shown around the window. There is also the option to set AllowTransparency to true but I wouldn't recommend that as this has some serious performance implications. Notice that the default Minimize, Maximize/Restore and Quit buttons will not be present in this modus, too.
Please feel free to ask if you have further questions.

How to create a Silverlight Pendulum Swing 3D text effect

I'm seeing this text effect in tons of commercials, and web sites lately.
And I've found ways to do it in Flash, and JavaScript, but nothing that would directly help me achieve it in Silverlight.
Here's an example of the effect:
http://activeden.net/item/pendulum-swing-3d-component-as3/85056
Basically the idea is the text is on a billboard and if flipped into view along the top horizontal axis.
Any one know of a tutorial or an approach to achieve this effect. I haven't been able to recreate it to where the effect matches and seems natural.
The 3D perspective appearance needed by this animation can be acheived by animation a PlaneProjection. The overshoot and then back swing of the "pendulum" can probably be approximated using a BackEase easing function.
Here is a rough attempt, its close but you will probably need to finesse the numbers a little more to get a smoother result (final settling isn't quite right):-
<Grid x:Name="LayoutRoot" Background="White">
<Grid.Resources>
<Storyboard x:Name="Swing">
<DoubleAnimationUsingKeyFrames Duration="0:0:1" Storyboard.TargetName="Notice"
Storyboard.TargetProperty="(Border.Projection).(PlaneProjection.RotationX)">
<EasingDoubleKeyFrame KeyTime="0:0:0.75" Value="15">
<EasingDoubleKeyFrame.EasingFunction>
<BackEase EasingMode="EaseOut" Amplitude="1.3" />
</EasingDoubleKeyFrame.EasingFunction>
</EasingDoubleKeyFrame>
<EasingDoubleKeyFrame KeyTime="0:0:1" Value="0">
<EasingDoubleKeyFrame.EasingFunction>
<BackEase EasingMode="EaseOut" Amplitude="2" />
</EasingDoubleKeyFrame.EasingFunction>
</EasingDoubleKeyFrame>
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</Grid.Resources>
<Border x:Name="Notice" Background="Orange" CornerRadius="5" HorizontalAlignment="Left" VerticalAlignment="Top" Margin="10" >
<Border.Projection>
<PlaneProjection RotationX="90" CenterOfRotationY="0" />
</Border.Projection>
<TextBlock FontSize="24" FontWeight="Bold" Foreground="Yellow">NICE EFFECT?</TextBlock>
</Border>
<Button Content="Go" Height="35" HorizontalAlignment="Left" Margin="214,13,0,0" Name="button1" VerticalAlignment="Top" Width="142" Click="button1_Click" />
</Grid>
Code:-
private void button1_Click(object sender, RoutedEventArgs e)
{
((PlaneProjection)Notice.Projection).RotationX = 90;
Swing.Begin();
}

Resources