WPF水珠效果按钮组的实现教程
 更新时间:2017年12月18日 14:24:41   作者:普通的地球人  

下面小编就为大家分享一篇WPF水珠效果按钮组的实现教程,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧

效果图

相关知识
这部分基本就是废话,网上都能找到,我只不过是整理了以下.建议先不看,用到的时候可以回来看看
贝塞尔曲线
先来看两组图,有助于理解什么是贝塞尔曲线(图片取自维基百科,参考链接1)
二次贝塞尔曲线:

P0是起点,P2是终点,P1是控制点
三次贝塞尔曲线:

P0是起点,P2是终点,P1是控制点1,P2是控制点2
依次连接所有点,组成线段
t是比例,在0-1之间,就是每条线段的长度都是1
贝塞尔曲线就是最里层的线段在t位置的点所组成的路径
三次贝塞尔曲线公式:B(t)=(1-t)^3*P0+3(1-t)^2*t*P1+3(1-t)*t^2*P2+t^3*P3,0<=t<=1
B(t)代表曲线上任意点,P0,1,2,3分别代表决定曲线的4个点,t代表曲线长度为1的任意取值
其他知识
没接触过贝塞尔曲线的话,可能得花些时间整理下,其他的知识就比较简单了

直角三角形,角A的对边a,临边b,斜边c
三角函数:
sinA=a/c
cosA=b/c
勾股定理:
c^2=a^2+b^2
概括介绍
这个效果难点就两部分:一是水球分离和融合时候的连接,二是主体圆的抖动
然而其实网上都有解决方案了
第一部分是在两个圆之间加个用贝塞尔曲线组成的path,用一样的颜色,其实是障眼法.见参考链接4
第二部分是用4段三次贝塞尔曲线组成的path代替Ellipse,因为Ellipse是抖动不起来的,这样就可以控制贝塞尔曲线的点来让圆抖动.见参考链接3
主体的大圆
Path画法
主体的大圆是个ToggleButton,替换模版,背景换成贝塞尔曲线组成的圆.
每个贝塞尔曲线的起点和终点就不说了,非常简单,这里主要说说控制点.

计算出1/4圆弧的中间位置的点,此时t=0.5, 三角型边长h=sin45*r
让控制点P1,P2分别在起点和终点的切线上,P1到X轴的距离等于P2到Y轴的距离L
B(0.5)=h=sin45*r=(1-0.5)^3*0+3*(1-0.5)^2*0.5*L+3*(1-0.5)*0.5^2*r+0.5^3*r
sin45*r=0+0.375*L+0.375*r+0.125*r
L=(sin45*r-0.5*r)/0.375
于是两个控制点(r,L)和(L,r)可以确定
求出来的两个点是数学的坐标,要转换成程序的坐标,对应成4个象限,无非就是加减半径,加减L,不细说了
完整的path,取半径是50,见代码

<Path>
<Path.Data>
<PathGeometry>
<PathFigure StartPoint=”50,0″>
<BezierSegment Point1=”77.614237491541,0″ Point2=”100,22.385762508459″ Point3=”100,50″></BezierSegment>
<BezierSegment Point1=”100,77.614237491541″ Point2=”77.614237491541,100″ Point3=”50,100″></BezierSegment>
<BezierSegment Point1=”22.385762508459,100″ Point2=”0,77.614237491541″ Point3=”0,50″></BezierSegment>
<BezierSegment Point1=”0,22.385762508459″ Point2=”22.385762508459,0″ Point3=”50,0″></BezierSegment>
</PathFigure>
</PathGeometry>
</Path.Data>
</Path>

抖动动画
由于圆是被12个点控制的,让圆抖动,也就是对12个点做点动画
可以用关键帧动画,这样控制的比较细致,要注意的是,衔接的地方要平滑.我这里做的比较简陋,就找了一个变换后的图形,重复了5次.如果你有兴趣,可以多做些,做的越多,动画看起来表现力越强
这里我并没有去研究什么算法,就是简单的在blend里,找了一些点
见代码:

<EventTrigger RoutedEvent=”Click”>
<BeginStoryboard>
<Storyboard>
<PointAnimationUsingKeyFrames Storyboard.TargetName=”pf_main” Storyboard.TargetProperty=”StartPoint” BeginTime=”0:0:0.7″ AutoReverse=”True” RepeatBehavior=”5x” FillBehavior=”Stop”>
<EasingPointKeyFrame Value=”40,0″ KeyTime=”0:0:0.2″></EasingPointKeyFrame>
</PointAnimationUsingKeyFrames>
<PointAnimationUsingKeyFrames Storyboard.TargetName=”bs_main0″ Storyboard.TargetProperty=”Point1″ BeginTime=”0:0:0.7″ AutoReverse=”True” RepeatBehavior=”5x” FillBehavior=”Stop”>
<EasingPointKeyFrame Value=”68,-10″ KeyTime=”0:0:0.2″></EasingPointKeyFrame>
</PointAnimationUsingKeyFrames>
<PointAnimationUsingKeyFrames Storyboard.TargetName=”bs_main0″ Storyboard.TargetProperty=”Point2″ BeginTime=”0:0:0.7″ AutoReverse=”True” RepeatBehavior=”5x” FillBehavior=”Stop”>
<EasingPointKeyFrame Value=”115,14″ KeyTime=”0:0:0.2″></EasingPointKeyFrame>
</PointAnimationUsingKeyFrames>
<PointAnimationUsingKeyFrames Storyboard.TargetName=”bs_main0″ Storyboard.TargetProperty=”Point3″ BeginTime=”0:0:0.7″ AutoReverse=”True” RepeatBehavior=”5x” FillBehavior=”Stop”>
<EasingPointKeyFrame Value=”100,66″ KeyTime=”0:0:0.2″></EasingPointKeyFrame>
</PointAnimationUsingKeyFrames>
<PointAnimationUsingKeyFrames Storyboard.TargetName=”bs_main1″ Storyboard.TargetProperty=”Point1″ BeginTime=”0:0:0.7″ AutoReverse=”True” RepeatBehavior=”5x” FillBehavior=”Stop”>
<EasingPointKeyFrame Value=”100,67″ KeyTime=”0:0:0.2″></EasingPointKeyFrame>
</PointAnimationUsingKeyFrames>
<PointAnimationUsingKeyFrames Storyboard.TargetName=”bs_main1″ Storyboard.TargetProperty=”Point2″ BeginTime=”0:0:0.7″ AutoReverse=”True” RepeatBehavior=”5x” FillBehavior=”Stop”>
<EasingPointKeyFrame Value=”85,111″ KeyTime=”0:0:0.2″></EasingPointKeyFrame>
</PointAnimationUsingKeyFrames>
<PointAnimationUsingKeyFrames Storyboard.TargetName=”bs_main1″ Storyboard.TargetProperty=”Point3″ BeginTime=”0:0:0.7″ AutoReverse=”True” RepeatBehavior=”5x” FillBehavior=”Stop”>
<EasingPointKeyFrame Value=”33,103″ KeyTime=”0:0:0.2″></EasingPointKeyFrame>
</PointAnimationUsingKeyFrames>
<PointAnimationUsingKeyFrames Storyboard.TargetName=”bs_main2″ Storyboard.TargetProperty=”Point1″ BeginTime=”0:0:0.7″ AutoReverse=”True” RepeatBehavior=”5x” FillBehavior=”Stop”>
<EasingPointKeyFrame Value=”22,103″ KeyTime=”0:0:0.2″></EasingPointKeyFrame>
</PointAnimationUsingKeyFrames>
<PointAnimationUsingKeyFrames Storyboard.TargetName=”bs_main2″ Storyboard.TargetProperty=”Point2″ BeginTime=”0:0:0.7″ AutoReverse=”True” RepeatBehavior=”5x” FillBehavior=”Stop”>
<EasingPointKeyFrame Value=”-15,85″ KeyTime=”0:0:0.2″></EasingPointKeyFrame>
</PointAnimationUsingKeyFrames>
<PointAnimationUsingKeyFrames Storyboard.TargetName=”bs_main2″ Storyboard.TargetProperty=”Point3″ BeginTime=”0:0:0.7″ AutoReverse=”True” RepeatBehavior=”5x” FillBehavior=”Stop”>
<EasingPointKeyFrame Value=”-6,50″ KeyTime=”0:0:0.2″></EasingPointKeyFrame>
</PointAnimationUsingKeyFrames>
<PointAnimationUsingKeyFrames Storyboard.TargetName=”bs_main3″ Storyboard.TargetProperty=”Point1″ BeginTime=”0:0:0.7″ AutoReverse=”True” RepeatBehavior=”5x” FillBehavior=”Stop”>
<EasingPointKeyFrame Value=”4,9″ KeyTime=”0:0:0.2″></EasingPointKeyFrame>
</PointAnimationUsingKeyFrames>
<PointAnimationUsingKeyFrames Storyboard.TargetName=”bs_main3″ Storyboard.TargetProperty=”Point2″ BeginTime=”0:0:0.7″ AutoReverse=”True” RepeatBehavior=”5x” FillBehavior=”Stop”>
<EasingPointKeyFrame Value=”41,-1″ KeyTime=”0:0:0.2″></EasingPointKeyFrame>
</PointAnimationUsingKeyFrames>
<PointAnimationUsingKeyFrames Storyboard.TargetName=”bs_main3″ Storyboard.TargetProperty=”Point3″ BeginTime=”0:0:0.7″ AutoReverse=”True” RepeatBehavior=”5x” FillBehavior=”Stop”>
<EasingPointKeyFrame Value=”42,0″ KeyTime=”0:0:0.2″></EasingPointKeyFrame>
</PointAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
</EventTrigger>

