【算法分析】QQ“一键退朝”之详细计算方法

版权声明:本文为博主原创文章,转载请标明原文地址以及作者(阿曌)。 https://blog.csdn.net/XieYupeng520/article/details/50374561

相信很多朋友和我一样很喜欢QQ上“一键退朝”的功能,就是把红点从它原本的地方拉走,消息提醒也就没有了。

这里写图片描述

直到如今我还是觉得这个功能很酷炫!于是想自己实现一番,经过一番调查知道拉伸其实就是由两个圆加上两条贝塞尔曲线组成的形状。

来看看腾讯设计师是怎么设计出来的吧:《QQ手机版 5.0“一键下班”设计小结》

看完了这个对实现思路有很大的帮助,可是我还是不能知道具体是怎么计算实现的,网上大部分的教程都是假想成了两个同样大小的圆来计算,这太取巧了!因为同样大小的圆两条外公切线是平行的,同一个圆上的公切点相连是会垂直于连心线的,但是大小不同的圆并没有这个特殊性!

另外网上也有很多仿照的项目,可是看算法看得头都大了也不明白为什么是这样算的!经过两天的研究,把初中数学(圆、三角函数等相关知识)好好复习了一遍,终于搞清楚了其中算法,现在跟我一起来看看吧!


1.得到连心线

通过观察可以发现,在“一键退朝”这个功能当中,有一个小圆固定在原来坐标位置不动的,只是半径会发生变化,另一个大圆是跟随着我们手指滑动到屏幕的位置来确定圆心坐标的,一般大圆的半径是固定的。

建立两圆的相对坐标系:

这里写图片描述

PS:在移动端的坐标系 y 轴是向下的。

假设某一个时刻,两圆的状态如图,我们现在可以确定的是小圆的圆心坐标 O 为(startXstartY),大圆的圆心坐标 P0 为 (x0, y0),以及小圆的半径 r 和大圆的半径 R

那么首先可以把连心线求出来!也就是 OP0 的距离。

线d=(x0startX)2+(y0startY)2

2.求切点坐标

复习一下初中数学:

两个外离的圆,一定有两条外公切线。若两圆半径相同,则两外公切线平行;否则相交于一点,且该点与两圆心在同一直线。

我们再作一张有公切线的图:

这里写图片描述

切点为 P1P2P3P4,我们现在目的就要求出这四个点,然后就能够在程序中画出切线。

整个算法最难的地方恐怕就是求这四个点了,我们需要借助作图来帮助计算,这之前还需要先复习下定理:

圆心和切点的连线一定垂直于过该点的公切线

首先过小圆圆心 O 作一条平行于 P1P2 的线,和 P0P2 相交于点 M ,可以得到 P1OMP2 为一个矩形,则 P2M=P1O=r ,又因为 P2P0=R ,所以 P0M=Rr ,那么

sinγ=sinMOP0=MP0OP0=Rrd

γ=MOP0=arcsinRrd

作连心线和 x 轴的夹角为 α

tanα=startYy0x0startX

α=arctanstartYy0x0startX

再作几个辅助点 ABCDAB 表示以大圆圆心为原点的坐标系的 x 轴的两端,CD 表示以小圆圆心为原点的坐标系的 x 轴的两端,

这里写图片描述

AB//CDP0OD=α

AP0O=α

P1OC=βP1O//P2P0

P2P0A=P1OC=β

OMP2P0OMP0=90°π2

β=π2γα

现在 β 求出来了,两圆的半径也已知,求 P1P2 的坐标是不是很简单?

P1.x=startXcosβrP1.y=startYsinβr

P2.x=x0cosβRP1.y=y0sinβR

3.求剩下两个切点的坐标

一开始我以为 P3P4 的算法和 P1P2 一样,就是把上面的减号换成加号就可以了。可是后来验证后发现不对, P3P4 不能直接使用 β 进行运算。

这里写图片描述

