自定义Marix的自定义动画,支持缓动动画属性

最近用画布的MatrixTransForm做变换,需要用Matrix做动画处理,网上冲浪找了一圈,没有找出好的解决方法

Stack Overflow 给出了一部分的解决方法,但是不支持缓动函数,貌似不是最优的:wpf - Smooth animation using MatrixTransform? - Stack Overflow

 

小组的大牛给了一个最新的,支持 IEasingFunction 属性的 关于 Matrix的动画,完美解决

1、LinearMatrixAnimation  主调用方法

 public class LinearMatrixAnimation : LinearMatrixAnimationBase
    {
        #region Fields

        private Matrix[] _keyValues;
        private AnimationType _animationType;
        private bool _isAnimationFunctionValid;

        private static Type typeofProp = typeof(Matrix?);
        private static Type typeofThis = typeof(LinearMatrixAnimation);

        #endregion

        #region Dependency Properties

        public static readonly DependencyProperty FromProperty =
            DependencyProperty.Register("From", typeofProp, typeofThis,
                new PropertyMetadata(null, AnimationFunction_Changed), ValidateFromToOrByValue);

        public static readonly DependencyProperty ToProperty =
            DependencyProperty.Register("To", typeofProp, typeofThis,
                new PropertyMetadata(null, AnimationFunction_Changed), ValidateFromToOrByValue);

        public static readonly DependencyProperty EasingFunctionProperty =
            DependencyProperty.Register("EasingFunction", typeof(IEasingFunction), typeofThis);

        public Matrix? From
        {
            get
            {
                return (Matrix?)GetValue(FromProperty);
            }
            set
            {
                SetValue(FromProperty, value);
            }
        }

        public Matrix? To
        {
            get
            {
                return (Matrix?)GetValue(ToProperty);
            }
            set
            {
                SetValue(ToProperty, value);
            }
        }

        public IEasingFunction EasingFunction
        {
            get
            {
                return (IEasingFunction)GetValue(EasingFunctionProperty);
            }
            set
            {
                SetValue(EasingFunctionProperty, value);
            }
        }

        #endregion

        #region Constructors

        /// <summary>
        /// Static ctor for LinearMatrixAnimation establishes
        /// dependency properties, using as much shared data as possible.
        /// </summary>
        static LinearMatrixAnimation()
        {
        }

        public LinearMatrixAnimation()
            : base()
        {
        }

        public LinearMatrixAnimation(Matrix toValue, Duration duration)
            : this()
        {
            To = toValue;
            Duration = duration;
        }

        public LinearMatrixAnimation(Matrix toValue, Duration duration, FillBehavior fillBehavior)
            : this()
        {
            To = toValue;
            Duration = duration;
            FillBehavior = fillBehavior;
        }

        public LinearMatrixAnimation(Matrix fromValue, Matrix toValue, Duration duration)
            : this()
        {
            From = fromValue;
            To = toValue;
            Duration = duration;
        }

        public LinearMatrixAnimation(Matrix fromValue, Matrix toValue, Duration duration, FillBehavior fillBehavior)
            : this()
        {
            From = fromValue;
            To = toValue;
            Duration = duration;
            FillBehavior = fillBehavior;
        }

        #endregion

        #region Private Methods

        private static void AnimationFunction_Changed(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            LinearMatrixAnimation a = (LinearMatrixAnimation)d;
            a._isAnimationFunctionValid = false;
        }

        private static bool ValidateFromToOrByValue(object value)
        {
            return true;//不做校验
        }

        private void ValidateAnimationFunction()
        {
            _animationType = AnimationType.Automatic;
            _keyValues = null;

            if (From.HasValue)
            {
                if (To.HasValue)
                {
                    _animationType = AnimationType.FromTo;
                    _keyValues = new Matrix[2];
                    _keyValues[0] = From.Value;
                    _keyValues[1] = To.Value;
                }
                else
                {
                    _animationType = AnimationType.From;
                    _keyValues = new Matrix[1];
                    _keyValues[0] = From.Value;
                }
            }
            else if (To.HasValue)
            {
                _animationType = AnimationType.To;
                _keyValues = new Matrix[1];
                _keyValues[0] = To.Value;
            }
            _isAnimationFunctionValid = true;
        }

        #endregion

        #region Protected Methods

        protected override Freezable CreateInstanceCore()
        {
            return new LinearMatrixAnimation();
        }

        protected override Matrix GetCurrentValueCore(Matrix defaultOriginValue, Matrix defaultDestinationValue, AnimationClock animationClock)
        {
            Debug.Assert(animationClock.CurrentState != ClockState.Stopped);

            if (!_isAnimationFunctionValid)
            {
                ValidateAnimationFunction();
            }

            double progress = animationClock.CurrentProgress.Value;

            IEasingFunction easingFunction = EasingFunction;
            if (easingFunction != null)
            {
                progress = easingFunction.Ease(progress);
            }

            Matrix from = new Matrix();
            Matrix to = new Matrix();
            switch (_animationType)
            {
                case AnimationType.Automatic:
                    from = defaultOriginValue;
                    to = defaultDestinationValue;
                    break;
                case AnimationType.From:
                    from = _keyValues[0];
                    to = defaultDestinationValue;
                    break;
                case AnimationType.To:
                    from = defaultOriginValue;
                    to = _keyValues[0];
                    break;
                case AnimationType.FromTo:

                    from = _keyValues[0];
                    to = _keyValues[1];
                    break;
                default:
                    Debug.Fail("Unknown animation type.");
                    break;
            }

            if (To.HasValue)
            {
                Matrix newMatrix = from.Add(to.Subtract(from).Multiply(progress));
                return newMatrix;
            }

            return Matrix.Identity;
        }

        #endregion

        #region Public Methods

        public new LinearMatrixAnimation Clone()
        {
            return (LinearMatrixAnimation)base.Clone();
        }

        #endregion
    }

 

