简体   繁体   English

使用#wpf 获得流畅的动画?

[英]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.我在 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.为了拥有可以以一定速率运行 animation 逻辑的东西,我设置了一个 DispatcherTimer 并在那里执行了所有 animation 逻辑。 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.我知道,这种在 canvas 上渲染并使用事件系统制作动画的方式并不像 2D 游戏引擎那样运行流畅。

So, my question is, being a newbie to WPF, is there something I could do to improve the performance/accuracy?所以,我的问题是,作为 WPF 的新手,我能做些什么来提高性能/准确性吗? 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?我已经看到存在一个可用于 c# (SkiaSharp) 的 Skia package,我认为它可以提高渲染性能,但是以“准确”(这里没有实时操作系统)帧速率运行逻辑呢?

Performance was not one of WPFs primary design goals.性能不是 WPF 的主要设计目标之一。 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:如果您需要无法使用 WPF animation 系统实现的高速自定义动画,那么您最好直接从 FrameworkElement 派生并覆盖 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.需要使用 Animatios 以所需的方式显示数据。 WPF Animations are meant to be used for smooth rendering and demonstrate better performance when dispatcher. WPF 动画旨在用于平滑渲染并在调度程序时展示更好的性能。

In the simplest case you will need to start a Double animation to update a single property of your view model.在最简单的情况下,您需要启动 Double animation 来更新视图 model 的单个属性。 Then recalculate properties (size, positions, etc) of different objects based on this value.然后根据该值重新计算不同对象的属性(大小、位置等)。

Another possible solution is to define a custom animation class.另一种可能的解决方案是定义自定义 animation class。 This solution is demonstrated in WPF Tutorial - Part 2: Writing a custom animation class .此解决方案在WPF 教程 - 第 2 部分:编写自定义 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. WPF 中有一个特殊机制仅适用于您的情况 - 动画。 I'd recommend you to read this article: How to: Animate an Object Along a Path .我建议您阅读这篇文章: 如何:沿路径为 Object 设置动画

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>

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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