1

是否可以更改动画已经在进行中的路径,例如PathGeometry属性?DoubleAnimationUsingPath如果是这样,怎么做?

我的一些代码:

XAML:

<Window x:Class="WpfApplication1.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" Loaded="Window_Loaded">
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition />
            <ColumnDefinition />
        </Grid.ColumnDefinitions>

        <Canvas x:Name="BackgroundCanvas"
                Background="Transparent"
                Grid.ColumnSpan="2">
            <Ellipse Fill="Black" Width="10" Height="10" x:Name="Circ">
                <Ellipse.RenderTransform>
                    <TranslateTransform X="-5" Y="-5" />
                </Ellipse.RenderTransform>
            </Ellipse>
        </Canvas>

        <Rectangle x:Name="LeftRect" Width="100" Height="100" Grid.Column="0" Fill="#80002EE2" />
        <Rectangle x:Name="RightRect" Width="100" Height="100" Grid.Column="1" Fill="#8000B70A" />
    </Grid>
</Window>

代码隐藏:

private void Window_Loaded(object sender, RoutedEventArgs e)
{
    Func<FrameworkElement, Point> centerOf = ele => ele.TransformToVisual(BackgroundCanvas).Transform(new Point(ele.Width/2, ele.Height/2));
    Point start = centerOf(LeftRect);
    Point end = centerOf(RightRect);

    LineGeometry geom = new LineGeometry(start, end);

    var animation = new DoubleAnimationUsingPath
    {
        Duration = Duration.Automatic,
        PathGeometry = PathGeometry.CreateFromGeometry(geom)
    };

    animation.Source = PathAnimationSource.X;
    Circ.BeginAnimation(Canvas.LeftProperty, animation);

    animation.Completed += delegate
    {
        Window_Loaded(null, null);
    };
    animation.Source = PathAnimationSource.Y;
    Circ.BeginAnimation(Canvas.TopProperty, animation);
}

这应该在两个矩形的中心之间移动一个 10x10 的圆。如果动画尚未完成并LayoutUpdated触发,我想更改动画,以便在调整窗口大小时动画实际上在矩形的中心结束。

4

1 回答 1

1

这是我尝试的

我在画布上添加了一个形状,当您单击画布中的任意位置时,该形状将移向鼠标指针。您可以再次单击画布中的其他任何位置,形状将更改其路径以跟随新的指针位置。

xml

<Canvas x:Name="BackgroundCanvas"
        Background="Transparent"
        PreviewMouseDown="BackgroundCanvas_PreviewMouseDown">
    <Ellipse Fill="Black"
             Width="10"
             Height="10"
             x:Name="circ">
        <Ellipse.RenderTransform>
            <TranslateTransform X="-5"
                                Y="-5" />
        </Ellipse.RenderTransform>
    </Ellipse>
</Canvas>

BackgroundCanvas_PreviewMouseDown

private void BackgroundCanvas_PreviewMouseDown(object sender, MouseButtonEventArgs e)
{
    double sx = (double)circ.GetValue(Canvas.LeftProperty);
    double sy = (double)circ.GetValue(Canvas.TopProperty);
    Point tp = e.GetPosition(BackgroundCanvas);

    if (double.IsNaN(sx))
        sx = 0;
    if (double.IsNaN(sy))
        sy = 0;

    LineGeometry geom = new LineGeometry(new Point(sx, sy), tp);

    Path p = new Path() { Data = geom, Stroke = Brushes.Black };
    BackgroundCanvas.Children.Add(p);

    var animation = new DoubleAnimationUsingPath
    {
        Duration = Duration.Automatic,
        PathGeometry = PathGeometry.CreateFromGeometry(geom)
    };

    animation.Source = PathAnimationSource.X;
    circ.BeginAnimation(Canvas.LeftProperty, animation);
    animation.Source = PathAnimationSource.Y;
    circ.BeginAnimation(Canvas.TopProperty, animation);
}

结果

结果

我添加了显示结果的路径。试一试,看看它有多接近


编辑

