Android开源:GADownloading - 一个绚丽的下载动效分析与实现
MaritaSoe
8年前
<p style="text-align: center;"><img src="https://simg.open-open.com/show/0e3cd3134bbf5199ba366c926d11de7c.png"></p> <p>闲逛之余,看到一个不错的downloading动效,这个动效用CJJ的话说难度还好,但本人觉得还比较灵动、带感、俏皮、有新意,好了话不多说,咱们先来撸一张高清无码gif图:</p> <p><img src="https://simg.open-open.com/show/d2e53b509f859685de623719fa9b89db.gif"></p> <p>撸完,咱可以将整个动效简单划分为以下流程:</p> <ol> <li>BeforeProgress(显示进度前);</li> <li>InProgress(显示进度中);<br> 3.Failed(失败动画);<br> 4.Done(完成动画);<br> 下面咱们一起对以上流程进行分析与实现;</li> </ol> <h2>1. BeforeProgress(显示进度前):</h2> <p>同样,咱们一起撸一下第一部分高清无码gif图:</p> <p><img src="https://simg.open-open.com/show/7353466f7dab816a4bf428f888daf304.gif"></p> <p>通过观察,我们可以将以上动画分割为以下几个内容:</p> <p>1.圆形背景和下载剪头整体缩放;</p> <p>2.圆形背景逐步镂空(缩放到一定阶段,内部镂空圆不断扩大);</p> <p>3.圆形背景变为一条直线,并伴随箭头些许上移;</p> <p>4.直线上下震荡及下载箭头(Arrow)变承载进度文字的线框形态;</p> <h3>1.1. 圆形背景和下载剪头整体缩放:</h3> <p>这里面,圆形背景和整体的缩放好说,稍显麻烦的是下载箭头,由于后面箭头还需要形变为承载进度文字的线框,所以丢掉你使用图片的小想法,咱们一起用path勾一个活泼的小箭头:</p> <p><img src="https://simg.open-open.com/show/52aa7d28cf19153eecbd5c40cd5c31ad.png"></p> <p>Paste_Image.png</p> <p>箭头OK了,圆形背景和整体的缩放就不再细说,只需要canvas.drawCircle()和使用ValueAnimator动态改变canvas缩放比例即可,so easy!</p> <p>后面箭头需要形变为承载进度文字的线框,通过观察,可以看到线框的4个角是圆角。由于使用path勾勒,实现圆角线框大致有以下几种方案:</p> <p>1.使用path的quadTo()以二次贝塞尔曲线连接;</p> <p>2.使用path的arcTo()以圆弧形式连接;</p> <p>3.使用path中addArc()添加一段圆;</p> <p>4.使用paint的setPathEffect设置PathEffect为ConnerPathEffect;</p> <p>本人最终采用第四种方式进行实现;</p> <p><img src="https://simg.open-open.com/show/277df1bbb3fb532e6ffdd67356e8e703.png"></p> <h3>1.2.圆形背景逐步镂空(缩放到一定阶段,内部镂空圆不断扩大):</h3> <p><img src="https://simg.open-open.com/show/5655f4c93522881a27f0c100a088aa68.gif"></p> <p>撸完上图,我们可看到,圆形背景由实心圆变换至一个圆环,最终消失,此处我们可以想到如下方案:</p> <ol> <li>直接采用背景的颜色,在里面画实心圆(需要提前知道背景颜色并且背景只能为纯色);</li> <li>外面深色的圆直接是圆环,然后通过调整圆的半径及paint的strokeWidth实现;<br> 3.直接采用混合模式(Xfermode),圆形背景中混合掉内圆部分;<br> 第一种方案太挫,帅气逼人的GAStudio哥肯定不会考虑,本文采用混合模式方案,关键代码如下:</li> </ol> <p><img src="https://simg.open-open.com/show/ebc53e7ad76df975561db888f0d88cef.png"></p> <p>Paste_Image.png</p> <h3>1.3.圆形背景变为一条直线,并伴随箭头些许上移:</h3> <p><img src="https://simg.open-open.com/show/32176fc279e2ed42391a83a5c85fea78.gif"></p> <p>这个部分相比前面两步稍显复杂,需要将圆环如丝般顺滑的变换成直线,并随之上线震荡,该过程拆解图如下:</p> <p><img src="https://simg.open-open.com/show/43c13a040f1fb4ecdb903c757eb6ffed.png"></p> <p>对于这个过程,GAStudio哥采用两条三阶贝塞尔曲线对初期的圆环、中间部分的曲线、最终的直线进行模拟;</p> <p>为了便于理解,抽象出四个核心状态,过程图解如下:</p> <p>1.完整圆形状态:</p> <p><img src="https://simg.open-open.com/show/52109abbc508210ba00dbf157d34d6dc.jpg"></p> <p>2.延展开来状态:</p> <p><img src="https://simg.open-open.com/show/a73a897edb2337292f4add9321e82163.jpg"></p> <p>3.横向铺开状态:</p> <p><img src="https://simg.open-open.com/show/33fd17bbc6183b512040ad313d6cc0a3.jpg"></p> <p>4.直线状态:</p> <p><img src="https://simg.open-open.com/show/fb1e6b10b19d1a7d50569a292ec0ea54.jpg"></p> <p>更新path核心逻辑如下:</p> <p><img src="https://simg.open-open.com/show/d5d90e1ed19d5f9e0a265eea9cf72c28.png"></p> <p>Paste_Image.png</p> <p><img src="https://simg.open-open.com/show/d8b1f5943df982319c875f61cd498262.png"></p> <p>Paste_Image.png</p> <p><img src="https://simg.open-open.com/show/8d76f8350f7dcf3b8367f60b6eedc022.png"></p> <p>Paste_Image.png</p> <p>整个过程路径及控制点变化如下:</p> <p><img src="https://simg.open-open.com/show/b59b47c4e91edb45cb704f0834b08ae7.gif"></p> <p>至此,箭头上移的效果,只需根据绳子中心点的位置,平移下载箭头位置即可;</p> <h3>1.4.直线上下震荡及下载箭头(Arrow)变承载进度文字的线框形态:</h3> <p><img src="https://simg.open-open.com/show/d4880191c1f2b611f4346b2b26cd32ec.gif"></p> <p>这个过程有以下三点需要考虑:</p> <p>1.4.1.直线震荡:</p> <p>该效果仅需持续上下移动二阶贝塞尔曲线的控制点即可,不再多言;</p> <p>1.4.2.箭头沿曲线移动:</p> <p>移动的路线可以采用一个三阶贝塞尔曲线进行模拟,再使用PathMeasure获取过程中的实时位置(x、y),关键代码如下:</p> <p><img src="https://simg.open-open.com/show/578cf5949b0a0ac2b38d1404d914ac7e.png"></p> <p>Paste_Image.png</p> <p>1.4.3.移动过程中的下载箭头形态变换:</p> <p>咱们用rectWidth、rectHeight分别指代下载箭头底部的矩形部分的宽高,triWidth、triHeight分别指代Arrow头部的三角形部分的宽高,angle指代下载箭头的旋转角度;</p> <p>只需用ValueAnimator创建一个过程将以上数值进行如下变换:</p> <p>rectWidth 到 2rectWidth;</p> <p>rectHeight 到 1.4rectHeight 再到 rectHeight;</p> <p>triWidth 到 0.65triWidth;</p> <p>triHeight 到 0.65*triHeight;</p> <p>angle 由 0 -> -30 -> 20 -> -10 -> 0度;</p> <p>OK,到这里,第一部分就可以告一段落,咱们继续看后面的部分;</p> <p>2. InProgress(显示进度中) :</p> <p><img src="https://simg.open-open.com/show/fa8681dbde5b92fb30098ca9cfa5228d.gif"></p> <p>GAStudio哥本次在实现过程中,没有实现在移动的过程中的线框的摇摆,有兴趣的同学可以自己修改实现,剩余部分主要讲下拉绳的变动:</p> <h2>2.1. 拉绳的变动:</h2> <p><img src="https://simg.open-open.com/show/d840dd6d8f1ac0f5881df5fc3212bea4.png"></p> <p>观察上图,可以将拉绳下拉的顶点移动的轨迹近似看成一条折线, 先计算出顶点的位置,再分别绘制左、右两边的直线,关键代码如下:</p> <p><img src="https://simg.open-open.com/show/ef3de3b32ba7cf8a3c1fdae414e3d21f.png"></p> <p>Paste_Image.png</p> <h2>3. Failed(失败动画):</h2> <p><img src="https://simg.open-open.com/show/56c0cdcad0f8fb985d116e87cb535537.gif"></p> <p>撸完以上gif,我们可以把这部分效果分为如下几点:</p> <p>1.线框内的文字变为Failed并且晃动;</p> <p>2.绳子上下抖动;</p> <p>3.绳子左侧的白色部分爆炸消失;</p> <p>4.线框回到最初位置,变且变为下载箭头;</p> <p>5.圆形背景逐渐放大出现;</p> <p>6.圆形背景和下载箭头整体缩放;</p> <p>在这里,我们一起看下爆炸效果的实现,其他部分相对简单,不再赘述;</p> <p>关于爆炸效果,我们可以很逼真的模拟,绘制出各式各样的圆点来模拟,但是由于点的个数多,大小不一,采用该方式费事费力,并且由于效果速度快,转瞬即逝,我们可以采用一种简单而效果看起来差不多的方式,就是只画几个形状,然后平铺到整个绳子;</p> <p>该处主要使用paint的setPathEffect方法将PathEffect设置为PathDashPathEffect,关键代码如下:</p> <p><img src="https://simg.open-open.com/show/3f43d97fce43a5fcde9b5cf0393cbb8c.png"></p> <p>Paste_Image.png</p> <h2>4.Done(完成动画):</h2> <p><img src="https://simg.open-open.com/show/fb6f8b544e8264956076e126d3a764d9.gif"></p> <p>撸完以上gif, 我们可以将该部分概括为以下部分:</p> <ol> <li> <p>线框绕Y轴旋转,并由100%变换为done;</p> <p>2.线框随进度条收缩到最中心;</p> <p>3.线框在中心点晃动;</p> <p>4.线框变换为下载箭头,圆形背景复出;</p> <p>5.圆形背景和下载箭头整体缩放,伴随下载箭头上下晃动;</p> </li> </ol> <p>该部分咱们一起看下第一条的实现,即Canvas里如何实现伪三维变换;</p> <p>Canvas中只有rotate函数,也就是在二维平面内进行旋转,不能实现如上的绕Y轴旋转,类似效果需要借助Camera来实现,关键代码如下:</p> <p><img src="https://simg.open-open.com/show/3bfd30f6c3fc4df20ab4060f49e5592e.png"></p> <p>Paste_Image.png</p> <p>至此,该效果的核心逻辑咱们已经分析完毕,实现效果如下:</p> <p>成功部分:</p> <p><img src="https://simg.open-open.com/show/9f1cf29db84372fce238cf42d4ffe4e7.gif"></p> <p>失败部分:</p> <p><img src="https://simg.open-open.com/show/1481a528e22275cd8858397d085e3617.gif"></p> <p>你以为到这里就结束了吗?No-No-No,作为一个负责任的开发者,最后咱们加上合理的自定义属性,以方便使用者自行定义:</p> <p><img src="https://simg.open-open.com/show/bc0d648f8c2cdb35351fd5b85581063b.png"></p> <p> </p>