John
John

Reputation: 5532

Play DoubleAnimationUsingPath in reverse

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

Answers (2)

Clemens
Clemens

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

Bahman_Aries
Bahman_Aries

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

Related Questions