前言
粒子动画效果相比其他动画来说是非常复杂了的,主要涉及三个方面,粒子初始化、粒子位移、粒子回收等问题,其中特别是粒子位移是最复杂的,涉及到的数学逻辑非常多,主要是各种三角函数、物理学公式等。
本篇将实现两种动画效果,代码基本相同,只是旋转速度不一样,因此,本篇其实可以看作一篇模板文章,具体效果可以通过调节参数生成各种动画
第一种动画

第二种动画

实现步骤
其实和以往的粒子效果一样,粒子需要被管理起来,因此我们需要有容器、也需要粒子对象
粒子对象定义
下面是创建粒子对象的逻辑,基本属性在注释中了
static class circle {
int maxlength; //最大运行距离
float speed; //外扩速度
float rotate; // 角速度
private float degree; //起始角度
private int y; //y坐标
private int x; //x坐标
private int color; //颜色
private float radius; //小圆半径
private float drawradius; //绘制时的小圆半径
public circle(int color, int maxlength, float radius, float degree) {
this.color = color;
this.radius = radius;
this.maxlength = maxlength;
this.degree = degree;
this.x = (int) (radius * math.cos(degree));
this.y = (int) (radius * math.sin(degree));
this.rotate = 0.35f; //触角效果
this.speed = 0.2f;
}
}
粒子更新
在任何动画中,粒子运动必须具备时间属性,任何符合物理学的位移运动,速度和时间的关系是位移计算的方法。下面,我们继续给circle类添加更新方法。
这里一个重要的知识点是
- math.hypot(x, y) :平方根计算
- math.atan2(y, x): 斜率计算,注意,此角度具备方向
public boolean update(long timeline) {
float length = (float) math.hypot(x, y); //计算当前移动的距离(距离中心点)
float center = length + this.speed * timeline; //计算即将到达的距离
float ratio = center / maxlength; //计算与最远距离的比值
this.drawradius = (1f - ratio) * radius; //距离越远,圆的半径越小
double degree = math.atan2(y, x) + rotate; //即将旋转的角度
this.x = (int) (center * math.cos(degree)); //新的x
this.y = (int) (center * math.sin(degree)); //新的y
if (drawradius <= 0) {
return false; //如果半径为0时,意味着圆看不见了,因此要坐下标记
}
return true;
}
粒子绘制方法
绘制自身其实很简单,只需要简单的调用canvas相关逻辑即可
public void draw(canvas canvas, textpaint paint) {
paint.setcolor(color);
canvas.drawcircle(x, y, drawradius, paint);
}
粒子回收
为了减少内存申请频率,我们对跑出边界的粒子进行重置
public void reset() {
this.x = (int) (radius * math.cos(degree));
this.y = (int) (radius * math.sin(degree));
}
view逻辑
以上是完整的粒子对象逻辑,接下来我们实现一个view,用来管理和绘制粒子。
int maxcircleradius = 20; //粒子初始半径 list<circle> circlelist = new arraylist<>(); //容器 int maxcirclenum = 300; //最大数量
绘制逻辑
首先是初始化,我们这里设置了3种粒子,因此间隔角度是120度,而我们每次增加三种,防止出现混乱的问题。
final float rotatedegree = (float) math.toradians(120f); //间隔角度
if (circlelist.size() < maxcirclenum) {
//每次增加三种
circlelist.add(new circle(color.red, (int) maxradius, maxcircleradius, 0 * rotatedegree));
circlelist.add(new circle(color.green, (int) maxradius, maxcircleradius, 1 * rotatedegree));
circlelist.add(new circle(color.cyan, (int) maxradius, maxcircleradius, 2 * rotatedegree));
}
下面是每个粒子的绘制逻辑
for (int i = 0; i < circlelist.size(); i++) {
circle circle = circlelist.get(i);
circle.draw(canvas, mpaint); //绘制方法
}
更新粒子
下面有个重要的逻辑,其实前面也提到过,就是重置跑出边界的粒子
for (int i = 0; i < circlelist.size(); i++) {
circle circle = circlelist.get(i);
if(!circle.update(16)){
circle.reset(); //如果不能更新,则进行重置
}
}
postinvalidate(); //刷新绘制逻辑
以上就是整体核心逻辑
效果调节
我们开头的两种效果其实是同一个view实现的,这其中一个重要的点就是速度调整,文章开头是调整出的两种效果,当然染还可以调整出其他效果 第一种
this.rotate = 0.2f; this.speed = 0.2f; //外扩效果

