WPF水珠效果按钮组
效果图
相关知识
这部分基本就是废话,网上都能找到,我只不过是整理了以下.建议先不看,用到的时候可以回来看看
贝塞尔曲线
先来看两组图,有助于理解什么是贝塞尔曲线(图片取自维基百科,参考链接1)
二次贝塞尔曲线:
P0是起点,P2是终点,P1是控制点
三次贝塞尔曲线:
P0是起点,P3是终点,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度
a = c * Math.Sin(2 * Math.PI / 8 * i - (itemsSource.Count - 1) * 2 * Math.PI / 8 / 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.zip
参考连接:
- https://en.wikipedia.org/wiki/B%C3%A9zier_curve
- https://baike.baidu.com/item/%E8%B4%9D%E5%A1%9E%E5%B0%94%E6%9B%B2%E7%BA%BF/1091769?fr=aladdin
- https://www.cnblogs.com/ArthurQQ/articles/1730214.html
- http://blog.csdn.net/chouglas/article/details/50967566
WPF水珠效果按钮组的更多相关文章
- WPF实现Twitter按钮效果
最近上网看到这个CSS3实现的Twitter按钮,感觉很漂亮,于是想用WPF来实现下. 实现这个效果,参考了CSS3 原文地址:http://www.html5tricks.com/css3-twit ...
- WPF实现Twitter按钮效果(转)
最近上网看到这个CSS3实现的Twitter按钮,感觉很漂亮,于是想用WPF来实现下. 实现这个效果,参考了CSS3 原文地址:http://www.html5tricks.com/css3-twit ...
- Bootstrap3系列:按钮组
1. 基本实例 1.1 示例代码 <div class="btn-group"> <button type="button" class=&q ...
- Bootstrap系列 -- 29. 按钮组
单个按钮在Web页面中的运用有时候并不能满足我们的业务需求,常常会看到将多个按钮组合在一起使用,比如富文本编辑器里的一组小图标按钮等 按钮组和下拉菜单组件一样,需要依赖于button.js插件才能正常 ...
- Bootstrap--组件之按钮组
什么是按钮组呢?简单解释:就是把一堆的按钮放在一行或者一列中.下面来看一个实例. 按钮组嘛,首先是按钮,所以会用到.btn这个类,还有就是bootstrap提供的按钮组.btn-group这个类,所以 ...
- BootStrap 按钮组简单介绍
学会按钮组需要掌握以下几个类. btn btn-group btn-toolbar btn-group-vertical 和 下拉菜单的基本类 dropdown-toggle dropdow ...
- Bootstrap入门(十)组件4:按钮组与下拉菜单结合
Bootstrap入门(十)组件4:按钮组与下拉菜单结合 先引入本地的CSS文件和JS文件(注:1.bootstrap是需要jQuery支持的.2.需要在<body>当中添加) < ...
- Bootstrap入门(九)组件3:按钮组
Bootstrap入门(九)组件3:按钮组 先引入本地的CSS文件和JS文件(注:1.bootstrap是需要jQuery支持的.2.需要在<body>当中添加) <link h ...
- QButtonGroup按钮组
继承 QObject 提供 一个抽象的按钮容器, 可以将多个按钮划分为一组,不具备可视化的效果,一般放的都是可以被检查的按钮 import sys from PyQt5.QtWidgets impo ...
随机推荐
- C语言程序内存布局
C语言程序内存布局 如有转载,请注明出处:http://blog.csdn.net/embedded_sky/article/details/44457453 作者:super_bert@csdn 一 ...
- Android 开发笔记___初级控件之实战__计算器
功能简单,实现并不难,对于初学者可以总和了解初级控件的基本使用. 用到的知识点如下: 线性布局 LinearLayout:整体界面是从上往下的,因此需要垂直方向的linearlayout:下面每行四个 ...
- HTML5与phonegap接口对比
HTML5与phonegap接口对比 接口 HTML5 phonegap 差异 地理定位 geolocation 单次定位: navigator.geolocation.getCurrentPosit ...
- Linux学习(二十)软件安装与卸载(三)源码包安装
一.概述 源码包安装的优点在于它自由程度比较高,可以指定目录与组件.再有,你要是能改源码也可以. 二.安装方法 步骤 1.从官网或者信任站点下载源码包 [root@localhost ~]# wget ...
- java非阻塞IO(NIO)流程
单线程 多线程(Netty/Mina)
- (二)springboot整合thymeleaf模板
在我们平时的开发中,用了很久的jsp作view显示层,但是标签库和JSP缺乏良好格式的一个副作用就是它很少能够与其产生的HTML类似.所以,在Web浏览器或HTML编辑器中查看未经渲染的JSP模板是非 ...
- C#获取指定月指定周的日期范围
); MessageBox.Show(end.ToShortDateString());
- SimpleMembership
最近2个月以来,一直在学习MVC,从最开始的2,一直到最新的4.从原来的aspx到现在的Razor引擎,越学越开心,越学越上瘾. 最近为新项目做准备,打算用MVC4,VS2012+SQL2012,反正 ...
- JDBC数据源 使用JNDI连接池实现数据库的连接
0.引言 许多Web应用程序需要通过JDBC驱动程序访问数据库,以支持该应用程序所需的功能.Java EE平台规范要求Java EE应用程序服务器为此目的提供一个DataSource实现(即,用于JD ...
- Android WebView 上传各种文件(包括拍照 录像 录音 文件 音乐 等,用到图片或拍照的,可以参考下)
我也是从网上扒下来的,经过多次实验,找到了个好用的.网上能搜到最多的也就是这个解决方案,我英文不好,也没仔细研究,但大多数都是出自这: http://stackoverflow.com/questio ...