为了能愉快阅读,再来复习一下各种拉丁希腊符号叫法:
α 阿尔法 β 贝塔 γ 伽玛 δ 德尔塔 ε 伊普西隆 ζ 泽塔

如上图作辅助线。

P0ON=γ

δ+α=90°π2

δ=π2α

ONP0P4

γ+δ+ε=π2ε=π2γδ

ε+ζ=π2

ζ=π2ε=π2π2γδ=π2π2γπ2α

ζ=π2+γα

在前面 γα 已经求出来了!那么 P4 坐标也就不难了。

P3.x=startX+cosζrP3.y=startY+sinζr

P4.x=x0+cosζRP4.y=y0+sinζR

4.画贝塞尔曲线

把四个切点坐标求出来了,后面就简单了,现在就是以切线为原轴,画贝塞尔曲线了,不过我们还缺少一个控制点的坐标。

4.1 科普贝塞尔

怕有不清楚贝塞尔曲线的朋友,我科普一下先,简单来说就是求一段平滑曲线的公式。

如果我们把画一条直线分为进度100%的话,那么当进度为0%,12%,58%,74%时,画线的状态为(注意红色部分末的黑色端点,灰色部分为路径指示)

0125874

那么把所有时刻的黑点连接起来就构成了直线:

这里写图片描述

这个概念应该比较容易接受,好了继续。

二次贝塞尔曲线(最简单的贝塞尔曲线)的作法首先需要两个点确定一条直线,另外在直线外确定一点(即控制点),然后此时三点会形成三个线段,即下图的P0P2P0P1P1P2(其实不用关注 P0P2

这里写图片描述

这只是进度为0时候的状态,按照上面概念,当进度 t 从 0 变化到 100 时的某一个时刻,比如 30, 66 ,99,那么各个时刻P0P1P1P2的状态为

这里写图片描述这里写图片描述这里写图片描述

可以发现,在P0P1P1P2上有一直运动的两个点,我们将这两个点连接起来又形成一段新的线段,而在不同时刻,在这个新线段上同样会有一个运动的点,这个点也遵守 t 的变化。

这里写图片描述这里写图片描述这里写图片描述

把所有时刻的黄色点连接起来,就形成了二阶贝塞尔曲线。

这里写图片描述

还不能理解的可以看下这个视频 - > 《bezier curve原理》 只要看就好,听不懂英文的可以把声音关掉。

费这么大劲把二阶贝塞尔讲了一遍,我们这里其实也只用到了二阶,高阶我就不讲了,一通百通。

4.2.寻找控制点

那么现在线段已经能确定了,就是两条公切线线段(P1P2P3P4),那么控制点在哪呢?

这个其实有点靠猜了=。= 一开始我觉得应该在连心线的中点,其实实现后效果也还行,后来参照腾讯设计师的想法效果更好,他令 P1P2 的控制点为 P1P4 的中点,令P3P4 的控制点为 P2P3 的中点。

这里写图片描述

软件实现效果对比(左边控制点是连心线的中点,右边是腾讯设计师提出的控制点):

这里写图片描述 这里写图片描述

我个人觉得右边效果更好,也不得不佩服TX设计师的聪明才智,让我自己想可能永远也想不到。

至于求 P1P4P2P3 的中点不难吧?连四个坐标点都求出来了,直接算就可以了!


源码地址:https://github.com/Xieyupeng520/AZMetaBall(还会不断完善的,求星星^3^)

这里写图片描述


References:

《QQ手机版 5.0“一键下班”设计小结》

《【Android开源项目解析】QQ“一键下班”功能实现解析——学习Path及贝塞尔曲线的基本使用》

Github - MetaballLoading

本教程为了方便讲解有篡改原图,还望原图作者见谅!

阅读更多

扫码向博主提问

阿曌

请开始你的问题
  • 擅长领域:
  • iOS
去开通我的Chat快问
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页