最近用画布的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);
调用的效果: