Adrian
Adrian

Reputation: 3438

How to make an ellipse follow a curve on a canvas

I am having an issue trying to get an ellipse to properly follow a path on my canvas. I supposed the problem stems from the fact that my mini syntax defines movement between x and y values but am targeting only one of those values in my target property (e.g. (Canvas.Top or Canvas.Left). I can not seem to find any attached "Position" property on the canvas that would take in a Position type which would work with my path. What would be the proper way to get this path to work?

<Canvas Name="WaitingCanvas">
            <Ellipse Name="WaitingEllipse"
                     Canvas.Top="100"
                     Canvas.Left="50"
                     Height="20"
                     Width="20"
                     Fill="White">
                <Ellipse.Triggers>
                    <EventTrigger RoutedEvent="Loaded">
                        <BeginStoryboard>
                            <Storyboard RepeatBehavior="Forever">
                                <DoubleAnimationUsingPath
                                    Storyboard.TargetName="WaitingEllipse"
                                    Storyboard.TargetProperty="(Canvas.Top)"

                                    Source="X" 
                                    Duration="0:0:5">
                                    <DoubleAnimationUsingPath.PathGeometry>
                                        <PathGeometry Figures="M 50,100 C 100,50 150,100 200, 100"/>
                                    </DoubleAnimationUsingPath.PathGeometry>
                                </DoubleAnimationUsingPath>


                            </Storyboard>
                        </BeginStoryboard>
                    </EventTrigger>
                </Ellipse.Triggers>
            </Ellipse>
        </Canvas>

Upvotes: 3

Views: 627

Answers (2)

Clemens
Clemens

Reputation: 128061

Instead of simultaneously animating Canvas.Left and Canvas.Top you may perhaps also animate the Matrix property of a MatrixTransform that is assigned to the RenderTransform property of the Ellipse. The Matrix would be animated by a MatrixAnimationUsingPath.

<Ellipse Height="20" Width="20" Fill="White">
    <Ellipse.RenderTransform>
        <MatrixTransform x:Name="transform" Matrix="1,0,0,1,50,100"/>
    </Ellipse.RenderTransform>
    <Ellipse.Triggers>
        <EventTrigger RoutedEvent="Loaded">
            <BeginStoryboard>
                <Storyboard RepeatBehavior="Forever">
                    <MatrixAnimationUsingPath
                        Storyboard.TargetName="transform"
                        Storyboard.TargetProperty="Matrix"
                        Duration="0:0:5">
                        <MatrixAnimationUsingPath.PathGeometry>
                            <PathGeometry Figures="M 50,100 C 100,50 150,100 200,100"/>
                        </MatrixAnimationUsingPath.PathGeometry>
                    </MatrixAnimationUsingPath>
                </Storyboard>
            </BeginStoryboard>
        </EventTrigger>
    </Ellipse.Triggers>
</Ellipse>

Using a MatrixAnimationUsingPath would even allow to rotate the animated element along the tangent of the animation path, by setting DoesRotateWithTangent="True". That would of course only be visible if the animated element wouldn't be a circle.

Upvotes: 4

McGarnagle
McGarnagle

Reputation: 102743

You can't animate both Canvas.Top and Canvas.Left at the same time, but you can use two separate animations. Make the PathGeometry a resources so it can be shared:

<Canvas Name="WaitingCanvas">
    <Canvas.Resources>
        <PathGeometry x:Name="AnimationPath" Figures="M 50,100 C 100,50 150,100 200, 100"/>
    </Canvas.Resources>
        <Ellipse Name="WaitingEllipse"
                 Canvas.Top="100"
                 Canvas.Left="50"
                 Height="20"
                 Width="20"
                 Fill="White">
            <Ellipse.Triggers>
                <EventTrigger RoutedEvent="Loaded">
                    <BeginStoryboard>
                        <Storyboard RepeatBehavior="Forever">
                            <DoubleAnimationUsingPath
                                Storyboard.TargetName="WaitingEllipse"
                                Storyboard.TargetProperty="(Canvas.Left)"
                                PathGeometry="{StaticResource AnimationPath}"
                                Source="X" 
                                Duration="0:0:5" />
                            <DoubleAnimationUsingPath
                                Storyboard.TargetName="WaitingEllipse"
                                Storyboard.TargetProperty="(Canvas.Top)"
                                PathGeometry="{StaticResource AnimationPath}"
                                Source="Y" 
                                Duration="0:0:5" />
                        </Storyboard>
                    </BeginStoryboard>
                </EventTrigger>
            </Ellipse.Triggers>
        </Ellipse>
    </Canvas>

Upvotes: 2

Related Questions