How does the Indeterminate ProgressRing animation work? - wpf

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.

Related

Quick One regarding Margins and Storyboards

Just a quick question, I've been searching for ages on Google. I have a storyboard:
<Storyboard x:Key="ViewLeftToRight" AccelerationRatio=".5" DecelerationRatio=".5">
<DoubleAnimation Storyboard.TargetName="ReferenceInfo" Storyboard.TargetProperty="Margin" Duration="0:0:0.15" To="{Binding, Width},0,0,0"/>
</Storyboard>
It doesn't work. I was wondering if there is a way for me to bind the Width of the control to the "left" margin. If I need to use a converter, could you possibly show how it would be written in XAML in the above example?
Thanks!
No, you can't bind an animation to Margin, because it does not define a corresponding dependency property. There are several alternatives, here are two:
Place your object in a Canvas and animate Canvas.Left and Canvas.Top
Define a RenderTransform on your object, and animate its X and Y properties.
1)
<Canvas>
<TextBlock Text="test" x:Name="ReferenceInfo" Canvas.Left="0" Canvas.Top="0" />
</Canvas>
Here your storyboard short target the same element ReferenceInfo, but target the attached properties, which you denote using brackets like "(Canvas.Left)":
<DoubleAnimation
Storyboard.TargetName="ReferenceInfo"
Storyboard.TargetProperty="(Canvas.Left)" Duration="0:0:0.15"
To="{Binding Width}"/>
2)
<TextBlock Text="test" x:Name="ReferenceInfo">
<TextBlock.RenderTransform>
<TranslateTransform x:Name="TranslateReferenceInfo" X="0" Y="0" />
</TextBlock.RenderTransform>
</TextBlock>
The animation would then reference the TranslateTransform itself by name:
<DoubleAnimation
Storyboard.TargetName="TranslateReferenceInfo"
Storyboard.TargetProperty="X" Duration="0:0:0.15"
To="{Binding Width}"/>

How can I animate a storyboard instantly from a new value back to the original value before the animation was applied?

In short, I know how to animate from a current value to a new value, but what I don't know how to do (and can't find) is how to animate back to the value before the animation ran.
In other words, say my current background is yellow. I want to flash the background as red based on some logic, but I want it to fade from that red back to yellow again. Transparent doesn't work here because it replaces the background value with 'Transparent', not fades through it.
Also, I'm not referring to the FillBehavior property which you can use to 'un-apply' the animated value after it's ran, nor am I referring to auto-reversing the animation as that would mean it would have to run forward first meaning we'd get a fade to the red, not an instant pulse as we want.
Only way I've found so far is to do this in code-behind, but that introduces its own issues with logically arranging things. I just want the 'To' value to be set to the pre-animated value. How can that be done?
Use ColorAnimation.From Property only as in this sample:
<StackPanel>
<StackPanel.Resources>
<Style TargetType="TextBox">
<Style.Triggers>
<EventTrigger RoutedEvent="MouseEnter">
<BeginStoryboard>
<BeginStoryboard.Storyboard>
<Storyboard>
<ColorAnimation Storyboard.TargetProperty="Background.Color"
From="Red" Duration="0:0:1" />
</Storyboard>
</BeginStoryboard.Storyboard>
</BeginStoryboard>
</EventTrigger>
</Style.Triggers>
</Style>
</StackPanel.Resources>
<TextBox Text="AAA" Background="Yellow" />
<TextBox Text="BBB" Background="Blue" />
</StackPanel>

Silverlight Animation Storyboard in Resource File

Can a storyboard be placed in a resource file such as styles.xaml? I have a toolbar that will be reused across many pages. I have this working now with a page level resource:
<navigation:Page.Resources>
<Storyboard x:Name="sbToolbarInitialization">
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.RenderTransform).(CompositeTransform.TranslateY)"
Storyboard.TargetName="Toolbar">
<EasingDoubleKeyFrame KeyTime="0"
Value="46" />
<EasingDoubleKeyFrame KeyTime="0:0:1"
Value="0">
<EasingDoubleKeyFrame.EasingFunction>
<BackEase EasingMode="EaseOut" />
</EasingDoubleKeyFrame.EasingFunction>
</EasingDoubleKeyFrame>
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</navigation:Page.Resources>
Which the border control uses:
<Border x:Name="Toolbar"
Style="{StaticResource ToolbarBorderStyle}">
<Border.RenderTransform>
<CompositeTransform />
</Border.RenderTransform>
<i:Interaction.Triggers>
<i:EventTrigger>
<ei:ControlStoryboardAction Storyboard="{StaticResource sbToolbarInitialization}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</Border>
Since I am already using the style.xaml file to place formatting for the border I would like to also store the storyboard there. Is this possible?
I've done something similar, storing the Storyboard in my App.xaml file and using across my entire application. One way to access it in your code-behind or view-model is as such:
public Storyboard MyStoryBoard = Application.Current.Resources["MyStoryBoard "] as Storyboard;
You could then bind that storyboard property to your view declaratively.

Animate a Path filling in - 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>

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