• 【UWP】对 Thickness 类型属性进行动画


    好几个月没写 blog 了,一个是在忙新版的碧影壁纸,另一方面是等(观望)周年更新的 api(不过现在还是比较失望,仍然没法支持矩形以外的 Clip)。闲话少说,进入主题。

    在 UWP 中,出于性能考虑,微软是不建议、不推荐对会影响布局的属性进行动画的。例如 Width 和 Height 这种,如果真的需要对这些属性进行动画的话(毕竟需求就摆在那里),可以将 Animation 的 EnableDependentAnimation 属性设置为 true 来对这些属性进行动画的。

    但是,对于 Thickness 类型来说,这是行不通的,因为 UWP 中并没有 ThicknessAnimation 这种动画类型(PS:WPF 里是有这种动画类型的说)。

    不过既然我标题都写了出来,那办法肯定是有的。Thickness 就是四个方向分量,也就是说,对这四个方向分量进行动画就等于对这个 Thickness 进行了动画。

    还有另外一点要注意的是,Thickness 类型的四个属性并不是依赖属性。

    例如:

    control.Margin.Left = 10;

    这一句是没有效果的。

    要实现效果,只能对 Margin 属性从新赋一个值:

    var margin = control.Margin;
    margin.Left = 10;
    control.Margin = margin;

    也就是说,我们需要一个可绑定的 Margin。(我就叫它 BindableMargin)

    新建一个用户控件

    sp160831_203229

    为什么是用户控件?因为经过我的发现,我们自定义的类的依赖属性,得有 xaml 文件才能进行动画。(不信你可以试试^-^)

    然后修改 BindableMargin.xaml 如下:

    <DependencyObject x:Class="AnimateThicknessDemo.BindableMargin"
                      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                      xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
                      xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
                      mc:Ignorable="d" />

    相当简单的一段 xaml,设计器就无视好了。重点在 BindableMargin.xaml.cs 里,修改代码:

        public partial class BindableMargin
        {
            public static readonly DependencyProperty BottomProperty = DependencyProperty.Register(nameof(Bottom), typeof(double), typeof(BindableMargin), new PropertyMetadata(default(double), BottomChanged));
    
            public static readonly DependencyProperty LeftProperty = DependencyProperty.Register(nameof(Left), typeof(double), typeof(BindableMargin), new PropertyMetadata(default(double), LeftChanged));
    
            public static readonly DependencyProperty RightProperty = DependencyProperty.Register(nameof(Right), typeof(double), typeof(BindableMargin), new PropertyMetadata(default(double), RightChanged));
    
            public static readonly DependencyProperty TopProperty = DependencyProperty.Register(nameof(Top), typeof(double), typeof(BindableMargin), new PropertyMetadata(default(double), TopChanged));
    
            private readonly FrameworkElement _owner;
    
            public BindableMargin(FrameworkElement owner)
            {
                if (owner == null)
                {
                    throw new ArgumentNullException(nameof(owner));
                }
    
                _owner = owner;
            }
    
            public double Bottom
            {
                get
                {
                    var ownerBottom = _owner.Margin.Bottom;
                    var bottom = (double)GetValue(BottomProperty);
                    if (ownerBottom.Equals(bottom) == false)
                    {
                        SetValue(BottomProperty, ownerBottom);
                    }
                    return ownerBottom;
                }
                set
                {
                    SetValue(BottomProperty, value);
                }
            }
    
            public double Left
            {
                get
                {
                    var ownerLeft = _owner.Margin.Left;
                    var left = (double)GetValue(LeftProperty);
                    if (ownerLeft.Equals(left) == false)
                    {
                        SetValue(LeftProperty, ownerLeft);
                    }
                    return ownerLeft;
                }
                set
                {
                    SetValue(LeftProperty, value);
                }
            }
    
            public double Right
            {
                get
                {
                    var ownerRight = _owner.Margin.Right;
                    var right = (double)GetValue(RightProperty);
                    if (ownerRight.Equals(right) == false)
                    {
                        SetValue(RightProperty, ownerRight);
                    }
                    return ownerRight;
                }
                set
                {
                    SetValue(RightProperty, value);
                }
            }
    
            public double Top
            {
                get
                {
                    var ownerTop = _owner.Margin.Top;
                    var top = (double)GetValue(TopProperty);
                    if (ownerTop.Equals(top) == false)
                    {
                        SetValue(TopProperty, ownerTop);
                    }
                    return ownerTop;
                }
                set
                {
                    SetValue(TopProperty, value);
                }
            }
    
            private static void BottomChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
            {
                var obj = (BindableMargin)d;
                var value = (double)e.NewValue;
    
                var owner = obj._owner;
                var margin = owner.Margin;
                margin.Bottom = value;
                owner.Margin = margin;
            }
    
            private static void LeftChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
            {
                var obj = (BindableMargin)d;
                var value = (double)e.NewValue;
    
                var owner = obj._owner;
                var margin = owner.Margin;
                margin.Left = value;
                owner.Margin = margin;
            }
    
            private static void RightChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
            {
                var obj = (BindableMargin)d;
                var value = (double)e.NewValue;
    
                var owner = obj._owner;
                var margin = owner.Margin;
                margin.Right = value;
                owner.Margin = margin;
            }
    
            private static void TopChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
            {
                var obj = (BindableMargin)d;
                var value = (double)e.NewValue;
    
                var owner = obj._owner;
                var margin = owner.Margin;
                margin.Top = value;
                owner.Margin = margin;
            }
        }

    别看代码这么多,其实不复杂。构造函数传入需要动画的控件。然后四个方向的依赖属性,值发生改变时回写到控件上。

    然后动画的例子代码:

    BindableMargin margin = new BindableMargin(control);
    DoubleAnimation animation = new DoubleAnimation();
    animation.EnableDependentAnimation = true;
    animation.From = 0;
    animation.To = 100;
    animation.Duration = TimeSpan.FromSeconds(1);
    Storyboard.SetTarget(animation, margin);
    Storyboard.SetTargetProperty(animation, "Left");
    await storyboard.BeginAsync();// WinRTXamlToolkit 里的扩展方法。
    margin.Left = 100;

    另外建议在动画播放完毕后,执行一次常规的赋值操作(一般赋最终值),因为视乎机器的配置,Storyboard 会有一定程度的跳帧,在低端的机器,可能动画就完全跳过去了。

    说了这么多,还是说说有啥应用吧。

    fq

    这是一个类似 IT 之家的通知控件。通过动画了 Margin 的 Right 来实现的。

    Demo 下载地址:AnimateThicknessDemo.zip

    当然应用还有很多,例如对 Border 的圆角进行动画。通过这么一种“桥”的方式,我们可以对很多属性,并不局限于 Thickness 类型,也进行动画,这里就留给各位看官发挥想象了。

  • 相关阅读:
    包含min函数的栈
    树的子结构
    合并两个排序的链表
    反转链表
    字符移位
    有趣的数字
    顺时针打印矩阵
    有道云笔记自动签到
    Shell重定向
    Ubuntu 18.04安装Docker
  • 原文地址:https://www.cnblogs.com/h82258652/p/5827638.html
Copyright © 2020-2023  润新知