视频1 视频21 视频41 视频61 视频文章1 视频文章21 视频文章41 视频文章61 推荐1 推荐3 推荐5 推荐7 推荐9 推荐11 推荐13 推荐15 推荐17 推荐19 推荐21 推荐23 推荐25 推荐27 推荐29 推荐31 推荐33 推荐35 推荐37 推荐39 推荐41 推荐43 推荐45 推荐47 推荐49 关键词1 关键词101 关键词201 关键词301 关键词401 关键词501 关键词601 关键词701 关键词801 关键词901 关键词1001 关键词1101 关键词1201 关键词1301 关键词1401 关键词1501 关键词1601 关键词1701 关键词1801 关键词1901 视频扩展1 视频扩展6 视频扩展11 视频扩展16 文章1 文章201 文章401 文章601 文章801 文章1001 资讯1 资讯501 资讯1001 资讯1501 标签1 标签501 标签1001 关键词1 关键词501 关键词1001 关键词1501 专题2001 知道1 知道21 知道41 知道61 知道81 知道101 知道121 知道141 知道161 知道181 知道201 知道221 知道241 知道261 知道281
问答文章1 问答文章501 问答文章1001 问答文章1501 问答文章2001 问答文章2501 问答文章3001 问答文章3501 问答文章4001 问答文章4501 问答文章5001 问答文章5501 问答文章6001 问答文章6501 问答文章7001 问答文章7501 问答文章8001 问答文章8501 问答文章9001 问答文章9501
WPF水珠效果按钮组的实现教程
2020-11-27 22:35:07 责编:小采
文档

效果图

相关知识

这部分基本就是废话,网上都能找到,我只不过是整理了以下.建议先不看,用到的时候可以回来看看

贝塞尔曲线

先来看两组图,有助于理解什么是贝塞尔曲线(图片取自维基百科,参考链接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水珠效果按钮组的实现教程就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持脚本之家。

下载本文
显示全文
专题