先放效果图:

首先,建立一个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())); 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 = )
{
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 * );
var finalScale = (float)r / 45f;
PropSet.InsertScalar("ScaleValue", finalScale);
ScaleAnimation.Duration = TimeSpan.FromMilliseconds();
OpacityAnimation.Duration = TimeSpan.FromMilliseconds();
}
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();
_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();
_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

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

UWP:使用Composition实现类似安卓的水波纹Ripple效果的更多相关文章

  1. html5 +css3 点击后水波纹扩散效果 兼容移动端

    <!DOCTYPE html> <html> <head> <meta http-equiv="Content-Type" content ...

  2. css 滚动视差 之 水波纹效果

    核心属性: background-attachment 这个属性就牛逼了, 它可以定义背景图片是相对视口固定, 还是随着视口滚动, 加上这个属性网页瞬间就从屌丝变成 高大上. 我们来看个例子: htm ...

  3. Android水波纹特效的简单实现

    我的开源页面指示器框架 MagicIndicator,各位一定不要错过哦. 水波纹特效,想必大家或多或少见过,在我的印象中,大致有如下几种: 支付宝 "咻咻咻" 式 流量球 &qu ...

  4. Android 水波纹点击效果(Ripple Effect)

    上周Android发布了Android M的Preview版本.但想必Android5.0很多炫酷效果,多数开发者还没有使用过,那更不要说广大用户了. 本文介绍的是Android5.0中其中一个炫酷的 ...

  5. CSS3 水波纹

    css3 动画设置水波纹,效果如下图: 源码: <!DOCTYPE html> <html lang="en"> <head> <meta ...

  6. android: Android水波纹点击效果

    Android API 21及以上新增了ripple标签用来实现水波纹的效果.我们可以通过设置ripple背景来实现一些View点击效果. 该水波纹效果有两种:一种是有界的(点击后类似于一个矩形向四周 ...

  7. iOS 自定义任意形状加载进度条(水波纹进度条)

    1. 项目中要做类似下面的加载动画: 先给出安卓的实现方式 2.iOS的实现方式参考了下面两位的,感谢. 以任意底部图片为背景的加载动画 和 水波纹动画 最后附上自己的demo

  8. Android特效专辑(十)——点击水波纹效果实现,逻辑清晰实现简单

    Android特效专辑(十)--点击水波纹效果实现,逻辑清晰实现简单 这次做的东西呢,和上篇有点类似,就是用比较简单的逻辑思路去实现一些比较好玩的特效,最近也是比较忙,所以博客更新的速度还得看时间去推 ...

  9. WebGL——水波纹特效

    大家好,今天我ccentry要做一个水波纹特效,我们来看看水波纹特效的做法.首先我们来看一下水波纹特效的效果是怎么样的,请看下图. 我们要做的就是类似这种纹理特效,那么我们来看看是如何制作的吧.首先鲫 ...

随机推荐

  1. MySql主键自动生成,表、实体、C#调用方法

    1.表:mysql建表语句 DROP TABLE IF EXISTS `keycode`; CREATE TABLE `keycode` ( `Id` ) NOT NULL AUTO_INCREMEN ...

  2. 在windows端和linux端安装Git

    一.Git的安装 1. 在windows端 到地址:https://git-scm.com/downloads 选择对应版本下载后,进行傻瓜式安装即可 2.  在linux端 查看是否安装了git,出 ...

  3. session垃圾回收机制

    主要有以下三个参数 session.gc_maxlifetime:session生命周期 session.gc-devisor:启动session回收机制频率的被除数(分母) session.gc_p ...

  4. Java中的volatile的作用和synchronized作用

    volatile该关键字是主要使用的场合是字啊多个线程中可以感知实例的变量被更改了并且可以获取到最新的值进行使用,也就是用多线程读取共享变量的时候可以获取到最新的值使用.不能保障原子性 如果你在jvm ...

  5. 快速入门vue-cli配置

    作为一名使用了一段时间Vue.js的新手,相信和不少初入Vue的朋友一样,都对Vue-cli的配置一知半解.后来通过对webpack的学习,也算是对脚手架的配置有了一定的了解,所以也想把这段时间自己的 ...

  6. Java遍历文件目录

    函数介绍 File[] listFiles():返回当前文件的子目录或子文件的文件数组. 遍历目录 调用listFiles()即可得文件的子目录和子文件,如果存在子目录,那么子目录需要再次调用list ...

  7. 转载微信公众号 测试那点事:Jmeter乱码解决

    原文地址: http://mp.weixin.qq.com/s/4Li5z_-rT0HPPQx9Iyi5UQ  中文乱码一直都是比较让人棘手的问题,我们在使用Jmeter的过程中,也会遇到中文乱码问题 ...

  8. 电脑中dll文件丢失怎么恢复?

    DLL文件是Windows系统中的动态链接文件,我们在运行程序时都必须链接到dll文件,如果缺少了则无法正常运行,相信大家都会遇到dll文件缺失的情况,那么电脑中dll文件丢失怎么恢复?下面装机之家分 ...

  9. linux dhcp搭建及pxe无人值守装机

    DHCP动态主机配置协议:由IETF组织制定,用来简化主机ip地址分配管理可以自动分配的入网参数ip地址/子网掩码/广播地址默认网关地址DNS服务器地址 ----------------------- ...

  10. 了解c3p0,dbcp与druid

    说到druid,这个是在开源中国开源项目中看到的,说是比较好的数据连接池.于是乎就看看.扯淡就到这. 下面就讲讲用的比较多的数据库连接池.(其实我最先接触的是dbcp这个) 1)DBCP DBCP是一 ...