Reputation: 5532
I have a WPF Storyboard
(with RepeatBehavior=Forever
) containing two DoubleAnimationUsingPath
animations. These animations trace a circular path, which I create through adding an EllipseGeometry
. One animation is used to control the X position, and the other animation is used to control the Y position, of another object which appears to orbit using the circular path.
The animation always follows the path in a clockwise direction, and always starts in the middle on the right side of the circle. I need to force the animation to play in reverse (i.e. counterclockwise along the path). Ideally I'd also like to be able to control the starting position, but this isn't a necessity right now.
The AutoReverse
property on the animation does what I want, but obviously then it alternates between clockwise and counterclockwise - and I need the animation to loop, always in a counterclockwise direction.
I also tried to use a ScaleTransform
with ScaleX = -1
in order to flip the ellipse, hoping that this would help - but it hasn't.
Is there any way to force a DoubleAnimationUsingPath
to follow a specific direction along the path?
Upvotes: 1
Views: 926
Reputation: 128147
An alternative to DoubleAnimationUsingPath
would be a custom animation that provides sinus values. It has an Amplitude
property that controls the amplitude and by its sign also the direction of the sinus animation. There is also a StartAngle
property that controls at which angle the animation starts.
public class SinusAnimation : DoubleAnimationBase
{
public static readonly DependencyProperty AmplitudeProperty =
DependencyProperty.Register(
"Amplitude", typeof(double), typeof(SinusAnimation));
public static readonly DependencyProperty StartAngleProperty =
DependencyProperty.Register(
"StartAngle", typeof(double), typeof(SinusAnimation));
public double Amplitude
{
get { return (double)GetValue(AmplitudeProperty); }
set { SetValue(AmplitudeProperty, value); }
}
public double StartAngle
{
get { return (double)GetValue(StartAngleProperty); }
set { SetValue(StartAngleProperty, value); }
}
protected override double GetCurrentValueCore(double defaultOriginValue,
double defaultDestinationValue, AnimationClock animationClock)
{
var result = defaultOriginValue;
var p = animationClock.CurrentProgress;
if (p.HasValue)
{
result = Amplitude * Math.Sin(
p.Value * Math.PI * 2d + StartAngle * Math.PI / 180d);
}
return result;
}
protected override Freezable CreateInstanceCore()
{
return new SinusAnimation();
}
}
This is some simple XAML that creates circular motion of a small blue circle. By different Amplitudes it might as well create elliptical trajectories, and by different StartAngles also all kinds of Lissajous figures.
<Canvas Margin="200">
<Path Fill="Blue">
<Path.Data>
<EllipseGeometry RadiusX="10" RadiusY="10"/>
</Path.Data>
<Path.Triggers>
<EventTrigger RoutedEvent="Loaded">
<BeginStoryboard>
<Storyboard Duration="0:0:2" RepeatBehavior="Forever">
<local:SinusAnimation
Storyboard.TargetProperty="(Canvas.Left)"
Amplitude="100"
Duration="0:0:2" RepeatBehavior="Forever"/>
<local:SinusAnimation
Storyboard.TargetProperty="(Canvas.Top)"
Amplitude="100" StartAngle="90"
Duration="0:0:2" RepeatBehavior="Forever"/>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Path.Triggers>
</Path>
</Canvas>
Edit: In order to reverse a DoubleAnmationUsingPath
you can simply set the Transform property of the used PathGeometry to an appropriate ScaleTransform:
<PathGeometry x:Key="path">
<PathGeometry.Transform>
<ScaleTransform ScaleX="-1"/>
</PathGeometry.Transform>
...
</PathGeometry>
Upvotes: 1
Reputation: 4808
You can switch between two paths with the same coordinates but reverse directions. here is a sample using MatrixAnimationUsingPath
in which the animation direction is cw when UI element loads and it changes to ccw when mouse pointer enters the blue rectangle.
<Canvas ClipToBounds="False" Width="400" Height="400">
<Path StrokeThickness="10" StrokeDashArray="" StrokeDashCap="Flat" StrokeDashOffset="0" StrokeStartLineCap="Flat" StrokeEndLineCap="Flat" StrokeLineJoin="Miter" StrokeMiterLimit="10" Stroke="#000000">
<Path.Data>
<PathGeometry Figures="M 150,250 C 150,120 300,120 300,250 C 300,390 150,390 150,250 Z" />
</Path.Data>
</Path>
<Rectangle Fill="Blue" RenderTransformOrigin="0.5,0.5" Width="10" Height="10" Margin="-5">
<Rectangle.RenderTransform>
<TransformGroup>
<MatrixTransform x:Name="tt">
<MatrixTransform.Matrix>
<Matrix />
</MatrixTransform.Matrix>
</MatrixTransform>
</TransformGroup>
</Rectangle.RenderTransform>
<Rectangle.Triggers>
<EventTrigger RoutedEvent="FrameworkElement.Loaded">
<BeginStoryboard>
<Storyboard>
<MatrixAnimationUsingPath Duration="0:0:05" Storyboard.TargetName="tt"
Storyboard.TargetProperty="Matrix"
AutoReverse="False"
DoesRotateWithTangent="True"
RepeatBehavior="Forever"
>
<MatrixAnimationUsingPath.PathGeometry>
<PathGeometry Figures="M 150,250 C 150,120 300,120 300,250 C 300,390 150,390 150,250 Z">
</PathGeometry>
</MatrixAnimationUsingPath.PathGeometry>
</MatrixAnimationUsingPath>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
<EventTrigger RoutedEvent="FrameworkElement.MouseEnter">
<BeginStoryboard>
<Storyboard>
<MatrixAnimationUsingPath Duration="0:0:05" Storyboard.TargetName="tt"
Storyboard.TargetProperty="Matrix"
AutoReverse="False"
DoesRotateWithTangent="True"
RepeatBehavior="Forever"
>
<MatrixAnimationUsingPath.PathGeometry>
<PathGeometry Figures="M 150,250 C 150,390 300,390 300,250 C 300,120 150,120 150,250 Z" >
</PathGeometry>
</MatrixAnimationUsingPath.PathGeometry>
</MatrixAnimationUsingPath>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Rectangle.Triggers>
</Rectangle>
</Canvas>
Upvotes: 1