第二种
this.rotate = 0.35f; //触角效果 this.speed = 0.2f;

第三种
this.rotate = 0.8f; this.speed = 0.1f;

当然,还有更多,篇幅原因就不深入了。
总结
本篇到这里就结束了,其实我们的核心代码并不多,但是简单的逻辑就能衍生出很多动画效果。其实,学习粒子动画是非常有意思的事,很多时候,你在实现某些效果的途中,就能突然开发出一种新的动画效果。
本篇代码
下面是本篇内容的完整逻辑,基本就在100行左右。
public class circleparticleview extends view {
private textpaint mpaint;
private displaymetrics mdm;
public circleparticleview(context context) {
this(context, null);
}
public circleparticleview(context context, attributeset attrs) {
super(context, attrs);
mdm = getresources().getdisplaymetrics();
initpaint();
}
private void initpaint() {
//否则提供给外部纹理绘制
mpaint = new textpaint(paint.anti_alias_flag);
mpaint.setantialias(true);
mpaint.setstyle(paint.style.fill_and_stroke);
mpaint.setstrokecap(paint.cap.round);
paintcompat.setblendmode(mpaint, blendmodecompat.plus);
}
@override
protected void onmeasure(int widthmeasurespec, int heightmeasurespec) {
int widthmode = measurespec.getmode(widthmeasurespec);
int widthsize = measurespec.getsize(widthmeasurespec);
if (widthmode != measurespec.exactly) {
widthsize = mdm.widthpixels / 2;
}
int heightmode = measurespec.getmode(heightmeasurespec);
int heightsize = measurespec.getsize(heightmeasurespec);
if (heightmode != measurespec.exactly) {
heightsize = widthsize / 2;
}
setmeasureddimension(widthsize, heightsize);
}
int maxcircleradius = 20;
list<circle> circlelist = new arraylist<>();
int maxcirclenum = 300;
long time = 0;
@override
protected void ondraw(canvas canvas) {
super.ondraw(canvas);
int width = getwidth();
int height = getheight();
float maxradius = math.min(width, height) / 2f;
int save = canvas.save();
canvas.translate(width / 2f, height / 2f);
final float rotatedegree = (float) math.toradians(120f);
if (circlelist.size() < maxcirclenum) {
circlelist.add(new circle(color.red, (int) maxradius, maxcircleradius, 0 * rotatedegree));
circlelist.add(new circle(color.green, (int) maxradius, maxcircleradius, 1 * rotatedegree));
circlelist.add(new circle(color.cyan, (int) maxradius, maxcircleradius, 2 * rotatedegree));
}
mpaint.setstyle(paint.style.fill);
for (int i = 0; i < circlelist.size(); i++) {
circle circle = circlelist.get(i);
circle.draw(canvas, mpaint);
}
canvas.restoretocount(save);
for (int i = 0; i < circlelist.size(); i++) {
circle circle = circlelist.get(i);
if (!circle.update(16)) {
circle.reset();
}
}
postinvalidate();
time += 16;
}
static class circle {
int maxlength; //最大运行距离
float speed; //外扩速度
float rotate; // 角速度
private float degree; //起始角度
private int y; //y坐标
private int x; //x坐标
private int color; //颜色
private float radius; //小圆半径
private float drawradius; //绘制时的小圆半径
public circle(int color, int maxlength, float radius, float degree) {
this.color = color;
this.radius = radius;
this.maxlength = maxlength;
this.degree = degree;
this.x = (int) (radius * math.cos(degree));
this.y = (int) (radius * math.sin(degree));
this.rotate = 0.35f; //触角效果
this.speed = 0.2f;
}
public boolean update(long timeline) {
float length = (float) math.hypot(x, y);
float center = length + this.speed * timeline; //距离增加
float ratio = center / maxlength;
this.drawradius = (1f - ratio) * radius;
double degree = math.atan2(y, x) + rotate; //角度增加
this.x = (int) (center * math.cos(degree));
this.y = (int) (center * math.sin(degree));
if (drawradius <= 0) {
return false;
}
return true;
}
public void draw(canvas canvas, textpaint paint) {
paint.setcolor(color);
canvas.drawcircle(x, y, drawradius, paint);
}
public void reset() {
this.x = (int) (radius * math.cos(degree));
this.y = (int) (radius * math.sin(degree));
}
}
}
以上就是android实现粒子中心扩散动画效果的详细内容,更多关于android粒子中心扩散的资料请关注代码网其它相关文章!
发表评论