2、动画类型:

 /// <summary>
    /// Describes the behavior of an animation.
    /// </summary>
    internal enum AnimationType : byte
    {
        /// <summary>
        /// The animation animates from the defaultOriginValue value to the defaultDestinationValue.
        /// </summary>
        Automatic = 0,

        /// <summary>
        /// The animation animates from the From property value to the defaultDestinationValue.
        /// </summary>
        From,

        /// <summary>
        /// The animation animates from the defaultOriginValue to the To property value.
        /// </summary>
        To,

        /// <summary>
        /// The animation animates from the defaultOriginValue value to the defaultOriginValue value plus
        /// the By property value.
        /// </summary>
        By,

        /// <summary>
        /// The animation animates from the From property value to the To property value.
        /// </summary>
        FromTo,

        /// <summary>
        /// The animation animates from the From property value to the From property value plus
        /// the By property value.
        /// </summary>
        FromBy
    }

 

3、LinearMatrixAnimationBase 基类实现:

 public abstract class LinearMatrixAnimationBase : AnimationTimeline
    {
        #region Constructors

        /// <Summary>
        /// Creates a new DoubleAnimationBase.
        /// </Summary>
        protected LinearMatrixAnimationBase()
            : base()
        {
        }

        #endregion

        #region Methods

        /// <summary>
        /// Creates a copy of this LinearMatrixAnimationBase
        /// </summary>
        /// <returns>The copy</returns>
        public new LinearMatrixAnimationBase Clone()
        {
            return (LinearMatrixAnimationBase)base.Clone();
        }

        /// <summary>
        /// Calculates the value this animation believes should be the current value for the property.
        /// </summary>
        public override sealed object GetCurrentValue(object defaultOriginValue, object defaultDestinationValue, AnimationClock animationClock)
        {
            if (defaultOriginValue == null)
            {
                throw new ArgumentNullException("defaultOriginValue");
            }
            if (defaultDestinationValue == null)
            {
                throw new ArgumentNullException("defaultDestinationValue");
            }
            return GetCurrentValue((Matrix)defaultOriginValue, (Matrix)defaultDestinationValue, animationClock);
        }

        /// <summary>
        /// Returns the type of the target property
        /// </summary>
        public override sealed Type TargetPropertyType
        {
            get
            {
                ReadPreamble();

                return typeof(Matrix);
            }
        }

        /// <summary>
        /// Calculates the value this animation believes should be the current value for the property.
        /// </summary>
        public Matrix GetCurrentValue(Matrix defaultOriginValue, Matrix defaultDestinationValue, AnimationClock animationClock)
        {
            ReadPreamble();

            if (animationClock == null)
            {
                throw new ArgumentNullException("animationClock");
            }

            if (animationClock.CurrentState == ClockState.Stopped)
            {
                return defaultDestinationValue;
            }

            return GetCurrentValueCore(defaultOriginValue, defaultDestinationValue, animationClock);
        }

        protected abstract Matrix GetCurrentValueCore(Matrix defaultOriginValue, Matrix defaultDestinationValue, AnimationClock animationClock);

        #endregion
    }

 

4、自定义帮助类

public static class MatrixHelper
    {
        public static Matrix Add(this Matrix left, Matrix right)
        {
            double m11 = left.M11 + right.M11;
            double m12 = left.M12 + right.M12;
            double m21 = left.M21 + right.M21;
            double m22 = left.M22 + right.M22;
            double offsetX = left.OffsetX + right.OffsetX;
            double offsetY = left.OffsetY + right.OffsetY;
            Matrix newMatrix = new Matrix(m11, m12,
                m21, m22,
                offsetX, offsetY);
            return newMatrix;
        }

        public static Matrix Subtract(this Matrix left, Matrix right)
        {
            double m11 = left.M11 - right.M11;
            double m12 = left.M12 - right.M12;
            double m21 = left.M21 - right.M21;
            double m22 = left.M22 - right.M22;
            double offsetX = left.OffsetX - right.OffsetX;
            double offsetY = left.OffsetY - right.OffsetY;
            Matrix newMatrix = new Matrix(m11, m12,
                m21, m22,
                offsetX, offsetY);
            return newMatrix;
        }

        public static Matrix Multiply(this Matrix matrix, double factor)
        {
            double m11 = matrix.M11 * factor;
            double m12 = matrix.M12 * factor;
            double m21 = matrix.M21 * factor;
            double m22 = matrix.M22 * factor;
            double offsetX = matrix.OffsetX * factor;
            double offsetY = matrix.OffsetY * factor;
            Matrix newMatrix = new Matrix(m11, m12,
                m21, m22,
                offsetX, offsetY);
            return newMatrix;
        }
    }

 

方法调用:

var  _lineAnimation = new LinearMatrixAnimation()
            {
                Duration = TimeSpan.FromSeconds(0.3),
                FillBehavior = FillBehavior.Stop,
                EasingFunction = new QuinticEase { EasingMode = EasingMode.EaseInOut }
            };

  _lineAnimation.From = Matrix;
  _lineAnimation.To = _animationMatrix;
            _matrixTransform.BeginAnimation(MatrixTransform.MatrixProperty, _lineAnimation);

 

 

调用的效果:

 

posted @ 2021-11-20 11:29  wuty007  阅读(92)  评论(0编辑  收藏  举报