item
按钮的位置
不管是奇数个,还是偶数个,我们都想让它以Y轴对称
首先把圆分成8等份,每一份都是45度,也就是最多只能放下8个item,

从上图可以看出来,其实就是奇数个在线上,偶数个在两线之间
有个简单的办法,就是先在顶点依次顺时针排列,每个item间隔45度,然后再逆时针旋转,每多一个item就多转45/2度(两条分割线是45度,中间也就是45/2度),如下图:

上图是item终点的位置,起点的位置是在圆心.
动画用DoubleAnimation控制item按钮的位移,从圆心移动到计算后的位置
计算位置的代码:

//函数是弧度制 2PI是360度
1 a = c * Math.Sin(2 * Math.PI / 8 * i – (itemsSource.Count – 1) * 2 * Math.PI / 8 / 2);
2 b = c * Math.Cos(2 * Math.PI / 8 * i – (itemsSource.Count – 1) * 2 * Math.PI / 8 / 2);

水球连接的部分
连接的部分是用两个二次贝塞尔和一条直线做一个path

开始的时候,两条贝塞尔曲线的高度是0,控制点在path所在矩形的边上,然后对而塞尔曲线上面的点和控制点做动画,分别向上和内移动,最终形成上图右边的图形,然后把这个动画和item按钮向外部移动的动画结合起来,就伪装成了水球分离的效果.

上图红色矩形就是连接部分的path.动画的过程就是Item按钮的直径和大圆相交的时候开始和item按钮一起做动画,最后移动到Item按钮直径所在的位置,整个距离就是Item的半径+item到主体的距离+蓝色的d,而蓝色的d可以通过公式求出
开始的时候也是让连接部分path在圆心的位置.定位方法和定位Item按钮的方法是完全一样的.这里就不在重复了.只说一下c边的距离是:大圆和小圆圆心的距离-连接path高度的一半
源码下载:WaterDropsButtonGroup(jb51.net).zip
以上这篇WPF水珠效果按钮组的实现教程就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持华域联盟。

您可能感兴趣的文章:WPF自定义控件和样式之自定义按钮(Button)WPF制作带小箭头的按钮完整代码WPF图片按钮的实现方法WPF中button按钮同时点击多次触发click解决方法WPF MVVM制作发送短信小按钮WPF微信聊天和通讯录按钮样式代码分享WPF实现窗体中的悬浮按钮

WPF
水珠
按钮

相关文章
浅谈VS中的DataPager分页这篇文章主要介绍了浅谈VS中的DataPager分页的相关资料和示例,需要的朋友可以参考下 2015-02-02
详解Asp.Net母版页元素ID不一致的体现由于总体排版和设计的需要,我们往往创建母版页来实现整个网站的统一性,这篇文章主要介绍了详解Asp.Net母版页元素ID不一致的体现,感兴趣的小伙伴们可以参考一下 2018-11-11
SignalR Self Host+MVC等多端消息推送服务(一)这篇文章主要为大家详细介绍了SignalR Self Host+MVC等多端消息推送服务,具有一定的参考价值,感兴趣的小伙伴们可以参考一下 2017-06-06
未处理的事件"PageIndexChanging" 之解决方案今天我写一个小程序遇到这个问题,上网搜了一下,已经有很好的解决方法了,以前都是拉控件自己生成,现在用代码自己写就出现了这个问题 2008-07-07
CheckBoxList两列并排编译为表格显示具体实现CheckBoxList两列并排的显示效果相比大家都有见到过吧,下面是具体的实现代码,感兴趣的朋友可以参考下哈 2013-05-05
Asp.Net套用母版页后元素ID不一致(个人总结)这篇文章主要介绍了Asp.Net套用母版页后元素ID不一致(个人总结),小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧 2018-11-11
asp.net viewstate 回发机制ASP.NET中,为了模拟Winform中的事件响应机制,微软的工程师真是煞费苦心,发明了“回发”机制,使得编写WEB页面变得和Winform一样简单。 2010-03-03
从请求管道深入剖析HttpModule的实现机制图文介绍想要了解底层的原理必须对请求处理过程和页面的生命周期有点了解才方便您入门学习一下内容,本文将详细介绍 2012-11-11
asp.net Coolite 学习交流近来不知什么原因,发现国人有不少开始学习Coolite了,对于有些人可能不知道什么是Coolite,这里先简单介绍一下 2009-05-05
ASP.NET实现级联下拉框效果实例讲解这篇文章主要为大家详细介绍了ASP.NET实现级联下拉框效果实例,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下 2015-09-09

最新评论

声明:本站(华域联盟www.cnhackhy.com)所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。