使用MatrixTransform平滑动画?

时间:2010-01-01 11:57:11

标签: wpf animation matrix

我正在尝试制作矩阵动画,我同时缩放和转置画布。我发现的唯一方法是使用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;

    }

6 个答案:

答案 0 :(得分:3)

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

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); }
        }
    }
}

答案 1 :(得分:1)

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

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

您将获得DiscreteMatrixKeyFrame导致的答案 突然改变,你应该使用LinearDoubleKeyFrame 或SplineDoubleKeyFrame以及源代码

编辑:啊,我知道,Matrix转换只支持离散 转换,所以你实际上有跳跃的问题。 所以我建议使用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,设置持续时间/关键时间和值即可 需要。要获得比例,您需要计算结束宽度/高度并设置它,但这应该不是问题。

答案 2 :(得分:1)

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

答案 3 :(得分:1)

我喜欢@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) ...

答案 4 :(得分:0)

我能想到的一种方法是将矩阵转换为包含TransformGroupScaleTransformRotateTransform的{​​{1}},然后使用普通动画为这些动画制作动画,然后,一旦动画完成,再从每个变换中的值创建Matrix?

答案 5 :(得分:0)

如果以后无法访问@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));
    }