WPF: Animating along path geometry

2020-03-24 09:11发布

问题:

I am dealing with the following issue for a week now. Please, bear with me as I explain.

What I am trying to achive is, for some of us, pretty simple, but being new to WPF makes things hard.

The image above was rendered by converting (already had) svg to xaml and afterwards importing it to Visual Studio. This is what the xaml looks like.

    <Canvas x:Name="Main" Margin="158,-223,-148,233">
        <Canvas.RenderTransform>
            <MatrixTransform Matrix="1.25 0 0 -1.25 -197.1231 961.58875"/>
        </Canvas.RenderTransform>
        <Path xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Name="path139746" Fill="#FFD3BC5F" Canvas.Left="399.4" Canvas.Top="-172.6">
            <Path.Data>
                <PathGeometry Figures="m 66.611178 552.33853 c -4.36996 0.6305 -8.8334 0.97293 -13.3752 0.97293 -18.11465 0 -35.027 -5.21161 -49.34949 -14.18974 l 21.64732 -36.67733 c 8.2963 5.25686 18.12667 8.31294 28.67512 8.31294 2.7121 0 5.37755 -0.20365 7.98106 -0.59283 l 2.23539 -0.38916 c 4.72288 -0.91109 9.2226 -2.43913 13.41755 -4.49964 l 35.933612 77.65818 c -12.53504 5.94167 -26.095762 10.06571 -40.347392 12.0342 0 0 -2.27764 -21.55688 -4.54327 -42.98252 l -2.2747 0.35297 z" FillRule="nonzero"/>
            </Path.Data>
        </Path>
        <Path xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Fill="#FFD3BC5F" Canvas.Left="401" Canvas.Top="-173" >
            <Path.Data>
                <PathGeometry x:Name="path139750" Figures="m -21.311892 515.98804 c -11.7053 -15.57594 -18.6547 -34.92004 -18.6622 -55.85694 l 35.53987 0 5.06525 -0.024 c 0.63507 11.4535 4.86922 21.94 11.58023 30.3706 l 1.44647 1.7423 c 2.94008 3.3894 6.29329 6.4077 9.98888 8.9736 l -43.53754 73.76488 c -13.18956 -8.30539 -24.88746 -18.76176 -34.60186 -30.87743 0 0 17.3968 -13.23944 34.61076 -26.3386 l -1.42986 -1.75428 z" FillRule="nonzero"/>
            </Path.Data>
        </Path>
        <Path xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Name="path139780" Fill="#FFD3BC5F" Canvas.Left="401.4" Canvas.Top="-167.6">
            <Path.Data>
                <PathGeometry Figures="m 214.75854 460.1016 94.92962 0 0 69.86866 -94.92962 0 0 -69.86866 z" FillRule="nonzero"/>
            </Path.Data>
        </Path>
        <Path xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Name="path139784" Fill="#FFD3BC5F" Canvas.Left="401.4" Canvas.Top="-167.6">
            <Path.Data>
                <PathGeometry Figures="m 211.64803 529.825 -36.87191 0 -62.32503 -69.72236 100.95576 0 0 69.74499 -1.75882 -0.0226 z" FillRule="nonzero"/>
            </Path.Data>
        </Path>
        <Path xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"  Fill="#FFD3BC5F" Canvas.Top="-173" Canvas.Left="401">
            <Path.Data>
                <PathGeometry x:Name="path139798" Figures="m -41.482092 460.131 -43.5377 0 c 0.6744 30.93022 11.4414 59.36853 29.1278 82.17137 l 33.3799 -25.40184 c -11.9001 -15.82789 -18.9641 -35.4887 -18.97 -56.76953" FillRule="nonzero"/>
            </Path.Data>
        </Path>
        <Path xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Name="path139802" Fill="#FFD3BC5F" Canvas.Left="399.4" Canvas.Top="-172.6">
            <Path.Data>
                <PathGeometry Figures="m 139.82292 494.54117 c -13.01467 -14.5593 -25.97803 -29.06131 -30.40076 -34.00892 0 0 -2.72123 28.49563 -29.566602 43.70507 l 17.54746 37.92328 c 19.158502 -10.35383 34.278952 -27.22704 42.419902 -47.61943" FillRule="nonzero"/>
            </Path.Data>
        </Path>
        <Path xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Name="path139806" Fill="#FFD3BC5F" Canvas.Left="399.4" Canvas.Top="-172.6">
            <Path.Data>
                <PathGeometry Figures="m 98.037008 543.53024 17.776782 38.41802 c 23.1211 -11.42479 42.62202 -29.06882 56.29587 -50.76297 l 0.18403 -0.31676 c 0 0 -15.53072 -17.37553 -31.34655 -35.06934 -8.34762 20.43762 -23.62341 37.33197 -42.910132 47.73105" FillRule="nonzero"/>
            </Path.Data>
        </Path>
        <Path xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Name="path139810" Fill="#FFD3BC5F" Canvas.Left="399.4" Canvas.Top="-172.6">
            <Path.Data>
                <PathGeometry Figures="m 53.235488 554.82033 c -18.39362 0 -35.56859 -5.28853 -50.11595 -14.3994 l -21.08756 35.73002 c 21.04842 12.78086 45.75185 20.14348 72.17809 20.14348 5.74697 0 11.40815 -0.36201 16.97265 -1.03931 l -4.37747 -41.4213 c -4.43321 0.63805 -8.96144 0.98651 -13.56976 0.98651" FillRule="nonzero"/>
            </Path.Data>
        </Path>

 <Rectangle Name="object_to_move" Fill="LightBlue" Height="36" Stroke="Black" Width="41" Canvas.Left="330" Canvas.Top="229"/>

