WPF路径已从计时器上的代码隐藏动画化.如何在动画运行时消除路径元素的抖动



我编写了这个示例,它通过计时器在代码后面的椭圆周围设置路径动画。问题是,路径元素随着路径的变化而稍微摆动。除了轻微的摇晃外,行为似乎基本正常。有什么想法我该怎么纠正吗?

顺便说一句,右边的ListBox只是用来显示计算出的点,以确保它们是我所期望的。

<Window x:Class="AnimationSamples.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="MainWindow" Height="350" Width="525" WindowStartupLocation="CenterScreen">
<Grid>
    <Grid.ColumnDefinitions>
        <ColumnDefinition></ColumnDefinition>
        <ColumnDefinition></ColumnDefinition>
    </Grid.ColumnDefinitions>
    <TextBlock Text="{Binding Degree}" HorizontalAlignment="Right" Margin="9"></TextBlock>
    <Grid>
        <Border BorderBrush="SteelBlue" BorderThickness="0">
            <Grid Width="200" Height="200" HorizontalAlignment="Center" VerticalAlignment="Center">
                <Ellipse Fill="SteelBlue"></Ellipse>
                <Path x:Name="_pathTest" Data="M 0,90 A 90,90 0 1 1 90,180" StrokeThickness="4" Stroke="LightSkyBlue" 
                      Visibility="Visible" HorizontalAlignment="Center" VerticalAlignment="Center"
                      Margin="2,2,0,0"/>
                <Path x:Name="_pathTarget" Stretch="None"
                        Data="{Binding PathData}" Fill="Transparent" StrokeThickness="10"
                        HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Stroke="White">
                    <Path.RenderTransform>
                        <TranslateTransform X="10" Y="10"/>
                    </Path.RenderTransform>
                </Path>
            </Grid>
        </Border>
    </Grid>
    <ListBox x:Name="_lbDoubleCheckPoints"
             Grid.Column="1" ItemsSource="{Binding Points}" 
             BorderBrush="Transparent" Background="Transparent"
             HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
        <ListBox.ItemsPanel>
            <ItemsPanelTemplate>
                <Canvas HorizontalAlignment="Center" VerticalAlignment="Center">
                </Canvas>
            </ItemsPanelTemplate>
        </ListBox.ItemsPanel>
        <ListBox.ItemTemplate>
            <DataTemplate>
                <Ellipse Width="1" Height="1" Fill="Red"/>
            </DataTemplate>
        </ListBox.ItemTemplate>
        <ListBox.ItemContainerStyle>
            <Style TargetType="ListBoxItem">
                <Setter Property="Focusable" Value="False"></Setter>
                <Setter Property="Canvas.Left" Value="{Binding X}"/>
                <Setter Property="Canvas.Top" Value="{Binding Y}"/>
            </Style>
        </ListBox.ItemContainerStyle>
    </ListBox>
</Grid>

下面是代码:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.ComponentModel;
using System.Timers;
namespace AnimationSamples
{
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            this.DataContext = new ViewModel();
            InitializeComponent();
        }
        public class ViewModel : NotifyRoot
        {
            Geometry _pathData = null;
            public Geometry PathData 
            {
                get { return _pathData; }
                set { _pathData = value; RaiseChanged("PathData"); }
            }
            double _dDegree = 180.0;
            public double Degree
            {
                get { return _dDegree; }
                set { if (_dDegree != value) { _dDegree = value; RaiseChanged("Degree"); } }
            }
            public IEnumerable<Point> Points { get; set; }
            ///////////////////////////////////////////////////////////////////
            Timer _timer = new Timer(1) { AutoReset = true };
            Point[] _pts = new Point[360];
            Point _ptStart;
            ///////////////////////////////////////////////////////////////////
            public ViewModel()
            {
                // calculate points around circle
                double r = 90;
                for (int i = 0; i < 360; i++)
                {
                    double rad = Helper.ToRadians(i);
                    _pts[i] = new Point(r * Math.Sin(rad), r * Math.Cos(rad));                    
                }
                _ptStart = _pts[180];
                Points = _pts.AsEnumerable();
                _timer.Elapsed += (object sender, ElapsedEventArgs e) =>
                {
                    Degree = (Degree + 1) % 360;
                    int iArc = (Degree < 179) ? 1 : 0;
                    Point pt = _pts[(int)Degree];
                    // M 0,90 A 90,90, 30 1 0 90, 0
                    PathData = Geometry.Parse(
                        string.Format(
                            "M {0},{1} A {2},{2}, 0 {3} 0 {4},{5}",
                            r + Math.Round(_ptStart.X), r + Math.Round(_ptStart.Y), 
                            r, iArc, 
                            r + Math.Round(pt.X), r + Math.Round(pt.Y)));
                    Debug.Assert(PathData != null);
                };
                _timer.Start();
            }
        }
    }
    public class Helper
    {
        public static double ToRadians(double d)
        {
            return d * Math.PI / 180;
        }
    }
    public class NotifyRoot : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;
        public void RaiseChanged(string strPropName)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(strPropName));
            }
        }
    }
}

