前言
粒子动画作用相比其他动画来说是十分杂乱了的,首要触及三个方面,粒子初始化、粒子位移、粒子收回等问题,其间特别是粒子位移是最杂乱的,触及到的数学逻辑十分多,首要是各种三角函数、物理学公式等。
本篇将完成两种动画作用,代码根本相同,仅仅旋转速度不一样,因而,本篇其实能够看作一篇模板文章,具体作用能够通过调理参数生成各种动画
第一种动画
第二种动画
完成过程
其实和以往的粒子作用一样,粒子需求被办理起来,因而咱们需求有容器、也需求粒子目标
粒子目标定义
下面是创建粒子目标的逻辑,根本属性在注释中了
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));
}
}
}
后续
本篇延续实用性风格,后续咱们持续完成一些粒子动画作用。