用过 iPhone 的都知道, iPhone 相册里, 当图片放大到一定程度后, 手指一放, 会自动缩回, 移动图片超出边框后手指一放, 图片也会自动缩回, 整个过程非常和谐, 自然, 精确, 那么 WPF 能否做到呢, 答案是肯定的.
在没有现成的控件的情况下, 只有自己做, 你肯定想到做动画, WPF 触屏开发提供了相应的功能来获取触控点的一些变化, 这些变化的最佳消费者个人认为是 Matrix. 我们回想下做动画一般怎么做, 比如给一个 button 做个宽度增 5 的动画, 我们一般是定义一个 DoubleAnimation, 然后定义一个 Sotryboard, 然后用 Sotryboard 的静态方法 SetTargetProperty 设置 UI 对象和动画作用的依赖属性. 按照这样的步骤, 我们给 UI 的 Matrix 做动画, 发现, 找不到这样的一个类似 DoubleAnimation 的动画类, Matrix 也没有类似 Button.WidthProperty 这样的依赖属性. 也许你会说 Matrix 的属性 OffsetX,M11 什么的都是 double 类型, 可以对其设置动画, 但是 Storyboard 应用的对象必须是继承自 DependencyProperty 的, 所以是不可能在 Matrix 的属性上设置动画的, 唯一的解决方案是自己做一个类似于 MatrixAnimation 的东西. 网上有人写过这样的动画类, 如下:
- 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); }
- }
- }
有了这个类, 我们就可以使用了:
- var animation = new LinearMatrixAnimation(oldMatrix, newMatrix, TimeSpan.FromSeconds(0.5));
- animation.AccelerationRatio = 0.3;
- animation.DecelerationRatio = 0.3;
有了动画, 只需要用 UI 的 MatrixTransform 启动动画即可, 假设某个 UI 的 MatrixTransform 为 matrixTransform, 我们就可以启动了:
matrixTransform.BeginAnimation(MatrixTransform.MatrixProperty, animation);
有效果, 但是貌似只能执行一次, 执行完之后, 后来无论怎样弄都没反应了, 这是由于动画执行完后锁定了属性, 网上也有人解决了, 办法是在执行完动画之后做了点处理:
- public static void PlayMatrixTransformAnimation(MatrixTransform matrixTransform, Matrix newMatrix, TimeSpan timeSpan)
- {
- var animation = new LinearMatrixAnimation(matrixTransform.Matrix, newMatrix, TimeSpan.FromSeconds(0.5));
- animation.AccelerationRatio = 0.3;
- animation.DecelerationRatio = 0.3;
- animation.FillBehavior = FillBehavior.HoldEnd;
- animation.Completed += (sender, e) =>
- {
- // 去除属性的动画绑定
- matrixTransform.BeginAnimation(MatrixTransform.MatrixProperty, null);
- // 将期望结果值保留
- matrixTransform.Matrix = newMatrix;
- };
- // 启动动画
- matrixTransform.BeginAnimation(MatrixTransform.MatrixProperty, animation, HandoffBehavior.SnapshotAndReplace);
- }
这样一来, 仿 iPhone 的反弹效果就已经差不多了. 其实这里是利用了 UI 的 MatrixTransform 做了动画, 有人说不是有个 MatrixAnimationUsingKeyFrames 动画类吗, 有兴趣的人可以继续探索下.
鉴于大家的热情, 我会把全部代码整理出来放到群文件共享里, 敬请关注.
来源: http://www.bubuko.com/infodetail-2776312.html