I want is to animate/move the box (Rectangle) along the path, making it (the box) look like traveling and using it (the path) as a road to move on.

I have tried using DoubleAnimationUsingPath. But I am very confused, and I think I am not using things properly.

I have found the following code snippt and tried to manipulate it and apply it to my example, but couldn't get it work.

 //  path139798.Freeze(); // For performance benefits. 
        DoubleAnimationUsingPath daPath = new DoubleAnimationUsingPath();
        daPath.Duration = TimeSpan.FromSeconds(5);
        daPath.RepeatBehavior = RepeatBehavior.Forever;
        daPath.AccelerationRatio = 0.6;
        daPath.DecelerationRatio = 0.4;
        daPath.AutoReverse = true;
        daPath.PathGeometry = path139798;
        daPath.Source = PathAnimationSource.X;
        circle2.BeginAnimation(Canvas.LeftProperty, daPath);


        daPath = new DoubleAnimationUsingPath();
        daPath.Duration = TimeSpan.FromSeconds(5);
        daPath.RepeatBehavior = RepeatBehavior.Forever;
        daPath.AccelerationRatio = 0.6;
        daPath.DecelerationRatio = 0.4;
        daPath.AutoReverse = true;
        daPath.PathGeometry = path139798;
        daPath.Source = PathAnimationSource.Y;
        circle2.BeginAnimation(Canvas.TopProperty, daPath);
    }

Could someone help. Thanks!

回答1:

So you have a lot of strange margins and stuff on your example. I didn't mess with it too much. I just added a motion path and notice the Storyboard but here you go.

Quick Tip: Blend (comes with visual studio) makes these sort of effects really quick and easy. For this example I just made the shape path to match your mock image. Then it's as easy as selecting it then right-click -> Path -> Make Motion Path and it will prompt you to select the object to follow the path. Took more time to load Blend then it did to make the example.

Hope this helps, cheers.

