美文网首页IOS数码舵手iOS开发
【算法分析】QQ“一键退朝”之详细计算方法

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

作者: 阿曌 | 来源:发表于2015-12-23 16:01 被阅读4133次

    Tips:由于简书好像不支持Latex公式,所以为了效果,有关公式部分截取了我发表在CSDN上的博文,地址:AZZ的博客


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

    一键退朝功能示意图

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

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

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

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


    1.得到连心线

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

    建立两圆的相对坐标系:

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

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

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

    2.求切点坐标

    复习一下初中数学:

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

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

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

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

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

    再作几个辅助点 A、B、C、D,AB 表示以大圆圆心为原点的坐标系的 x 轴的两端,CD 表示以小圆圆心为原点的坐标系的 x 轴的两端,

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

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

     为了能愉快阅读,再来复习一下各种拉丁希腊符号叫法:

     α 阿尔法 β 贝塔 γ 伽玛 δ 德尔塔 ε 伊普西隆 ζ 泽塔

    如上图作辅助线。

    4.画贝塞尔曲线

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

    4.1 科普贝塞尔

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

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

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

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

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

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

    t=30 t=66 t=99

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

    t=30 t=66 t=99

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

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


    bezier curve原理

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

    4.2.寻找控制点

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

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

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

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

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

    ---

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

    ---

    References:

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

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

    [Github - MetaballLoading]

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

    相关文章

      网友评论

      • 不会算卦的杨大仙:但是这样用画板实现的不能超出底部的table区域啊
        阿曌:啥意思?
      • 韩大熊宝要姓张:不错, 加油哦.
      • chuanwei2050:P1.x=startX−cosβ*r,P1.y=startY−sinβ*r //P1.x为startX−cosβ*r,为啥不是startX+cosβ*r ? P1.y=startY−sinβ*r,为啥不是startY+sinβ*r
        P3.x=startX+cosζ*r,P3.y=startY+sinζ*r //这里的为什么又是相加,为什么不是相减呢?
        还请楼主帮忙解释一下吧?
      • chuanwei2050:P1.x=startX−cosβ*r,P1.y=startY−sinβ*r //P1.x为startX−cosβ*r,为啥不是startX+cosβ*r ? P1.y=startY−sinβ*r,为啥不是startY+sinβ*r
        P3.x=startX+cosζ*r,P3.y=startY+sinζ*r //下面的为什么又是加起来,为什么不是相减呢?
        还请楼主帮忙解释一下吧?:pray:
        chuanwei2050: @阿曌 上面说的坐标大小,已经明白了。就是上面的计算方式,最后那个切点坐标是按公式计算出来的吗?
        chuanwei2050: @阿曌 楼主好,感谢回答了问题。这里还2个疑问四个切点坐标是最后的那步计算是有公式计算出来的吗?
        阿曌:因为在手机上,坐标系是“↓→”这样的,左上角是坐标系原点,右下角是坐标系最大点。所以原点O的startX和startY都是比p1的x,y大的,这个能理解吗?同理p3的坐标比O大。
      • JOJO_YH:你好,请问有安卓的源码吗?
        http://www.jianshu.com/p/dce9794ed07e ,这一篇里面的是match_parent然后设置透明背景来的,那要怎么在item里面设置呢?初学,不懂,谢谢!!!
      • 0ae651a8854e:第一百个喜欢 :smile:
      • cc_Jumper:666666666
      • 81db1687fc6c:现在连冒泡排序都被困扰的我表示,你这篇文章在说啥?
        阿曌:@俊骜骐骥王者 冒泡排序也是我的困扰你信嘛_(:з」∠)_ 还有快速排序,二分法等等等都是什么鬼总是记不住
      • b6b104e00a80:我想知道示意图都是怎么做的~
        b6b104e00a80: @阿曌 ~~~好神奇~~
        阿曌:@Tacey_Wong 前面的图从设计师的文章里拖的,不晓得Ta用的什么软件~后面加的辅助线是我用PS做的_(:з」∠)_
      • afb4edaeda9d:大神收下我的膝盖
        阿曌:@譿芯 已收!
      • 画家与酒:服气(°ー°〃)
      • acb61e645904:学霸
        acb61e645904:@阿曌 😕😕😕😕
        阿曌:@苑Xs 隐藏这么深还是被发现了我的真实身份╰( ̄▽ ̄)╮
      • 37554709b211:牛气的哟
      • WeLMusic:好牛逼的样子。
      • b5380da04368::turtle:好厉害的样子
      • Sept丶Jours:还是QQ会玩 😂
      • 苏本的书柜:牛逼不解释
      • 斯德瑞伊尔:不仅可以一键消除,还能拖着玩
      • 8e6b06f97cfe:天,好厉害,原来“一键退朝”有这么多算法,我一直以为它只是简单的设计了一个气泡而已,原谅我是学文的😓
      • 阿基拉:这个b给高分
      • 烛之影:编程这还得要数学好!!!
      • 止战止殇:数学改变生活 😀
      • af5d59e96234:每一个所谓酷炫的功能,即使只是很小的功能,都是我们竭尽全力的成果。技术,从来就没有理所当然。感谢我自己,在高考后周围人的不理解中选择了成为一名工科女生。希望工程师这个职业能让我拥有自己对社会真实的不可忽略的贡献!
        1b2ae550dc99:@风土山河 加油,这段话不错
        阿曌:@风土山河 共勉,同女程!~
      • FAIRY猫小狸:好厉害。。。
      • 彭小六:如果有人问我学编程为什么要学算法,我会给他看这篇文章
        13ea610725c6:@彭小六 别逗了。算法不是数学
      • 1aadd68f9ee0:第二节的第一条公式,后面一段是:= MP0/OM = (R-r)/d,错了吧,应该是 = MP0/OP0 = (R-r)/d 吧
        阿曌:@修电脑的 哈哈哈,是我错了!!!写错了!!!多谢提醒!马上改!
      • 1aadd68f9ee0:如果再有人问学编程是不是要数学成绩很好,就拿这个例子给他看好了
        阿曌:@左蓝 我也没想到学设计的居然数学还要好,细思极恐
        左蓝:@修电脑的 应该是学设计的 :smile:
        af5d59e96234:@修电脑的 哦我的天,这就是我们把数学专业基本学一遍的原因!
      • 裸奔的凯子哥:哈哈,其实我当时弄明白不相同圆的计算了,但是讲解起来会比较麻烦,就简化了计算模型,这篇文章确实不错,赞!

      本文标题:【算法分析】QQ“一键退朝”之详细计算方法

      本文链接:https://www.haomeiwen.com/subject/trdvhttx.html