WPF 实现水珠效果按钮组

本文经原作者授权以原创方式二次分享,欢迎转载、分享。

原文作者:普通的地球人

原文地址:https://www.cnblogs.com/tsliwei/p/8041928.html

相关知识

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

贝塞尔曲线

先来看两组图,有助于理解什么是贝塞尔曲线(图片取自维基百科,参考链接)

二次贝塞尔曲线:

5e65c63e19961054a532960cf12eed4b.pngabe4904afdafc4d0d0e8b9ef354bf473.gif

P0是起点,P2是终点,P1是控制点

三次贝塞尔曲线:

a5c62b924dad7cdd4d593f3997ff8e63.png4f145da9cc27084b0f52c3a39e8cd0ed.gif

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的任意取值

其他知识

没接触过贝塞尔曲线的话,可能得花些时间整理下,其他的知识就比较简单了

b28ea359e3dca4061893f3e10ec1e87e.png

直角三角形,角A的对边a,临边b,斜边c

三角函数:

sinA=a/c

cosA=b/c

勾股定理:

c^2=a^2+b^2

概括介绍

这个效果难点就两部分:一是水球分离和融合时候的连接,二是主体圆的抖动

然而其实网上都有解决方案了

第一部分是在两个圆之间加个用贝塞尔曲线组成的path,用一样的颜色,其实是障眼法.见参考链接

第二部分是用4段三次贝塞尔曲线组成的path代替Ellipse,因为Ellipse是抖动不起来的,这样就可以控制贝塞尔曲线的点来让圆抖动.见参考链接

主体的大圆

Path画法 主体的大圆是个ToggleButton,替换模版,背景换成贝塞尔曲线组成的圆.

每个贝塞尔曲线的起点和终点就不说了,非常简单,这里主要说说控制点.

997172e68c2ff3ebf414229f16dd01ab.png

计算出1/4圆弧的中间位置的点,此时t=0.5, 三角型边长h=sin45*r

让控制点P1,P2分别在起点和终点的切线上,P1X轴的距离等于P2Y轴的距离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度,也就是最多只能放下8item,

968b7efc5d85c314ee2fa10dd5abfccd.png从上图可以看出来,其实就是奇数个在线上,偶数个在两线之间

有个简单的办法,就是先在顶点依次顺时针排列,每个item间隔45度,然后再逆时针旋转,每多一个item就多转45/2度(两条分割线是45度,中间也就是45/2度),如下图:

9d41ac8f9bca8f657fd144b034a11617.png上图是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

c04fa7227de47485ef2e1520da156060.png

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

ba01222b45a237832fe4246851518688.png上图红色矩形就是连接部分的path.动画的过程就是Item按钮的直径和大圆相交的时候开始和item按钮一起做动画,最后移动到Item按钮直径所在的位置,整个距离就是Item的半径+item到主体的距离+蓝色的d,而蓝色的d可以通过公式求出

开始的时候也是让连接部分path在圆心的位置.定位方法和定位Item按钮的方法是完全一样的.这里就不在重复了.只说一下c边的距离是:大圆和小圆圆心的距离-连接path高度的一半

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/286851.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

GetDisplayName 获取枚举的显示值

item.State.GetDisplayName(), 转载于:https://www.cnblogs.com/zhongku/p/4944315.html

组策略管理——软件限制策略(4)

编写软件限制规则 在前面几篇文章中讲了软件限制规则的基本概念&#xff0c;现在就来学习如何编写自定义软件限制策略。 编写规则应遵循的原则 首先&#xff0c;需要大家注意的是&#xff0c;软件限制策略应本着方便、安全、实用的原则来编写。限制规则灵活方便&#xff0c;自定…

我使用 html 反向输出自己打自己(7)

作者简介 作者名&#xff1a;1_bit 简介&#xff1a;CSDN博客专家&#xff0c;2020年博客之星TOP5&#xff0c;蓝桥签约作者。15-16年曾在网上直播&#xff0c;带领一批程序小白走上程序员之路。欢迎各位小白加我咨询我相关信息&#xff0c;迷茫的你会找到答案。 目录 HTML基…

甘肃省普通高等学校高职(专科)升本科考试英语科考试大纲(试行)

甘肃省普通高等学校高职&#xff08;专科&#xff09;升本科考试英语科考试大纲&#xff08;试行&#xff09; 一、考试目的 全面考核普通高等学校高职&#xff08;专科&#xff09;应届毕业生英语课程是否达到教学大纲所规定的目标&#xff08;领会式掌握3500单词&#xff0c…

256种编程语言大荟萃

本文是码农网原创翻译&#xff0c;转载请看清文末的转载要求&#xff0c;谢谢合作&#xff01; 双休日常常意味着很多休息时间。与其懒洋洋地坐在那里玩游戏&#xff0c;为何不学点新知识武装自己&#xff1f;本文中不会特定推荐哪种编程语言&#xff0c;但是会提供基于GitHub上…

java 获取系统当前时间

Calendar ca Calendar.getInstance(); int year ca.get(Calendar.YEAR);//获取年份 int monthca.get(Calendar.MONTH);//获取月份 int dayca.get(Calendar.DATE);//获取日 int minuteca.get(Calendar.MINUTE);//分 int hourca.get(Calendar.HOUR)…

Android之最简单的遍历某个目录下的所有文件(递归)