我还尝试平滑动画路径,使其看起来不像直线

    Point pp;
    private void BackgroundCanvas_PreviewMouseDown(object sender, MouseButtonEventArgs e)
    {
        double sx = (double)circ.GetValue(Canvas.LeftProperty);
        double sy = (double)circ.GetValue(Canvas.TopProperty);
        Point tp = e.GetPosition(BackgroundCanvas);

        if (double.IsNaN(sx))
            sx = 0;
        if (double.IsNaN(sy))
            sy = 0;
        Point sp = new Point(sx, sy);
        StreamGeometry geom = new StreamGeometry();
        using (StreamGeometryContext ctx = geom.Open())
        {
            ctx.BeginFigure(sp, false, false);
            ctx.BezierTo(pp, tp, tp, true, false);
        }
        geom.Freeze();

        pp = tp;

        Path p = new Path() { Data = geom, Stroke = Brushes.Black };
        BackgroundCanvas.Children.Add(p);

        var animation = new DoubleAnimationUsingPath
        {
            Duration = Duration.Automatic,
            PathGeometry = PathGeometry.CreateFromGeometry(geom)
        };

        animation.Source = PathAnimationSource.X;
        circ.BeginAnimation(Canvas.LeftProperty, animation);
        animation.Source = PathAnimationSource.Y;
        circ.BeginAnimation(Canvas.TopProperty, animation);
    }

结果

结果

我希望这可以解决您修改正在运行的动画路径的问题(实际上不是修改,而是从触发点开始新动画),并具有融合外观的平滑度


编辑 2

我将我的方法与您的代码相结合,现在当窗口大小发生变化时,圆圈将沿着新路径到达第二个矩形。

    private void Window_Loaded(object sender, RoutedEventArgs e)
    {
        Func<FrameworkElement, Point> centerOf = ele => ele.TransformToVisual(BackgroundCanvas).Transform(new Point(ele.Width / 2, ele.Height / 2));
        Point start = centerOf(LeftRect);
        Point end = centerOf(RightRect);

        LineGeometry geom = new LineGeometry(start, end);
        pp = end;
        var animation = new DoubleAnimationUsingPath
        {
            Duration = TimeSpan.FromMilliseconds(totalDuration),
            PathGeometry = PathGeometry.CreateFromGeometry(geom)
        };

        animation.Source = PathAnimationSource.X;
        Circ.BeginAnimation(Canvas.LeftProperty, animation);

        animation.Completed += delegate { Window_Loaded(null, null); };

        animation.Source = PathAnimationSource.Y;
        Circ.BeginAnimation(Canvas.TopProperty, animation);
        startTime = DateTime.Now;
        started = true;
    }

    Point pp;
    DateTime startTime;
    double totalDuration = 5000;
    bool started;
    private void Window_SizeChanged(object sender, SizeChangedEventArgs e)
    {
        if (!started)
            return;

        Func<FrameworkElement, Point> centerOf = ele => ele.TransformToVisual(BackgroundCanvas).Transform(new Point(ele.Width / 2, ele.Height / 2));
        double sx = (double)Circ.GetValue(Canvas.LeftProperty);
        double sy = (double)Circ.GetValue(Canvas.TopProperty);
        Point tp = centerOf(RightRect);

        double timeLeft = totalDuration - DateTime.Now.Subtract(startTime).TotalMilliseconds;

        if (timeLeft < 1) return;

        if (double.IsNaN(sx))
            sx = 0;
        if (double.IsNaN(sy))
            sy = 0;
        Point sp = new Point(sx, sy);
        StreamGeometry geom = new StreamGeometry();
        using (StreamGeometryContext ctx = geom.Open())
        {
            ctx.BeginFigure(sp, false, false);
            ctx.BezierTo(pp, pp, tp, true, false);
        }
        geom.Freeze();

        pp = tp;

        var animation = new DoubleAnimationUsingPath
        {
            Duration = TimeSpan.FromMilliseconds(timeLeft),
            PathGeometry = PathGeometry.CreateFromGeometry(geom)
        };

        animation.Source = PathAnimationSource.X;
        Circ.BeginAnimation(Canvas.LeftProperty, animation);

        animation.Completed += delegate { Window_Loaded(null, null); };

        animation.Source = PathAnimationSource.Y;
        Circ.BeginAnimation(Canvas.TopProperty, animation);
    }

看看这是否是你要找的。当前圆形将使用其当前位置和第二个矩形来创建它的路径。如果您希望在调整窗口大小时移动圆圈,那么也许我们可能需要实现情节提要并使用 seek 方法来实现相同的目的。

于 2014-08-24T08:35:33.543 回答