<Canvas x:Name="Main" Height="1025 " Width="1025">
            <Canvas.Resources>
                <Storyboard x:Key="Weeeee" RepeatBehavior="Forever">
                    <DoubleAnimationUsingPath Duration="0:0:3" Source="X" Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[3].(TranslateTransform.X)" Storyboard.TargetName="object_to_move">
                        <DoubleAnimationUsingPath.PathGeometry>
                            <PathGeometry Figures="M0.83299852,-4.019 L0.83299852,0.6089829 L0.61794496,0.94214186 C-7.6701996,14.489389 -12.443,30.392629 -12.443001,47.403001 C-12.443,96.887715 27.948303,137.003 77.773499,137.003 C113.58536,137.003 144.52365,116.27938 159.09367,86.248303 L159.90265,84.471135 L380.931,84.471135 L380.931,86.871121 L160.63918,86.871121 L160.4838,87.217053 C145.62575,118.25356 114.07582,139.671 77.556,139.671 C26.745804,139.671 -14.444,98.212666 -14.444,47.071218 C-14.444,29.491346 -9.5768454,13.055669 -1.124851,-0.94513857 z"/>
                        </DoubleAnimationUsingPath.PathGeometry>
                    </DoubleAnimationUsingPath>
                    <DoubleAnimationUsingPath Duration="0:0:3" Source="Y" Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[3].(TranslateTransform.Y)" Storyboard.TargetName="object_to_move">
                        <DoubleAnimationUsingPath.PathGeometry>
                            <PathGeometry Figures="M0.83299852,-4.019 L0.83299852,0.6089829 L0.61794496,0.94214186 C-7.6701996,14.489389 -12.443,30.392629 -12.443001,47.403001 C-12.443,96.887715 27.948303,137.003 77.773499,137.003 C113.58536,137.003 144.52365,116.27938 159.09367,86.248303 L159.90265,84.471135 L380.931,84.471135 L380.931,86.871121 L160.63918,86.871121 L160.4838,87.217053 C145.62575,118.25356 114.07582,139.671 77.556,139.671 C26.745804,139.671 -14.444,98.212666 -14.444,47.071218 C-14.444,29.491346 -9.5768454,13.055669 -1.124851,-0.94513857 z"/>
                        </DoubleAnimationUsingPath.PathGeometry>
                    </DoubleAnimationUsingPath>
                </Storyboard>
            </Canvas.Resources>
            <Canvas.Triggers>
                <EventTrigger RoutedEvent="FrameworkElement.Loaded">
                    <BeginStoryboard Storyboard="{StaticResource Weeeee}"/>
                </EventTrigger>
            </Canvas.Triggers>
            <Canvas.RenderTransform>
                <MatrixTransform Matrix="1.25 0 0 -1.25 -197.1231 961.58875"/>
            </Canvas.RenderTransform>
            <Path xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Name="path139746" Fill="#FFD3BC5F" Canvas.Left="399.4" Canvas.Top="-172.6">
                <Path.Data>
                    <PathGeometry Figures="m 66.611178 552.33853 c -4.36996 0.6305 -8.8334 0.97293 -13.3752 0.97293 -18.11465 0 -35.027 -5.21161 -49.34949 -14.18974 l 21.64732 -36.67733 c 8.2963 5.25686 18.12667 8.31294 28.67512 8.31294 2.7121 0 5.37755 -0.20365 7.98106 -0.59283 l 2.23539 -0.38916 c 4.72288 -0.91109 9.2226 -2.43913 13.41755 -4.49964 l 35.933612 77.65818 c -12.53504 5.94167 -26.095762 10.06571 -40.347392 12.0342 0 0 -2.27764 -21.55688 -4.54327 -42.98252 l -2.2747 0.35297 z" FillRule="nonzero"/>
                </Path.Data>
            </Path>
            <Path xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Fill="#FFD3BC5F" Canvas.Left="401" Canvas.Top="-173" >
                <Path.Data>
                    <PathGeometry x:Name="path139750" Figures="m -21.311892 515.98804 c -11.7053 -15.57594 -18.6547 -34.92004 -18.6622 -55.85694 l 35.53987 0 5.06525 -0.024 c 0.63507 11.4535 4.86922 21.94 11.58023 30.3706 l 1.44647 1.7423 c 2.94008 3.3894 6.29329 6.4077 9.98888 8.9736 l -43.53754 73.76488 c -13.18956 -8.30539 -24.88746 -18.76176 -34.60186 -30.87743 0 0 17.3968 -13.23944 34.61076 -26.3386 l -1.42986 -1.75428 z" FillRule="nonzero"/>
                </Path.Data>
            </Path>
            <Path xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Name="path139780" Fill="#FFD3BC5F" Canvas.Left="401.4" Canvas.Top="-167.6">
                <Path.Data>
                    <PathGeometry Figures="m 214.75854 460.1016 94.92962 0 0 69.86866 -94.92962 0 0 -69.86866 z" FillRule="nonzero"/>
                </Path.Data>
            </Path>
            <Path xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Name="path139784" Fill="#FFD3BC5F" Canvas.Left="401.4" Canvas.Top="-167.6">
                <Path.Data>
                    <PathGeometry Figures="m 211.64803 529.825 -36.87191 0 -62.32503 -69.72236 100.95576 0 0 69.74499 -1.75882 -0.0226 z" FillRule="nonzero"/>
                </Path.Data>
            </Path>
            <Path xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"  Fill="#FFD3BC5F" Canvas.Top="-173" Canvas.Left="401">
                <Path.Data>
                    <PathGeometry x:Name="path139798" Figures="m -41.482092 460.131 -43.5377 0 c 0.6744 30.93022 11.4414 59.36853 29.1278 82.17137 l 33.3799 -25.40184 c -11.9001 -15.82789 -18.9641 -35.4887 -18.97 -56.76953" FillRule="nonzero"/>
                </Path.Data>
            </Path>
            <Path xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Name="path139802" Fill="#FFD3BC5F" Canvas.Left="399.4" Canvas.Top="-172.6">
                <Path.Data>
                    <PathGeometry Figures="m 139.82292 494.54117 c -13.01467 -14.5593 -25.97803 -29.06131 -30.40076 -34.00892 0 0 -2.72123 28.49563 -29.566602 43.70507 l 17.54746 37.92328 c 19.158502 -10.35383 34.278952 -27.22704 42.419902 -47.61943" FillRule="nonzero"/>
                </Path.Data>
            </Path>
            <Path xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Name="path139806" Fill="#FFD3BC5F" Canvas.Left="399.4" Canvas.Top="-172.6">
                <Path.Data>
                    <PathGeometry Figures="m 98.037008 543.53024 17.776782 38.41802 c 23.1211 -11.42479 42.62202 -29.06882 56.29587 -50.76297 l 0.18403 -0.31676 c 0 0 -15.53072 -17.37553 -31.34655 -35.06934 -8.34762 20.43762 -23.62341 37.33197 -42.910132 47.73105" FillRule="nonzero"/>
                </Path.Data>
            </Path>
            <Path xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Name="path139810" Fill="#FFD3BC5F" Canvas.Left="399.4" Canvas.Top="-172.6">
                <Path.Data>
                    <PathGeometry Figures="m 53.235488 554.82033 c -18.39362 0 -35.56859 -5.28853 -50.11595 -14.3994 l -21.08756 35.73002 c 21.04842 12.78086 45.75185 20.14348 72.17809 20.14348 5.74697 0 11.40815 -0.36201 16.97265 -1.03931 l -4.37747 -41.4213 c -4.43321 0.63805 -8.96144 0.98651 -13.56976 0.98651" FillRule="nonzero"/>
                </Path.Data>
            </Path>

            <!-- Trajectory -->
            <Path Data="M16.276999,1 L16.276999,5.627983 16.061945,5.9611419 C7.7738004,19.508389 3.0009999,35.41163 3.000999,52.422002 3.0009999,101.90672 43.392303,142.022 93.217499,142.022 129.02936,142.022 159.96765,121.29839 174.53767,91.267304 L175.34665,89.490136 396.375,89.490136 396.375,91.890122 176.08318,91.890122 175.9278,92.236054 C161.06975,123.27256 129.51982,144.69 93,144.69 42.189804,144.69 1,103.23167 1,52.090218 1,34.510346 5.8671546,18.07467 14.319149,4.0738615 z" Fill="#7F000000" Height="145.69" Canvas.Left="360.056" Stretch="Fill" Stroke="Red" StrokeThickness="2" Canvas.Top="236.648" Width="397.375"/>

            <!-- Because I like to move it, move it ~~ -->
            <Rectangle x:Name="object_to_move" Fill="LightBlue" Height="36" Stroke="Black" Width="41" Canvas.Left="355" Canvas.Top="223.667" RenderTransformOrigin="0.5,0.5">
                <Rectangle.RenderTransform>
                    <TransformGroup>
                        <ScaleTransform/>
                        <SkewTransform/>
                        <RotateTransform/>
                        <TranslateTransform/>
                    </TransformGroup>
                </Rectangle.RenderTransform>
            </Rectangle>

        </Canvas>

