简体   繁体   中英

Get smooth animations using #wpf?

I am developing a simple graph viewer. I got it rendered nicely using simple Ellipses and Lines on a canvas.

Now, I want to have some dots navigating the graph. You could imagine it as cars traversing the streets of a city.

To have something that could run the animation logic at a certain rate I setup a DispatcherTimer and did all the animation logic there. It works, but it is really choppy. To be true, I am not surprised. I understand that this way of rendering onto a canvas and animate using the event system is not something you could expect to run as smooth as a 2D game engine for example.

So, my question is, being a newbie to WPF, is there something I could do to improve the performance/accuracy? I have seen there exist a Skia package available for c# (SkiaSharp) that I think could improve the rendering performance but what about running the logic at an "accurate" (no realtime os here) frame rate?

Performance was not one of WPFs primary design goals. If you need high-speed custom animations that can't be implemented with the WPF animation system then you're better off deriving from FrameworkElement directly and overriding OnRender:

public class CustomControl : FrameworkElement
{
    public CustomControl()
    {
        // leave this line in to force a render of the control for each frame
        CompositionTarget.Rendering += (s, e) => InvalidateVisual();
    }

    protected override void OnRender(DrawingContext drawingContext)
    {
        var angle = Math.PI * DateTime.Now.Ticks / 10000000;
        var xoffset = 100 + (int)(50 * Math.Sin(angle));
        var yoffset = 100 + (int)(50 * Math.Cos(angle));
        drawingContext.DrawEllipse(Brushes.Red, null, new Point(xoffset, yoffset), 50, 50);
    }
}

It is necessary to use Animatios to display data in the required manner. WPF Animations are meant to be used for smooth rendering and demonstrate better performance when dispatcher.

In the simplest case you will need to start a Double animation to update a single property of your view model. Then recalculate properties (size, positions, etc) of different objects based on this value.

Another possible solution is to define a custom animation class. This solution is demonstrated in WPF Tutorial - Part 2: Writing a custom animation class . This solution provides more flexibility, but generally you will get a similar value (you can consider it to be "time ellapsed") that can be used to calculate objects settings.

There's a special mechanism in WPF just for your case - Animations. I'd recommend you to read this article: How to: Animate an Object Along a Path .

Below is an example of traversing a simple graph. Also note that in the example I use one predefined path (for simplicity). In practice you can create such paths and animations dynamically from code-behind.

<Canvas>
    <Canvas.Resources>
        <PathGeometry x:Key="AnimationPath" Figures="M 100,100 L 200,150 L 230,250 L 150,300 L 100,100"/>
    </Canvas.Resources>

    <Line X1="115" X2="215" Y1="115" Y2="165" Stroke="Green" StrokeThickness="5"/>
    <Line X1="215" X2="245" Y1="165" Y2="265" Stroke="Green" StrokeThickness="5"/>
    <Line X1="245" X2="165" Y1="265" Y2="315" Stroke="Green" StrokeThickness="5"/>
    <Line X1="165" X2="115" Y1="315" Y2="115" Stroke="Green" StrokeThickness="5"/>

    <Ellipse Width="30" Height="30" Canvas.Left="100" Canvas.Top="100" Fill="Red"/>
    <Ellipse Width="30" Height="30" Canvas.Left="200" Canvas.Top="150" Fill="Red"/>
    <Ellipse Width="30" Height="30" Canvas.Left="230" Canvas.Top="250" Fill="Red"/>
    <Ellipse Width="30" Height="30" Canvas.Left="150" Canvas.Top="300" Fill="Red"/>

    <Ellipse x:Name="traveller" Width="30" Height="30" Fill="Blue">
        <Ellipse.RenderTransform>
            <TranslateTransform x:Name="transform"/>
        </Ellipse.RenderTransform>
    </Ellipse>

    <Canvas.Triggers>
        <EventTrigger RoutedEvent="Loaded">
            <BeginStoryboard>
                <Storyboard RepeatBehavior="Forever">
                    <DoubleAnimationUsingPath PathGeometry="{StaticResource AnimationPath}"
                                              Storyboard.TargetName="transform"
                                              Storyboard.TargetProperty="X"
                                              Source="X"
                                              Duration="0:0:10"/>

                    <DoubleAnimationUsingPath PathGeometry="{StaticResource AnimationPath}"
                                              Storyboard.TargetName="transform"
                                              Storyboard.TargetProperty="Y"
                                              Source="Y"
                                              Duration="0:0:10"/>
                </Storyboard>
            </BeginStoryboard>
        </EventTrigger>
    </Canvas.Triggers>
</Canvas>

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM