【问题标题】:Smooth animation using MatrixTransform?使用 MatrixTransform 平滑动画?
【发布时间】:2010-01-01 11:57:11
【问题描述】:

我正在尝试制作一个矩阵动画​​,在其中我同时缩放和转置画布。我发现的唯一方法是使用 MatrixTransform 和 MatrixAnimationUsingKeyFrames。由于似乎没有内置矩阵的任何插值(仅用于路径/旋转),似乎唯一的选择是尝试自己构建插值和 DiscreteMatrixKeyFrame。

我对此做了一个基本的实现,但它并不完全顺利,我不确定这是否是最好的方法以及如何处理帧速率等。有人有改进的建议吗?代码如下:

        MatrixAnimationUsingKeyFrames anim = new MatrixAnimationUsingKeyFrames();
        int duration = 1;
        anim.KeyFrames = Interpolate(new Point(0, 0), centerPoint, 1, factor,100,duration);
        this.matrixTransform.BeginAnimation(MatrixTransform.MatrixProperty, anim,HandoffBehavior.Compose);


public MatrixKeyFrameCollection Interpolate(Point startPoint, Point endPoint, double startScale, double endScale, double framerate,double duration)
    {
        MatrixKeyFrameCollection keyframes = new MatrixKeyFrameCollection();

        double steps = duration * framerate;
        double milliSeconds = 1000 / framerate;
        double timeCounter = 0;



        double diffX = Math.Abs(startPoint.X-  endPoint.X);
        double xStep = diffX / steps;

        double diffY = Math.Abs(startPoint.Y - endPoint.Y);
        double yStep = diffY / steps;

        double diffScale= Math.Abs(startScale- endScale);
        double scaleStep = diffScale / steps;


        if (endPoint.Y < startPoint.Y)
        {
            yStep =  -yStep;
        }

        if (endPoint.X < startPoint.X)
        {
            xStep =  -xStep;
        }


        if (endScale < startScale)
        {
            scaleStep =  -scaleStep;
        }


        Point currentPoint = new Point();
        double currentScale = startScale;

        for (int i = 0; i < steps; i++)
        {
            keyframes.Add(new DiscreteMatrixKeyFrame(new Matrix(currentScale, 0, 0, currentScale, currentPoint.X, currentPoint.Y), KeyTime.FromTimeSpan(TimeSpan.FromMilliseconds(timeCounter))));
            currentPoint.X += xStep;
            currentPoint.Y += yStep;
            currentScale += scaleStep;
            timeCounter += milliSeconds;

        }

        keyframes.Add(new DiscreteMatrixKeyFrame(new Matrix(endScale, 0, 0, endScale, endPoint.X, endPoint.Y), KeyTime.FromTimeSpan(TimeSpan.FromMilliseconds(0))));

        return keyframes;

    }

