Android粒子破碎效果(1)——开源项目ExplosionField代码分析
来源:互联网 发布:上海unity3d培训 编辑:程序博客网 时间:2024/04/29 13:57
使用过MIUI的同学应该遇到过MIUI的app卸载动画,作为多年的米粉,当我尝试去实现这个动画的时候,第一时间就是在网上看有没有类似的效果,果然我找到了这个:
【Android效果集】学习ExplosionField之粒子破碎效果
可这个动画使用起来并不理想,其粒子在爆炸后,其运动方向左右摇摆,当我仔细阅读代码之后,发现其中 advance方法(即动画进行过程中,用于改变粒子参数的方法)如图:
可以看到,随着动画的进行,粒子的圆心x坐标,每次都会加一个随机正负的随机数;圆心的y坐标会加一个正随机数;因此粒子的左右移动是不确定的,这并不符合自然规律。
那么什么才是自然规律呢?
- 粒子在x轴上:爆炸的那一刻,就决定了是往左还是往右,之后只能朝着这个方向继续移动。
- 粒子的y轴上:可以看到MIUI的效果,是粒子先向上运动,然后下落。
于是,我又找了开源项目:
ExplosionField
该项目效果如图:
可以看到效果几乎与MIUI的效果相同,但是该项目没有一句注释,且其对粒子的参数进行的大量数学计算,因此我费了好大劲,终于像解方程一样,理清了开发者的思路。下面先分析该项目代码:
代码分析
使用方法:
实例化:
mExplosionField = ExplosionField.attach2Window(this);
给View添加爆炸效果:
mExplosionField.explode(view);
分析
该项目总共有四个类:
- ExplosionAnimator,继承自ValueAnimator,负责产生具有动画规律的数字,还有负责生成粒子、绘制粒子的方法。
- ExplosionField,继承自View,用于将动画生成的粒子绘制在界面上,包含执行动画、将自身添加到ContentView中的方法。
- Particle,粒子的实体类,同时也是ExplosionAnimator的内部类,包含粒子绘制的参数,以及最重要的粒子随着动画进程,改变自身参数的advance方法。
- Utils,工具类,包含dp转px、根据View创建Bitmap方法。
其思路流程不在赘述,了解过自定义View和属性动画的同学应该都能看的懂,这里贴两个思维导图(原谅我做的图太丑了 o(╥﹏╥)o):
我们重点来讲讲粒子的生成方法和变化方法:
首先是粒子的各项参数(加注释版):
private class Particle { float alpha; // 透明度 int color; // 颜色 float cx; // 粒子圆心 x float cy; // 粒子圆心 y float radius; // 粒子半径 float baseCx; // 粒子圆心 x的基础值,后续cx的取值就由baseCx为基准 float baseCy; // 粒子圆心 y的基础值,后续cy的取值就由baseCy为基准 float baseRadius; // 粒子的基础半径,后续radius的取值就由baseRadius为基准 float top; // 负责cy变化的因素 float bottom; // 负责cx变化的因素 float mag; // 负责cy变化的因素(因为是基于上面两个值计算而来,通过修改计算公式可以修改粒子变化幅度 float neg; // 同上 float life; // 决定了粒子在动画开始多久之后,开始显示 float overflow; // 决定了粒子动画结束前多少时间开始隐藏 }
当我刚开始看到一大堆bottom、top、mag等参数时,一脸懵逼,后来通过分析其粒子生成方法和粒子变化方法,才推测出这些参数的用处。
然后,我们来看看粒子生成方法 generateParticle(int color, Random random):
private Particle generateParticle(int color, Random random) { Particle particle = new Particle(); particle.color = color; particle.radius = V; if (random.nextFloat() < 0.2f) { particle.baseRadius = V + ((X - V) * random.nextFloat()); } else { particle.baseRadius = W + ((V - W) * random.nextFloat()); } float nextFloat = random.nextFloat(); particle.top = mBound.height() * ((0.18f * random.nextFloat()) + 0.2f); particle.top = nextFloat < 0.2f ? particle.top : particle.top + ((particle.top * 0.2f) * random.nextFloat()); particle.bottom = (mBound.height() * (random.nextFloat() - 0.5f)) * 1.8f; float f = nextFloat < 0.2f ? particle.bottom : nextFloat < 0.8f ? particle.bottom * 0.6f : particle.bottom * 0.3f; particle.bottom = f; particle.mag = 4.0f * particle.top / particle.bottom; particle.neg = (-particle.mag) / particle.bottom; f = mBound.centerX() + (Y * (random.nextFloat() - 0.5f)); particle.baseCx = f; particle.cx = f; f = mBound.centerY() + (Y * (random.nextFloat() - 0.5f)); particle.baseCy = f; particle.cy = f; particle.life = END_VALUE / 10 * random.nextFloat(); particle.overflow = 0.4f * random.nextFloat(); particle.alpha = 1f; return particle; }
恩…配合下面的思维导图食用更佳:
红色参数:粒子在生成时,就固定下来的参数,随着动画进程而不改变的值。
请注意绿色部分的正负取值
总之,上面的一系列计算,都是以为了让每一个粒子都有不一样的参数,以及后续在动画进程中不一样的运动轨迹。值得注意的是,上面的top和bottom在计算中,使用了同一个变量–nextFloat,因此bottom与top的规律在于:top越大,bottom的相对值就越小,反之亦然。表现在运动轨迹上,就是粒子横向运动的越远,竖直方向运动的就越近(相对来说).这里就不得不佩服开发者的细心了,这种规律都能考虑到 Orz。
我们继续来看粒子的变化方法 advance(float factor):
public void advance(float factor) { float f = 0f; float normalization = factor / END_VALUE; if (normalization < life || normalization > 1f - overflow) { alpha = 0f; return; } normalization = (normalization - life) / (1f - life - overflow); float f2 = normalization * END_VALUE; if (normalization >= 0.7f) { f = (normalization - 0.7f) / 0.3f; } alpha = 1f - f; f = bottom * f2; cx = baseCx + f; cy = (float) (baseCy - this.neg * Math.pow(f, 2.0)) - f * mag; radius = V + (baseRadius - V) * f2; }
添加注释后:
public void advance(float factor) { float f = 0f; // normal= 粒子在可显示的范围内,动画进行到了几分之几 float normalization = factor / END_VALUE; // 动画开始前和结束前的一段时间内是透明(不进行绘制)的。 if (normalization < life || normalization > 1f - overflow) { alpha = 0f; return; } // normal= 粒子在可显示的范围内,动画实际进行到了几分之几 normalization = (normalization - life) / (1f - life - overflow); // f2= 实际进行到的数值 float f2 = normalization * END_VALUE; // 动画实际进程超过7/10,则开始逐渐透明。 if (normalization >= 0.7f) { f = (normalization - 0.7f) / 0.3f; } alpha = 1f - f; // cx 在baseCx的基础上增长f2个bottom(bottom可能是负数,这里就表现了粒子是往左移动还是往右移动 f = bottom * f2; cx = baseCx + f; // 可以把这个计算视为一个方程,然后,我们一步步简化: // 已知:mag=4*top/bottom; neg=-mag / bottom; f=bottom*f2; // 则:cy = (float) (baseCy - this.neg * Math.pow(f, 2.0)) - f * mag; // 则:cy= (float)(baseCy-(-(4*top/bottom)/bottom)*bottom*bottom*f2*f2)-bottom*f2*4*top/bottom; // 则:cy= baseCy+(4*top*(f2*(f2-1))); // 那么,我们就可以的出cy的变化曲线函数: y=baseCy+4*top*(x*(x-1),再简化: y=j+k*(x*(x-1),j、k都是常数,x为 0~1.4; // 那么,粒子的变化因素只有一个x*(x-1) cy = (float) (baseCy - this.neg * Math.pow(f, 2.0)) - f * mag; // 可以简化为:y=k*x,k是常数,x为 0~1.4;因此radius是不断增长的。 radius = V + (baseRadius - V) * f2; }
注释里基本都写的很清楚了,关键是Cy的取值,我们可以看到,cy的变化因素为y=x*(x-1),那么,我们在函数曲线中看一下:
可以看到,y是先下降再上升,且当x小于1时,y是负值。动画的结束值是1.4,那么当动画进程在0.5之前时,baseCy是加一个不断变小的负值,表现到View坐标系中,则是粒子向上运动。之后,便是baseCy加一个不断增加的值,表现为粒子向下运动。
我们可以测试一下,先打印第一个粒子的baseCy和top值:
if(ttt==0){ tt=bottom; Log.d("ExplosionAnimator","baseCy="+baseCy+";top="+top); } else{ if(ttt==bottom){ Log.d("ExplosionAnimator","baseCy="+baseCy+";top="+top); } }
日志:
D/ExplosionAnimator: baseCy=299.99106;top=147.68047
我们将其应用到函数曲线中:
因为View坐标系y轴是向下的,与数学坐标系相反,我们可以修改一下方程,达到类似View坐标系的效果:
总结
代码分析的差不多了,我们基本上可以看出开发者的思路:粒子的生成的时候,通过大量的随机运算,给粒子赋予尽量区别于其他粒子的参数。
其中:
- cx,初始位置为view中心点左右随机偏移一定值,根据bottom值,又可以分为向左运动(bottom为负数)的粒子、向右运动(bottom为正数)的粒子;
- cy,初始位置为view中心点上下随机偏移一定值,粒子在y轴上沿y=x*(x-1)曲线运动;
- radius,初始为大半径(1/5概率)、小半径(4/5概率),之后开始逐渐变大;
- alpha,初始为1,动画实际进程超过7/10时,开始逐渐变透明;
- 每一个粒子都有一个经过随机运算得出的life和overflow,取值差不多为0.0x~0.1x之间,用于控制粒子在开始的前多少时间、动画结束前的多少时间,是不显示的,这样就有了一个错落出现、消失的层次感。
在这里,再次为开发者献上自己的膝盖~~~
一般当我们读懂了别人的代码后,自己去实现的时候,总是会遇到这样那样的问题,因此,我们这里可以尝试自己去顺着大牛的思路来实现这个效果,同时,加入自己的想法,进行部分功能的改进。这些东西就留给下一篇博客了!
Android粒子破碎效果(2)——实现多种破碎效果之ParticleSmasher
- Android粒子破碎效果(1)——开源项目ExplosionField代码分析
- 【Android效果集】学习ExplosionField之粒子破碎效果
- 【Android效果集】学习ExplosionField之粒子破碎效果
- 学习ExplosionField之粒子破碎效果
- Android粒子破碎效果(2)——实现多种破碎效果之ParticleSmasher
- Android 爆炸粒子动画-参考ExplosionField开源项目
- 自定义粒子破碎效果
- 开源项目ExplosionField(爆炸特效)源码分析
- 开源项目ExplosionField(爆炸特效)源码分析
- 烟花爆炸色彩效果—ExplosionField
- 【Android开源项目解析】RecyclerView侧滑删除粒子效果实现——初探Android开源粒子库 Leonids
- 【Android开源项目解析】RecyclerView侧滑删除粒子效果实现——初探Android开源粒子库 Leonids
- Android 玻璃破碎效果
- android opengl es 粒子效果实例代码
- android任意view爆炸效果--第三方开源--ExplosionField
- Swift——粒子效果
- Android 开源项目代码分析思路
- Android粒子效果
- BBC权威分析:未来哪些职业不容易被淘汰?
- 一文读懂贝叶斯分类算法(附学习资源)
- 玩家最难忘的游戏音乐——第二弹!
- C语言删除特定字符串
- webview头部添加自定义view,跟webview一起滚动
- Android粒子破碎效果(1)——开源项目ExplosionField代码分析
- SimpleDateFormat使用详解
- ThinkPHP路由模式-URI
- redis缓存和mysql数据库同步
- Mysql 查询用户排行名次
- Python图像处理库:Pillow 初级教程
- Java8 Lambda表达式要点
- STM32——CAN总线通信
- 记录 Duplicate spring bean id dubbo