zoukankan      html  css  js  c++  java
  • UWP:使用Composition实现类似安卓的水波纹Ripple效果

    先放效果图:

    首先,建立一个RippleHelper.cs文件,然后建立以下附加属性:

    IsFillEnable:是否扩大到整个控件

    RippleDuration:持续时间

    RippleRadius:不扩大到整个控件时的最大半径

    RippleColor:波纹的颜色

            public static bool GetIsFillEnable(DependencyObject obj)
            {
                return (bool)obj.GetValue(IsFillEnableProperty);
            }
    
            public static void SetIsFillEnable(DependencyObject obj, bool value)
            {
                obj.SetValue(IsFillEnableProperty, value);
            }
    
            public static readonly DependencyProperty IsFillEnableProperty =
                DependencyProperty.RegisterAttached("IsFillEnable", typeof(bool), typeof(RippleHelper), new PropertyMetadata(false));
    
            public static TimeSpan GetRippleDuration(UIElement obj)
            {
                return (TimeSpan)obj.GetValue(RippleDurationProperty);
            }
    
            public static void SetRippleDuration(UIElement obj, TimeSpan value)
            {
                obj.SetValue(RippleDurationProperty, value);
            }
    
            public static readonly DependencyProperty RippleDurationProperty =
                DependencyProperty.RegisterAttached("RippleDuration", typeof(TimeSpan), typeof(RippleHelper), new PropertyMetadata(TimeSpan.FromMilliseconds(330)));
    
            public static double GetRippleRadius(UIElement obj)
            {
                return (double)obj.GetValue(RippleRadiusProperty);
            }
    
            public static void SetRippleRadius(UIElement obj, double value)
            {
                obj.SetValue(RippleRadiusProperty, value);
            }
    
            public static readonly DependencyProperty RippleRadiusProperty =
                DependencyProperty.RegisterAttached("RippleRadius", typeof(double), typeof(RippleHelper), new PropertyMetadata(100d));
    
            public static Color GetRippleColor(UIElement obj)
            {
                return (Color)obj.GetValue(RippleColorProperty);
            }
    
            public static void SetRippleColor(UIElement obj, Color value)
            {
                obj.SetValue(RippleColorProperty, value);
            }
    
            public static readonly DependencyProperty RippleColorProperty =
                DependencyProperty.RegisterAttached("RippleColor", typeof(Color), typeof(RippleHelper), new PropertyMetadata(Colors.White));
    

    接下来再写一个附加属性和一个enum

    public static RippleHelperState GetRippleHelperState(UIElement obj)
            {
                return (RippleHelperState)obj.GetValue(RippleHelperStateProperty);
            }
    
            public static void SetRippleHelperState(UIElement obj, RippleHelperState value)
            {
                obj.SetValue(RippleHelperStateProperty, value);
            }
    
            public static readonly DependencyProperty RippleHelperStateProperty =
                DependencyProperty.RegisterAttached("RippleHelperState", typeof(RippleHelperState), typeof(RippleHelper), new PropertyMetadata(RippleHelperState.None, (s, e) =>
                {
                    if (e.NewValue != null && e.OldValue != e.NewValue)
                    {
                        var value = (RippleHelperState)e.NewValue;
                        var oldvalue = (RippleHelperState)e.OldValue;
                        if (s is UIElement ele)
                        {
                            switch (value)
                            {
                                case RippleHelperState.Pressed:
                                    {
                                        ele.RemoveHandler(UIElement.PointerReleasedEvent, pointerEventHandler);
                                        ele.AddHandler(UIElement.PointerPressedEvent, pointerEventHandler, true);
                                    }
                                    break;
    
                                case RippleHelperState.Released:
                                    {
                                        ele.RemoveHandler(UIElement.PointerPressedEvent, pointerEventHandler);
                                        ele.AddHandler(UIElement.PointerReleasedEvent, pointerEventHandler, true);
                                    }
                                    break;
    
                                case RippleHelperState.None:
                                    {
                                        ele.RemoveHandler(UIElement.PointerPressedEvent, pointerEventHandler);
                                        ele.RemoveHandler(UIElement.PointerReleasedEvent, pointerEventHandler);
                                        ElementCompositionPreview.SetElementChildVisual(ele, null);
                                    }
                                    break;
                            }
                        }
                    }
                }));

    在命名空间里建立enum

        public enum RippleHelperState
        {
            Pressed, Released, None
        }

    然后编写两个鼠标事件,对应RippleHelperState的Pressed和Released两个状态

            private static void Ele_PointerPressed(object sender, PointerRoutedEventArgs e)
            {
                if (sender is UIElement ele)
                {
                    var position = e.GetCurrentPoint(ele).Position.ToVector2();
                    StartRippleAnimation(ele, position);
                }
            }
    
            private static void Ele_PointerReleased(object sender, PointerRoutedEventArgs e)
            {
                if (sender is UIElement ele)
                {
                    var position = e.GetCurrentPoint(ele).Position.ToVector2();
                    StartRippleAnimation(ele, position);
                }
            }
            public static void StartRippleAnimation(UIElement ele, Vector2 position)
            {
                StartRippleAnimation(ele, position, GetRippleColor(ele), GetIsFillEnable(ele), GetRippleDuration(ele), GetRippleRadius(ele));
            }
    
            public static void StartRippleAnimation(UIElement ele, Vector2 position, Color color, bool isFillEnable, TimeSpan duration, double radius = 0)
            {
                var hostVisual = ElementCompositionPreview.GetElementVisual(ele);
                var cVisual = ElementCompositionPreview.GetElementChildVisual(ele) as ContainerVisual;
                if (cVisual == null)
                {
                    cVisual = compositor.CreateContainerVisual();
                    SizeBind.ClearParameter("hostVisual");
                    SizeBind.SetReferenceParameter("hostVisual", hostVisual);
                    cVisual.StartAnimation("Size", SizeBind);
                    cVisual.Clip = compositor.CreateInsetClip();
                    ElementCompositionPreview.SetElementChildVisual(ele, cVisual);
                }
    
                var sVisual = CreateSpriteVisual(ele, color);
                cVisual.Children.InsertAtTop(sVisual);
                sVisual.Offset = new Vector3(position.X, position.Y, 0f);
    
                if (isFillEnable)
                {
                    var nWidth = Math.Max(Math.Max(position.X, ele.RenderSize.Width - position.X), Math.Max(position.Y, ele.RenderSize.Height - position.Y));
                    var r = Math.Sqrt(nWidth * nWidth * 2);
                    var finalScale = (float)r / 45f;
                    PropSet.InsertScalar("ScaleValue", finalScale);
                    ScaleAnimation.Duration = TimeSpan.FromMilliseconds(400);
                    OpacityAnimation.Duration = TimeSpan.FromMilliseconds(430);
                }
                else
                {
                    if (radius == 100d)
                    {
                        PropSet.InsertScalar("ScaleValue", 2f);
                    }
                    else
                    {
                        PropSet.InsertScalar("ScaleValue", (float)GetRippleRadius(ele) / 45f);
                    }
                }
    
                ScaleAnimation.Duration = duration;
                OpacityAnimation.Duration = duration;
    
                var batch = compositor.GetCommitBatch(CompositionBatchTypes.Animation);
                batch.Completed += (s1, e1) =>
                {
                    OnRippleComplated(ele);
                    cVisual.Children.Remove(sVisual);
                };
                sVisual.StartAnimationGroup(RippleAnimationGroup);
            }

     动画完成的事件:

            public static event EventHandler RippleComplated;
    
            private static void OnRippleComplated(UIElement ele)
            {
                RippleComplated?.Invoke(ele, EventArgs.Empty);
            }

    最后在类的开头编写Composition的动画和资源:

            private static readonly PointerEventHandler pointerEventHandler = new PointerEventHandler(Ele_PointerReleased);
            private static Compositor compositor => Window.Current.Compositor;
            private static ExpressionAnimation _SizeBind;
            private static CompositionEasingFunction _EaseOut;
            private static ScalarKeyFrameAnimation _OpacityAnimation;
            private static Vector3KeyFrameAnimation _ScaleAnimation;
            private static CompositionAnimationGroup _RippleAnimationGroup;
            private static CompositionPropertySet _PropSet;
            private static CompositionBrush _Mask;
    
            private static ExpressionAnimation SizeBind
            {
                get
                {
                    if (_SizeBind == null) _SizeBind = compositor.CreateExpressionAnimation("hostVisual.Size");
                    return _SizeBind;
                }
            }
    
            private static CompositionEasingFunction EaseOut
            {
                get
                {
                    if (_EaseOut == null) _EaseOut = compositor.CreateCubicBezierEasingFunction(new Vector2(0f, 0f), new Vector2(0.9f, 1f));
                    return _EaseOut;
                }
            }
    
            private static ScalarKeyFrameAnimation OpacityAnimation
            {
                get
                {
                    if (_OpacityAnimation == null)
                    {
                        _OpacityAnimation = compositor.CreateScalarKeyFrameAnimation();
                        _OpacityAnimation.InsertKeyFrame(0f, 1f, EaseOut);
                        _OpacityAnimation.InsertKeyFrame(1f, 0f, EaseOut);
                        _OpacityAnimation.Duration = TimeSpan.FromMilliseconds(350);
                        _OpacityAnimation.Target = "Opacity";
                    }
                    return _OpacityAnimation;
                }
            }
    
            private static Vector3KeyFrameAnimation ScaleAnimation
            {
                get
                {
                    if (_ScaleAnimation == null)
                    {
                        _ScaleAnimation = compositor.CreateVector3KeyFrameAnimation();
                        _ScaleAnimation.InsertKeyFrame(0f, new Vector3(0f, 0f, 1f), EaseOut);
                        _ScaleAnimation.InsertExpressionKeyFrame(0.8f, "Vector3(propSet.ScaleValue,propSet.ScaleValue,1f)", EaseOut);
                        _ScaleAnimation.InsertExpressionKeyFrame(1f, "Vector3(propSet.ScaleValue,propSet.ScaleValue,1f)", EaseOut);
                        _ScaleAnimation.SetReferenceParameter("propSet", PropSet);
                        _ScaleAnimation.Duration = TimeSpan.FromMilliseconds(320);
                        _ScaleAnimation.Target = "Scale";
                    }
                    return _ScaleAnimation;
                }
            }
    
            private static CompositionAnimationGroup RippleAnimationGroup
            {
                get
                {
                    if (_RippleAnimationGroup == null)
                    {
                        _RippleAnimationGroup = compositor.CreateAnimationGroup();
                        _RippleAnimationGroup.Add(OpacityAnimation);
                        _RippleAnimationGroup.Add(ScaleAnimation);
                    }
                    return _RippleAnimationGroup;
                }
            }
    
            private static CompositionPropertySet PropSet
            {
                get
                {
                    if (_PropSet == null)
                    {
                        _PropSet = compositor.CreatePropertySet();
                        PropSet.InsertScalar("ScaleValue", 2f);
                    }
                    return _PropSet;
                }
            }
    
            private static CompositionBrush Mask
            {
                get
                {
                    if (_Mask == null)
                    {
                        var surface = LoadedImageSurface.StartLoadFromUri(new Uri("ms-appx:///MaterialLibs/Assets/RippleMask.png"), new Windows.Foundation.Size(100d, 100d));
                        _Mask = compositor.CreateSurfaceBrush(surface);
                    }
                    return _Mask;
                }
            }

     最后在Mask读取的Uri的对应位置放上如下的图片文件:

    完整代码已经开源在Github:https://github.com/cnbluefire/MaterialLibs

    受个人技术所限,没有想到怎么做到圆角或者不规则图形,所以目前只支持直角矩形控件

  • 相关阅读:
    python多线程
    python网络编程-udp/tcp通信
    通达OA未授权任意文件上传及文件包含导致远程代码执行漏洞
    星盟安全awd复盘20200314
    Axublog1.1.0代码审计
    回调函数绕过D盾小套路
    php7.1后webshell免杀的去路
    GO语言 特性概要
    Openstack 笔记概要
    大数据--基本理论知识(1)
  • 原文地址:https://www.cnblogs.com/blue-fire/p/8575968.html
Copyright © 2011-2022 走看看