【问题讨论】:

    标签: wpf animation matrix


    【解决方案1】:

    试试这个!只要您不旋转/剪切,它就可以解决问题。

    using System.Windows;
    using System.Windows.Media;
    using System.Windows.Media.Animation;
    
    namespace MapControl
    {
        public class LinearMatrixAnimation : AnimationTimeline
        {
    
            public Matrix? From
            {
                set { SetValue(FromProperty, value);}
                get { return (Matrix)GetValue(FromProperty); }
            }
            public static DependencyProperty FromProperty = DependencyProperty.Register("From", typeof(Matrix?), typeof(LinearMatrixAnimation), new PropertyMetadata(null));
    
            public Matrix? To
            {
                set { SetValue(ToProperty, value); }
                get { return (Matrix)GetValue(ToProperty); }
            }
            public static DependencyProperty ToProperty = DependencyProperty.Register("To", typeof(Matrix?), typeof(LinearMatrixAnimation), new PropertyMetadata(null));
    
            public LinearMatrixAnimation()
            {            
            }
    
            public LinearMatrixAnimation(Matrix from, Matrix to, Duration duration)
            {
                Duration = duration;
                From = from;
                To = to;
            }
    
            public override object GetCurrentValue(object defaultOriginValue, object defaultDestinationValue, AnimationClock animationClock)
            {
                if (animationClock.CurrentProgress == null)
                {
                    return null;
                }
    
                double progress = animationClock.CurrentProgress.Value;
                Matrix from = From ?? (Matrix)defaultOriginValue;
    
                if (To.HasValue)
                {
                    Matrix to = To.Value;
                    Matrix newMatrix = new Matrix(((to.M11 - from.M11) * progress)+from.M11, 0, 0, ((to.M22 - from.M22) * progress)+from.M22,
                                                  ((to.OffsetX - from.OffsetX) * progress) + from.OffsetX, ((to.OffsetY - from.OffsetY) * progress)+ from.OffsetY);
                    return newMatrix;
                }
    
                return Matrix.Identity;
            }
    
            protected override System.Windows.Freezable CreateInstanceCore()
            {
                return new LinearMatrixAnimation();
            }
    
            public override System.Type  TargetPropertyType
            {
                get { return typeof(Matrix); }
            }
        }
    }
    

    【讨论】:

    • 干得好!我在这段代码中添加了缓动。在下面寻找我的部分。
    【解决方案2】:

    我已经实现了支持平滑平移、缩放和旋转动画的 MatrixAnimation 类。它还支持缓动功能!找到它here

    【讨论】:

    • 你写了一堂很棒的课。我将您的课程和一个小样本添加到此线程中,否则将来可能会丢失。
    【解决方案3】:

    好吧,如果你在 MSDN 中问这个问题

    http://msdn.microsoft.com/en-us/library/system.windows.media.animation.discretematrixkeyframe.aspx

    你得到了 DiscreteMatrixKeyFrame 导致的答案 突然的变化,你应该使用 LinearDoubleKeyFrame 或 SplineDoubleKeyFrame 以及 源代码

    编辑:啊,我明白了,矩阵变换只支持离散 转换,所以你实际上有跳跃的问题。 所以我建议使用 RectAnimationUsingKeyFrames

    // Create a RectAnimationUsingKeyFrames to
    // animate the RectangleGeometry.
    RectAnimationUsingKeyFrames rectAnimation = new RectAnimationUsingKeyFrames();
    rectAnimation.Duration = TimeSpan.FromSeconds(timeInSeconds);
    
    // Animate position, width, and height in first 2 seconds. LinearRectKeyFrame creates
    // a smooth, linear animation between values.
    rectAnimation.KeyFrames.Add(
                    new LinearRectKeyFrame(
                        new Rect(600,50,200,50), // Target value (KeyValue)
                        KeyTime.FromTimeSpan(TimeSpan.FromSeconds(2))) // KeyTime
                    );
    
    // In the next half second, change height to 10. 
       rectAnimation.KeyFrames.Add(
                    new LinearRectKeyFrame(
                        new Rect(600, 50, 200, 10), // Target value (KeyValue)
                        KeyTime.FromTimeSpan(TimeSpan.FromSeconds(2.5))) // KeyTime
                    );
    

    只需使用 Linear 或 SplineRectKeyFrame,设置持续时间/Keytime 和您需要的值 需要。要获得比例,您需要计算结束宽度/高度并设置它,但这应该不是问题。

    【讨论】:

    • 好吧,那太好了,但我不能真正将这些关键帧与 MatrixTransform afaik 一起使用。我可以将动画分成两部分(缩放和转换),但它们不会同时发生
    【解决方案4】:

    我喜欢@LukeN 的回答。适用于简单的平移/缩放动画。
    我在这段代码中添加了缓动(虽然是手工制作的,不是 WPF 原生缓动)。

    private double Sigmoid(double v)
    {
        double t = -6 + (v * 12.0);
        return 1.0 / (1.0 + Math.Exp(-t));
    }
    
    private double EaseIn(double v)
    {
        return 2.0 * Sigmoid(v/2.0);
    }
    
    private double EaseOut(double v)
    {
        return 2.0 * ( Sigmoid(0.5 + v/2.0) - 0.5);
    }
    

    然后在GetCurrentValue 中执行progress = Sigmoid(progress)EaseIn(progress)...

    【讨论】:

      【解决方案5】:

      如果@pwlodek 链接将来无法访问并且不要忘记他的精彩课程,我复制到 SO:

      //http://pwlodek.blogspot.com/2010/12/matrixanimation-for-wpf.html
      public class MatrixAnimation : MatrixAnimationBase
      {
          public Matrix? From
          {
              set { SetValue(FromProperty, value); }
              get { return (Matrix)GetValue(FromProperty); }
          }
      
          public static DependencyProperty FromProperty =
              DependencyProperty.Register("From", typeof(Matrix?), typeof(MatrixAnimation),
                  new PropertyMetadata(null));
      
          public Matrix? To
          {
              set { SetValue(ToProperty, value); }
              get { return (Matrix)GetValue(ToProperty); }
          }
      
          public static DependencyProperty ToProperty =
              DependencyProperty.Register("To", typeof(Matrix?), typeof(MatrixAnimation),
                  new PropertyMetadata(null));
      
          public IEasingFunction EasingFunction
          {
              get { return (IEasingFunction)GetValue(EasingFunctionProperty); }
              set { SetValue(EasingFunctionProperty, value); }
          }
      
          public static readonly DependencyProperty EasingFunctionProperty =
              DependencyProperty.Register("EasingFunction", typeof(IEasingFunction), typeof(MatrixAnimation),
                  new UIPropertyMetadata(null));
      
          public MatrixAnimation()
          {
          }
      
          public MatrixAnimation(Matrix toValue, Duration duration)
          {
              To = toValue;
              Duration = duration;
          }
      
          public MatrixAnimation(Matrix toValue, Duration duration, FillBehavior fillBehavior)
          {
              To = toValue;
              Duration = duration;
              FillBehavior = fillBehavior;
          }
      
          public MatrixAnimation(Matrix fromValue, Matrix toValue, Duration duration)
          {
              From = fromValue;
              To = toValue;
              Duration = duration;
          }
      
          public MatrixAnimation(Matrix fromValue, Matrix toValue, Duration duration, FillBehavior fillBehavior)
          {
              From = fromValue;
              To = toValue;
              Duration = duration;
              FillBehavior = fillBehavior;
          }
      
          protected override Freezable CreateInstanceCore()
          {
              return new MatrixAnimation();
          }
      
          protected override Matrix GetCurrentValueCore(Matrix defaultOriginValue, Matrix defaultDestinationValue, AnimationClock animationClock)
          {
              if (animationClock.CurrentProgress == null)
              {
                  return Matrix.Identity;
              }
      
              var normalizedTime = animationClock.CurrentProgress.Value;
              if (EasingFunction != null)
              {
                  normalizedTime = EasingFunction.Ease(normalizedTime);
              }
      
              var from = From ?? defaultOriginValue;
              var to = To ?? defaultDestinationValue;
      
              var newMatrix = new Matrix(
                      ((to.M11 - from.M11) * normalizedTime) + from.M11,
                      ((to.M12 - from.M12) * normalizedTime) + from.M12,
                      ((to.M21 - from.M21) * normalizedTime) + from.M21,
                      ((to.M22 - from.M22) * normalizedTime) + from.M22,
                      ((to.OffsetX - from.OffsetX) * normalizedTime) + from.OffsetX,
                      ((to.OffsetY - from.OffsetY) * normalizedTime) + from.OffsetY);
      
              return newMatrix;
          }
      }
      

      后面代码的示例用法:

          protected void AnimateMatrix(MatrixTransform matrixTransform, Matrix toMatrix, TimeSpan duration)
          {
              if (matrixTransform is MatrixTransform mt && toMatrix is Matrix to && duration is TimeSpan ts)
                  AnimateMatrix(mt, mt.Matrix, to, duration);
          }
          protected void AnimateMatrix(MatrixTransform matrixTransform, Matrix? fromMatrix, Matrix? toMatrix, TimeSpan duration)
          {
              if (matrixTransform is MatrixTransform mt)
                  mt.BeginAnimation(MatrixTransform.MatrixProperty, GetMatrixAnimation(fromMatrix, toMatrix, duration));
          }
          private MatrixAnimation GetMatrixAnimation(Matrix? fromMatrix, Matrix? toMatrix, TimeSpan duration)
          {
              return new MatrixAnimation(fromMatrix ?? Matrix.Identity, toMatrix ?? Matrix.Identity, new Duration(duration));
          }
      

      【讨论】:

        【解决方案6】:

        我能想到的一种方法是将矩阵转换为TransformGroup,其中包含ScaleTransformRotateTransformTranslateTransform,然后使用普通动画对它们进行动画处理,然后在动画完成后创建Matrix 再次从每个 Transforms 中的值返回?

        【讨论】:

        • 这与更改 MatrixTransform 不同。我只知道,因为我最初尝试过,但它不起作用!
        猜你喜欢
        • 1970-01-01
        • 2012-09-17
        • 2017-08-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2023-03-23
        • 2019-08-24
        相关资源
        最近更新 更多