非常感谢您的帮助。

====================================

感谢马克早些时候的回复。我对此做了更多的修改。这里有一个基本相同的XAML版本,没有代码隐藏。

<Grid>
    <Border BorderBrush="SteelBlue" BorderThickness="0" Width="200" Height="200"
            HorizontalAlignment="Center" VerticalAlignment="Center">
        <Grid>
            <Ellipse Fill="SteelBlue"/>
            <Path x:Name="_pathTest2" Stretch="None"
                      Fill="Transparent" StrokeThickness="10"
                      HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Stroke="White">
                <Path.Data>
                    <PathGeometry>
                        <PathGeometry.Figures>
                            <PathFigureCollection>
                                <PathFigure StartPoint="0,90">
                                    <PathFigure.Segments>
                                        <PathSegmentCollection>
                                            <ArcSegment x:Name="_arc" RotationAngle="0" Size="90,90" IsLargeArc="True" 
                                                        Point="0,90.001" SweepDirection="Clockwise"/>
                                        </PathSegmentCollection>
                                    </PathFigure.Segments>
                                </PathFigure>
                            </PathFigureCollection>
                        </PathGeometry.Figures>
                    </PathGeometry>
                </Path.Data>
                <Path.Triggers>
                    <EventTrigger RoutedEvent="Path.Loaded">
                        <BeginStoryboard x:Name="_sb0">
                            <Storyboard TargetName="_arc" AutoReverse="False" 
                                        DecelerationRatio="0.99"
                                        RepeatBehavior="Forever" SpeedRatio="1.2">
                                <PointAnimationUsingPath Storyboard.TargetProperty="Point" Duration="0:0:1.5" >
                                    <PointAnimationUsingPath.PathGeometry>
                                        <PathGeometry Figures="M 0,90 A 90,90 0 1 1 0,90.001" />
                                    </PointAnimationUsingPath.PathGeometry>
                                </PointAnimationUsingPath>
                                <BooleanAnimationUsingKeyFrames Storyboard.TargetProperty="IsLargeArc" Duration="0:0:1.5">
                                    <DiscreteBooleanKeyFrame KeyTime="0:0:0" Value="False" />
                                    <DiscreteBooleanKeyFrame KeyTime="0:0:0.75" Value="True" />                                        
                                </BooleanAnimationUsingKeyFrames>
                            </Storyboard>
                        </BeginStoryboard>
                    </EventTrigger>
                </Path.Triggers>
                <Path.RenderTransform>
                    <TranslateTransform X="10" Y="10"/>
                </Path.RenderTransform>
            </Path>
        </Grid>
    </Border>
</Grid>

去掉路径生成代码中的Math.Round():

// M 0,90 A 90,90, 30 1 0 90, 0
PathData = Geometry.Parse(
    string.Format(
        "M {0},{1} A {2},{2}, 0 {3} 0 {4},{5}",
        r + _ptStart.X, r + _ptStart.Y, 
        r, iArc, 
        r + pt.X, r + pt.Y));

最新更新