博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
UWP:使用Composition实现类似安卓的水波纹Ripple效果
阅读量:4925 次
发布时间:2019-06-11

本文共 11719 字,大约阅读时间需要 39 分钟。

先放效果图:

首先,建立一个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://www.cnblogs.com/blue-fire/p/8575968.html

你可能感兴趣的文章
C# Select
查看>>
【转】关于Scapy
查看>>
关于AES加密,以及各种分组加密
查看>>
修改 Win10 默认输入法为英语(美式键盘)
查看>>
IE浏览器使用VLC实时显示视频(海康、大华)
查看>>
计算机网络介绍,TCP协议,Socket网络编程
查看>>
移动端页面输入法挡住input输入框的解决方法
查看>>
操作系统--进程
查看>>
LWP::UserAgent - Web user agent class Web 用户agent 类:
查看>>
zookeeper 手动T掉已挂节点
查看>>
apache commons io入门
查看>>
在OS X 10.9配置WebDAV服务器联合NSURLSessionUploadTask实现文件上传
查看>>
C语言位运算
查看>>
OSI七层协议模型、TCP/IP四层模型学习笔记
查看>>
windown vs2012 编译ffplay
查看>>
RTMP协议规范(转载)
查看>>
盘点那些大牌互联网公司内部使用的JavaScript库
查看>>
CentOS 7.0下使用yum安装MySQL
查看>>
vue初级学习--路由router的编写(resolve的使用)
查看>>
批处理学习01
查看>>