贝塞尔风暴 - 超炫GABottleLoading效果

hp1227hp 7年前
   <p>谈到贝塞尔曲线,很多人会觉得高逼格、复杂、头疼,实则不然,贝塞尔曲线经过android封装,已经显得娇俏可爱,简单好用,之前一些红极一时的效果也均是由其打造,比如QQ的“一键退潮”效果、电子书曲面翻页效果...... 现在咱们就用贝塞尔曲线一起从0到1打造一个拥有极致体验、清秀灵动的GABottleLoading效果;</p>    <p>好了,不多吹NB了,老规矩先上一个原始效果图:</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/213283022b11dbe56ba5325c247138a4.gif"></p>    <p>看到这个效果,估计有人开喷:</p>    <p>“我擦,听你吹半天NB,这个效果老子两年前就看过了,github上早有了,垃圾......”</p>    <p>此时,沉稳优雅、帅气逼人的GA哥在github上通过关键字搜索,两个实现赫然出现在我的面前,看来天不助我,然后通过查看他们的实现,发现其中一个实现的很棒,老实说GA哥都没有信心实现到如此完美,but看过之后发现他是直接加载gif,fuck, too young, too simple, 另一个呢?通过代码实现,现在让我们瞻仰下他实现的效果:</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/931521c1affd7b0e2aedd4602710e83c.gif"></p>    <p>从效果上来看,基本实现了瓶身、波纹晃动,但真正复杂的气泡与动态并倾斜的水面的脱离过程却并未实现,而这才是GA哥感兴趣的地方,此时天空响起一声惊雷,GA哥闪亮登场;</p>    <p>首先,让我们一起分解下这个动效,简单来看,该效果可以分成以下几个部分:</p>    <p>1.水滴从水面弹出和融入;</p>    <p>2.水面的波动;</p>    <p>3.瓶身的绘制;</p>    <p>接下来,咱们一起从以上三个内容逐个处理:</p>    <h3>一、水滴从水面弹出和融入</h3>    <p>正式分析之前,请和GA哥一起通过一个慢镜头看看其中一个水滴脱离水面的过程;</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/24e580be10bcfe9420ab6c8aac7dbfe3.gif"></p>    <p>我擦,不看不知道,没想到过程如此细腻!</p>    <p>是不是突然感觉这个动效没那么简单了?是不是有点难度了?尤其是水滴离开水面的过程中,水面还在不断的变动,而整个水滴弹出和融入的过程都需要和水面柔和爽朗的连接;</p>    <p>俗话说,“擒贼先擒王”,咱们第一步就来搞定水滴的粘连出入过程!</p>    <p>首先我们来看一下过程分解图(请关注左边的水滴):</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/af453a23dd9ae3f020f13c7ef8b6b73f.png"></p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/92ba88d2cf9e545e30eed78bfe08505b.png"></p>    <p>由上图可以观察到,到(4)的时候,水滴已经完全脱离了水面,只不过存在一定的粘连(由于水的张力);</p>    <p>接下来我们分析下水滴脱离水面的过程,为了更好的说明,将水面简化为一个静态的斜面,这样更加直观;</p>    <p>那么原始模型如下图,其中斜线代表水面,圆代表水滴,是不是很简单?</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/c10a0ae154bba3fc2101fd705f71bc2f.png"></p>    <p>接下来我们需要考虑,如何处理水滴和水面的粘连效果,标题既然叫做贝塞尔曲线打造极致GABottleLoading效果,咱们肯定是使用贝塞尔曲线这一神器了,既然使用贝塞尔曲线,那么不用多说,就需要考虑起始点、终点、控制点这些核心数据;</p>    <p>从效果来看,起始点咱们可以直接从水滴上取,然而具体取到何处呢?咱们可以以水滴冒出水面的高度为基准,然后定义一个适当比例,以该比例计算具体数据点;</p>    <p>既然起始点在水滴上,那么终点毋庸置疑在水面上,具体取于何处呢?咱们可以采用如下方式(至于为何要这么取则是GA哥的思路):</p>    <p>使用一个矩形框框住水滴区域,使水滴距离左右两边 L1、L2,并且L1 == L2 ;</p>    <p>此时矩形框与水面形成交点w1、w2,咱们可以直接选取如图所示w1、w2作为两个终点,这两个点即表示水滴由于张力而形成的拖尾和水面的接触点;</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/eb7bd25b1292f57a29768a27105ea008.png"></p>    <p>经过以上思路,咱们画出以下图示:</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/bb442479e0b93ed99bace65b5db7bbca.png"></p>    <p>看上面这张图,其中L3为水面上点w1和w2的连接线,L4为经过圆心并且和L3垂直的直线,wd为L3与L4的交点,,Ct为圆最顶端数据点,C1、C2为垂直于L4的L5与圆环的交点;</p>    <p>咱们将辅助线都去掉,那么就得到如下的图:</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/1d9ed0d4d6569b824ce53662b0243e58.png"></p>    <p>在图上,C1、C2为起始点,W1、W2为终点;</p>    <p>好了,起始点、终点咱们定下了确定的方法,控制点呢?</p>    <p>且继续看下图;</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/93f46f0ccfd866fb2b5441c4ab0b82f0.png"></p>    <p>图中L6、L9分别为点w1及w2所在的水面的切线,L7、L8分别为C1及C2处的切线,q1为L6与L7的交点、q2为L8与L9的交点,q1与q2则为咱们找的两个控制点;</p>    <p>到此,包括起始点、终点、控制点在内的贝塞尔曲线所需的核心数据咱们就都找到了,如下图所示的6个点;</p>    <p>6个点、6个点、6个点,重要的事情说三遍......</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/dea8ee18b036846e96b6d2d5afc524ed.png"></p>    <p>然后咱们利用以上6个点绘制两条二阶贝塞尔曲线,形成相应拖尾粘连效果,具体效果图如下:</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/2529a07b20f161cb364b35cdd53cff61.png"></p>    <p>我们把不需要的点去除,并填充上颜色,看看最后的效果:</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/66aa773484b2196568718c153982cee5.png"></p>    <p>那么这部分整体效果的结果如何?请看!</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/6ec4611ef7f9f485b7c1622bf500bdf4.gif"></p>    <p>这个不够直观?那让我们加上辅助点,请看!</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/c981507367acdcaa464fb39b125b74cb.gif"></p>    <ul>     <li>圆上的白色的点从左往右分别是c1、c2,分别表示拖尾与圆的接触点,即贝塞尔曲线的起始点;</li>     <li>圆两侧两侧红色的点从左往右为分别为w1、w2,表示拖尾与水面的接触点,即贝塞尔曲线的终点;</li>     <li>蓝色点从左往右分别为q1、q2,分别表示左右两侧的控制点;</li>    </ul>    <p>最后,圆完全脱离水面如下图:</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/02707c0128a178ce5e5f6415659a44ca.png"></p>    <p>圆拖着拖尾上移:</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/5f08b238f248dd181d7dc5739d6f2e2c.png"></p>    <p>最后拖尾断裂:</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/7f5a409e9cc32b7178a02eef11557553.png"></p>    <p>最后水滴完全脱离,水面恢复平静:</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/4fa282d4cae8305af0a6ebfaa639306f.png"></p>    <p>让我们一起来看看整个过程:</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/24fa1b4044daa85690ea693f948524b4.gif"></p>    <p>ok,到此,水滴从水面弹出和融入的思路分析就此结束;</p>    <h3>二、水面的波动分析</h3>    <p>同样,咱们先看下原始效果图:</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/213283022b11dbe56ba5325c247138a4.gif" alt="贝塞尔风暴 - 超炫GABottleLoading效果" width="300" height="240"></p>    <p>github上已实现效果图:</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/931521c1affd7b0e2aedd4602710e83c.gif" alt="贝塞尔风暴 - 超炫GABottleLoading效果" width="258" height="201"></p>    <p>没有对比就没有伤害,看起来是不是觉得原始效果图要柔和自然很多?那么到底是什么原因呢?GA哥分析主要是以下两点原因:</p>    <ul>     <li>1.波动效果,原效果图是一个减速过程,当水波达到最高点的时候速度变为了0,而对比图是一直匀速的过程;</li>     <li>2.上面流动的水和下面静止的水的连接处理有差异;</li>    </ul>    <p>当然,以GA哥的尿性肯定是以原效果图为目标,而当GA哥在PS中采用三阶贝塞尔曲线去拟合的时候,发现还是存在一定的瑕疵,不能完全的拟合上;</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/d49ef0d82bd69c3081664ccc78fa0ea5.png"></p>    <p>最终,最右边的连接弧度采用上图所示数据作为参数,实现的效果如下:</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/8980cea19c8560fa88d85dbbb25d9546.png"></p>    <p>我擦,连接处不够柔顺,此时GA哥采用了以下处理方案;</p>    <p>将波动的水面抬高,和底部静止的水面保持一定的距离,然后采用二阶贝塞尔曲线将两者的连接处进行连接:</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/e9929251369f77b92e38d6caf7b09739.png"></p>    <p>恩,上图的效果还是可以接受的,让我们看看动起来什么效果:</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/264ca79a18699aa8a956e0b76997240f.gif"></p>    <ul>     <li>作者注:恩,此时如GA哥的秀发般飘逸了! - GAStudio哥</li>    </ul>    <h3>三、瓶身的绘制</h3>    <p>瓶身绘制就是一个字——"扣",扣细节,然后达到各个接触点比较完美的连接;</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/e9cc3fd3d89d633aa31ecd429f95e4db.png" alt="贝塞尔风暴 - 超炫GABottleLoading效果" width="200" height="280"></p>    <p>让我们从左半部分的上方开始讲起。</p>    <p>瓶口处的的弯角,一开始GA哥也是认为是一个半圆(180度),然后再连接直线。然而这样做效果不是很好,所以GA哥采用四分之一圆环(90度),然后连接45度的直线,最后连接垂直的瓶嘴直线,效果如下图:</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/f442f672d4aeecddfeb45c3fe2588372.png"></p>    <ul>     <li>路人甲:纳尼?怎么有这么明显的棱角?作为细致的GA哥,依旧没有放过这个细节。</li>     <li>路人乙:敢问GA哥有什么有效的方案?使用Paint.Cap.ROUND?</li>     <li>GA哥:No, No, No, Paint.Cap.ROUND 只对line的头部有效,GA哥采用了将Paint的PathEffect设置成了CornerPathEffect,那么就会将直线和直线间的连接处,自动做了圆滑处理。</li>    </ul>    <p>那么效果如何呢?请看下图:</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/59ce899c9220ae17856756551aa27091.png"></p>    <p>卧槽,好帅,好柔滑有木有!</p>    <p>此外, <strong>瓶身连接处</strong> (如下图红色标注处)也是需要注意的,要么计算准确,完美的连接;要么索性有一点缺口,采用arcTo绘制瓶身,此时缺口会自动连接上直线,再加上之前配置的CornerPathEffect,就会使得该处显得自然:</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/6cc247beb39ba0201297601a236bf615.png"></p>    <p>接着 <strong>瓶身</strong> 采用圆弧绘制,从多少到多少角度都需要进行计算;</p>    <p>最后 <strong>底部</strong> 采用直线直接连接;</p>    <ul>     <li>GA哥: 恩,你们是不是觉得GA哥会说,右半部分和左半部分采用一样的思路?</li>     <li>路人甲: 难道不是吗?难道GA哥有啥高招?</li>     <li>GA哥: 恩,GA哥这个人比较懒,所以什么事都想偷懒;</li>    </ul>    <p>恩,我们都不用仔细观察,这个瓶子是 <strong>左右对称</strong> 的!所以直接反转过来就行了哇!</p>    <p>关键代码如下:</p>    <p>// Generate the right path Camera camera = new Camera(); Matrix matrix = new Matrix(); camera.save(); camera.rotateY(HALF_FULL_ANGLE); camera.getMatrix(matrix); camera.restore(); matrix.preTranslate(-mViewRectF.centerX(), -mViewRectF.centerY()); matrix.postTranslate(mViewRectF.centerX(), mViewRectF.centerY()); Path rightBottlePath = new Path(); rightBottlePath.addPath(mBottlePath); mBottlePath.addPath(rightBottlePath, matrix);</p>    <h3>总结</h3>    <ul>     <li>GA哥:总结?总结啥呢?</li>     <li>路人甲:看看效果呗,看你吹了这么久,哈哈!</li>     <li>路人乙:赞同楼上的;</li>     <li>路人丙:赞同楼上的;</li>     <li>路人丁:赞同楼上的;</li>     <li>...</li>     <li>GA哥:盛情难却哇,那就献丑了!</li>    </ul>    <p style="text-align:center"><img src="https://simg.open-open.com/show/71a3b5117dc1a0fe953fef28919a6e80.gif"></p>    <p>最后,附上GAStudio技术交流群和Github,喜欢的话欢迎follow和star:</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/c0484cb3f1b61eac313532f60a27f924.png"></p>    <p> </p>    <p>来自:http://www.jianshu.com/p/93b0d948abf8</p>    <p> </p>