1、问题 遍历某个目录下的所有问题文件 2、代码实现 fun getRecoverTrashFile(path: String) {if (TextUtils.isEmpty(path))returntry {var file File(path)if (file null || !file.exists()) {return}var files file.listFiles()if (files null || files.size < 0) {…

Castle.DynamicProxy拦截器

在asp.net mvc或asp.net miniapi中&#xff0c;有过滤器&#xff0c;可以在请求前或后增加一层&#xff0c;达到验证&#xff0c;过滤等作用&#xff0c;如果在Service的方法前后加一层呢&#xff1f;这里介绍一下Castle.DynamicProxy的用法。首先引入Castle.Core实现代码相对轻…

甘肃省普通高等学校高职(专科)升本科考试计算机科考试大纲(试行)

甘肃省普通高等学校高职&#xff08;专科&#xff09;升本科考试计算机科考试大纲&#xff08;试行&#xff09; 一、考试目的及要求 全面考核普通高等学校高职&#xff08;专科&#xff09;应届毕业生计算机应用能力是否达到教学大纲所规定的要求。所有考生计算机基础知识必须…

Android选项切换条SHSegmentControl

&#xfeff;&#xfeff;Android选项切换条SHSegmentControl SHSegmentControl是github上一个开源的选项切换条&#xff0c;其样式如图所示&#xff1a; SHSegmentControl在github上的项目主页地址&#xff1a;https://github.com/7heaven/SHSegmentControl SHSegmentControl…

从零开始编写自己的C#框架(14)——T4模板在逻辑层中的应用(三)

原本关于T4模板原想分5个章节详细解说的&#xff0c;不过因为最近比较忙&#xff0c;也不想将整个系列时间拉得太长&#xff0c;所以就将它们整合在一块了&#xff0c;可能会有很多细节没有讲到&#xff0c;希望大家自己对着代码与模板去研究。 本章代码量会比较大&#xff0c;…

赶紧3分钟学完15分钟的内容我要出去玩(8)

作者简介 作者名&#xff1a;1_bit 简介&#xff1a;CSDN博客专家&#xff0c;2020年博客之星TOP5&#xff0c;蓝桥签约作者。15-16年曾在网上直播&#xff0c;带领一批程序小白走上程序员之路。欢迎各位小白加我咨询我相关信息&#xff0c;迷茫的你会找到答案。 目录 HTML基…

Android之获取到音视频的时长后按格式(00:00或者00:00:00)显示

1 需求 我们获取到了本地视频时长(秒为单位),然后需要按照如下格式显示 没有到小时的时长如下格式 00:00 有到小时的时长如下格式 00:00:00 2 代码实现 /*** 可以显示小时*/fun getDateStr(ms: Long): String? {val ss = 1val mi = ss * 60val hh = mi * 60val dd = …

Hello Playwright:(5)查找元素

操作浏览器归根到底就是和页面进行交互&#xff0c;那么必不可少的操作就是查找页面上的元素。因此我们需要熟练掌握Locator 定位器。在上一节我们讲过&#xff0c;可以使用Page.Locator(selector, options)方法创建定位器&#xff0c;而如何定位到元素则取决于selector 选择器…

RxSwift 之官方文档

RxSwift 官方文档结构 Introduction:SubjectsTransforming ObservablesFiltering ObservablesCombining ObservablesError Handing OperatorsObservable Utility OperatorsConditional and Boolean OperatorsMathematical and Aggregate OperatorsConnectable Observable Opera…

SQL一键备份用户数据库

大家都知道&#xff0c;Ms Sql 有自动备份的功能&#xff0c;但如果由于某种原因不能自动备份&#xff0c;或者我们想手动备份的话&#xff0c;就可以用下边的sql语句来执行备份。 --------------------代码开始------------- USE [master]------删除旧数据-------------------…

2019年甘肃省普通高等学校高职(专科)升本科考试招生工作实施办法

2019年甘肃省普通高等学校高职&#xff08;专科&#xff09;升本科考试招生工作实施办法 2019年甘肃省普通高等学校高职&#xff08;专科&#xff09;升本科考试招生工作实施办法 根据教育部有关规定及要求&#xff0c;结合我省实际&#xff0c;为确保普通高等学校高职&#x…

HTML基础之bit哥的反客为主之道(9)

作者简介 作者名&#xff1a;1_bit 简介&#xff1a;CSDN博客专家&#xff0c;2020年博客之星TOP5&#xff0c;蓝桥签约作者。15-16年曾在网上直播&#xff0c;带领一批程序小白走上程序员之路。欢迎各位小白加我咨询我相关信息&#xff0c;迷茫的你会找到答案。 目录 HTML基…

Atom编辑Markdown文件保存后行尾的空格自动消失的问题解决

Markdown文件的行尾增加两个空格表示一行结束需要换行。 但保存文件后&#xff0c;行尾的空格自动消失&#xff0c;导致不换行。 解决方法&#xff1a; 1、【Edit】->【Preferences】->【Packages】->【whitespace】->【Settings】->【Keep Markdown Line Brea…

将Abp移植进.NET MAUI项目

前言写在.NET MAUI官宣正式发布之际&#xff0c;热烈庆祝MAUI正式发布&#xff01;去年12月份做了MAUI混合开发框架的调研&#xff0c;想起来文章里给自己挖了个坑&#xff0c;要教大家如何把Abp移植进Maui项目。熟悉Abp的同学都知道&#xff0c;Abp 是一套强大的应用程序设计时…