为什么我不能使用 DoubleAnimation Using Path 来为 TranslateForm.X 属性设置动画?

Why I can't use DoubleAnimationUsingPath to animate the TranslateForm.X propertry?

我在 wpf 中创建了一个自定义 Panel,其中 children 需要在渲染后进行动画处理。我想要的每个 child 的动画是使用 bezier 曲线 平移(移动)。

我遵循 Microsoft's example 并创建了 AddAnimation 方法,我在其中添加了 Path 我希望面板的每个项目都具有动画效果。

不幸的是我得到了以下异常:

Cannot animate the 'X' property on a 'System.Windows.Media.TranslateTransform' using a 'System.Windows.Media.Animation.DoubleAnimationUsingPath'

但是我应该如何在没有 DoubleAnimationUsingPath 的情况下为 X 属性 设置动画?

方法如下:

private void AddAnimation(FrameworkElement child, Point childLocation)
        {
            var middlePoint = GetMiddlePoint();
            var finalPoint = GetFinalPoint();

            var animatedTranslateTransform = new TranslateTransform();
            child.RenderTransform = animatedTranslateTransform;
            child.RenderTransformOrigin = new Point(0.5, 0.5);

            // Create the animation path.
            PathGeometry animationPath = new PathGeometry();
            PathFigure pFigure = new PathFigure();
            pFigure.StartPoint = childLocation;
            PolyBezierSegment pBezierSegment = new PolyBezierSegment();
            pBezierSegment.Points.Add(middlePoint);
            pBezierSegment.Points.Add(finalPoint);
            pFigure.Segments.Add(pBezierSegment);
            animationPath.Figures.Add(pFigure);

            // Freeze the PathGeometry for performance benefits.
            animationPath.Freeze();

            // Create a DoubleAnimationUsingPath to move the
            // rectangle horizontally along the path by animating 
            // its TranslateTransform.
            DoubleAnimationUsingPath translateXAnimation =
                new DoubleAnimationUsingPath();
            translateXAnimation.PathGeometry = animationPath;
            translateXAnimation.Duration = TimeSpan.FromSeconds(5);

            // Set the Source property to X. This makes
            // the animation generate horizontal offset values from
            // the path information. 
            translateXAnimation.Source = PathAnimationSource.X;

            // Set the animation to target the X property
            Storyboard.SetTarget(translateXAnimation, child);
            Storyboard.SetTargetProperty(translateXAnimation,
                new PropertyPath("(FrameworkElement.RenderTransform).(TranslateTransform.X)"));

            // Create a DoubleAnimationUsingPath to move the
            // rectangle vertically along the path by animating 
            // its TranslateTransform.
            DoubleAnimationUsingPath translateYAnimation =
                new DoubleAnimationUsingPath();
            translateYAnimation.PathGeometry = animationPath;
            translateYAnimation.Duration = TimeSpan.FromSeconds(5);

            // Set the Source property to Y. This makes
            // the animation generate vertical offset values from
            // the path information. 
            translateYAnimation.Source = PathAnimationSource.Y;

            // Set the animation to target the Y property
            // of the TranslateTransform named "AnimatedTranslateTransform".
            Storyboard.SetTarget(translateYAnimation, child);
            Storyboard.SetTargetProperty(translateYAnimation,
                new PropertyPath("(FrameworkElement.RenderTransform).(TranslateTransform.Y)"));

            // Create a Storyboard to contain and apply the animations.
            Storyboard pathAnimationStoryboard = new Storyboard();
            pathAnimationStoryboard.RepeatBehavior = RepeatBehavior.Forever;
            pathAnimationStoryboard.Children.Add(translateXAnimation);
            pathAnimationStoryboard.Children.Add(translateYAnimation);

            // Start the animations when the rectangle is loaded.
            child.Loaded += (sender, args) =>
            {
                Debug.WriteLine($"Child {child} loaded.");
                pathAnimationStoryboard.Begin(child);
            };
        }

看看AnimationException的InnerException 属性。

异常的原因是两个点对于PolyBezierSegment是不够的,因为它需要两个控制点和一个终点,因此每条曲线三个点。

改用PolyQuadraticBezierSegment

var pBezierSegment = new PolyQuadraticBezierSegment();
pBezierSegment.Points.Add(middlePoint);
pBezierSegment.Points.Add(finalPoint);

或者只是一个 QuadraticBezierSegment:

var bezierSegment = new QuadraticBezierSegment();
bezierSegment.Point1 = middlePoint;
bezierSegment.Point2 = finalPoint;

对于它应该做的事情来说,代码似乎也太复杂了。您可以将 Storyboard 替换为两个 DoubleAnimationUsingPath 子级,其中一个 MatrixAnimationUsingPathMatrixTransform:

Matrix 属性 设置动画
var animation = new MatrixAnimationUsingPath
{
    PathGeometry = animationPath,
    Duration = TimeSpan.FromSeconds(5)
};

var transform = new MatrixTransform();

child.RenderTransform = transform;
child.Loaded += (s, e) =>
    transform.BeginAnimation(MatrixTransform.MatrixProperty, animation);