...and a crappy quality GIF to visualize it for other viewers.



回答2:

Here's a working demo that might shed some light on DoubleAnimationUsingPath

<Window.Resources>
    <PathGeometry x:Key="AnimationPath" Figures="M 10,100 C 35,0 135,0 160,100 180,190 285,200 310,100" />
</Window.Resources>

<Grid>
    <Canvas x:Name="Main" >

        <!-- The Rectangle itself ... -->

        <Path xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Name="path139746" Fill="#FFD3BC5F">
            <Path.Data>
                <PathGeometry Figures="M 10,100 C 35,0 135,0 160,100 180,190 285,200 310,100" />
            </Path.Data>
        </Path>


        <Rectangle   
  Width="30" Height="30" Fill="Blue">
            <Rectangle.RenderTransform>
                <TransformGroup>
                    <RotateTransform x:Name="AnimatedRotateTransform" />
                    <TranslateTransform x:Name="AnimatedTranslateTransform"  />
                </TransformGroup>
            </Rectangle.RenderTransform>

            <Rectangle.Triggers>
                <EventTrigger RoutedEvent="Path.Loaded">
                    <BeginStoryboard>
                        <Storyboard RepeatBehavior="Forever" AutoReverse="True" >

                            <!-- Generates angle values (in degrees) from
               the path. This animation is used to
               rotate the rectangle. -->
                            <DoubleAnimationUsingPath
            Storyboard.TargetName="AnimatedRotateTransform"
            Storyboard.TargetProperty="Angle"
            PathGeometry="{StaticResource AnimationPath}"
            Source="Angle"
            Duration="0:0:5"  />

                            <!-- Generates horizontal offset values from
               the path. This animation is used to 
               animate the rectangle horizontally. -->
                            <DoubleAnimationUsingPath
            Storyboard.TargetName="AnimatedTranslateTransform"
            Storyboard.TargetProperty="X"
            PathGeometry="{StaticResource AnimationPath}"
            Source="X" 
            Duration="0:0:5"  />

                            <!-- Generates vertical offset values from
               the path. This animation is used to move
               the rectangle vertically. -->
                            <DoubleAnimationUsingPath
            Storyboard.TargetName="AnimatedTranslateTransform"
            Storyboard.TargetProperty="Y"
            PathGeometry="{StaticResource AnimationPath}"
            Source="Y" 
            Duration="0:0:5"  />
                        </Storyboard>
                    </BeginStoryboard>
                </EventTrigger>
            </Rectangle.Triggers>
        </Rectangle>

    </Canvas>
</Grid>

As you can see, the Rectangle follows a single path.... It will follow path139798 but it wont look right because that is a polygon, if you want the Rectangle to follow the centre line of the shapes (PathGeometry Figures) you will need to define the centre line for the animation path