Android實(shí)現(xiàn)粒子中心擴(kuò)散動(dòng)畫(huà)效果
前言
粒子動(dòng)畫(huà)效果相比其他動(dòng)畫(huà)來(lái)說(shuō)是非常復(fù)雜了的,主要涉及三個(gè)方面,粒子初始化、粒子位移、粒子回收等問(wèn)題,其中特別是粒子位移是最復(fù)雜的,涉及到的數(shù)學(xué)邏輯非常多,主要是各種三角函數(shù)、物理學(xué)公式等。
本篇將實(shí)現(xiàn)兩種動(dòng)畫(huà)效果,代碼基本相同,只是旋轉(zhuǎn)速度不一樣,因此,本篇其實(shí)可以看作一篇模板文章,具體效果可以通過(guò)調(diào)節(jié)參數(shù)生成各種動(dòng)畫(huà)
第一種動(dòng)畫(huà)

第二種動(dòng)畫(huà)

實(shí)現(xiàn)步驟
其實(shí)和以往的粒子效果一樣,粒子需要被管理起來(lái),因此我們需要有容器、也需要粒子對(duì)象
粒子對(duì)象定義
下面是創(chuàng)建粒子對(duì)象的邏輯,基本屬性在注釋中了
static class Circle {
int maxLength; //最大運(yùn)行距離
float speed; //外擴(kuò)速度
float rotate; // 角速度
private float degree; //起始角度
private int y; //y坐標(biāo)
private int x; //x坐標(biāo)
private int color; //顏色
private float radius; //小圓半徑
private float drawRadius; //繪制時(shí)的小圓半徑
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;
}
}
粒子更新
在任何動(dòng)畫(huà)中,粒子運(yùn)動(dòng)必須具備時(shí)間屬性,任何符合物理學(xué)的位移運(yùn)動(dòng),速度和時(shí)間的關(guān)系是位移計(jì)算的方法。下面,我們繼續(xù)給Circle類(lèi)添加更新方法。
這里一個(gè)重要的知識(shí)點(diǎn)是
- Math.hypot(x, y) :平方根計(jì)算
- Math.atan2(y, x): 斜率計(jì)算,注意,此角度具備方向
public boolean update(long timeline) {
float length = (float) Math.hypot(x, y); //計(jì)算當(dāng)前移動(dòng)的距離(距離中心點(diǎn))
float center = length + this.speed * timeline; //計(jì)算即將到達(dá)的距離
float ratio = center / maxLength; //計(jì)算與最遠(yuǎn)距離的比值
this.drawRadius = (1f - ratio) * radius; //距離越遠(yuǎn),圓的半徑越小
double degree = Math.atan2(y, x) + rotate; //即將旋轉(zhuǎn)的角度
this.x = (int) (center * Math.cos(degree)); //新的x
this.y = (int) (center * Math.sin(degree)); //新的y
if (drawRadius <= 0) {
return false; //如果半徑為0時(shí),意味著圓看不見(jiàn)了,因此要坐下標(biāo)記
}
return true;
}
粒子繪制方法
繪制自身其實(shí)很簡(jiǎn)單,只需要簡(jiǎn)單的調(diào)用Canvas相關(guān)邏輯即可
public void draw(Canvas canvas, TextPaint paint) {
paint.setColor(color);
canvas.drawCircle(x, y, drawRadius, paint);
}
粒子回收
為了減少內(nèi)存申請(qǐng)頻率,我們對(duì)跑出邊界的粒子進(jìn)行重置
public void reset() {
this.x = (int) (radius * Math.cos(degree));
this.y = (int) (radius * Math.sin(degree));
}
View邏輯
以上是完整的粒子對(duì)象邏輯,接下來(lái)我們實(shí)現(xiàn)一個(gè)View,用來(lái)管理和繪制粒子。
int maxCircleRadius = 20; //粒子初始半徑 List<Circle> circleList = new ArrayList<>(); //容器 int maxCircleNum = 300; //最大數(shù)量
繪制邏輯
首先是初始化,我們這里設(shè)置了3種粒子,因此間隔角度是120度,而我們每次增加三種,防止出現(xiàn)混亂的問(wèn)題。
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));
}
下面是每個(gè)粒子的繪制邏輯
for (int i = 0; i < circleList.size(); i++) {
Circle circle = circleList.get(i);
circle.draw(canvas, mPaint); //繪制方法
}
更新粒子
下面有個(gè)重要的邏輯,其實(shí)前面也提到過(guò),就是重置跑出邊界的粒子
for (int i = 0; i < circleList.size(); i++) {
Circle circle = circleList.get(i);
if(!circle.update(16)){
circle.reset(); //如果不能更新,則進(jìn)行重置
}
}
postInvalidate(); //刷新繪制邏輯
以上就是整體核心邏輯
效果調(diào)節(jié)
我們開(kāi)頭的兩種效果其實(shí)是同一個(gè)View實(shí)現(xiàn)的,這其中一個(gè)重要的點(diǎn)就是速度調(diào)整,文章開(kāi)頭是調(diào)整出的兩種效果,當(dāng)然染還可以調(diào)整出其他效果 第一種
this.rotate = 0.2f; this.speed = 0.2f; //外擴(kuò)效果

第二種
this.rotate = 0.35f; //觸角效果 this.speed = 0.2f;

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

當(dāng)然,還有更多,篇幅原因就不深入了。
總結(jié)
本篇到這里就結(jié)束了,其實(shí)我們的核心代碼并不多,但是簡(jiǎn)單的邏輯就能衍生出很多動(dòng)畫(huà)效果。其實(shí),學(xué)習(xí)粒子動(dòng)畫(huà)是非常有意思的事,很多時(shí)候,你在實(shí)現(xiàn)某些效果的途中,就能突然開(kāi)發(fā)出一種新的動(dòng)畫(huà)效果。
本篇代碼
下面是本篇內(nèi)容的完整邏輯,基本就在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; //最大運(yùn)行距離
float speed; //外擴(kuò)速度
float rotate; // 角速度
private float degree; //起始角度
private int y; //y坐標(biāo)
private int x; //x坐標(biāo)
private int color; //顏色
private float radius; //小圓半徑
private float drawRadius; //繪制時(shí)的小圓半徑
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實(shí)現(xiàn)粒子中心擴(kuò)散動(dòng)畫(huà)效果的詳細(xì)內(nèi)容,更多關(guān)于Android粒子中心擴(kuò)散的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Android仿新聞頂部導(dǎo)航標(biāo)簽切換效果
這篇文章主要為大家詳細(xì)介紹了Android仿新聞頂部導(dǎo)航標(biāo)簽切換效果,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-11-11
Android底部導(dǎo)航欄的三種風(fēng)格實(shí)現(xiàn)
這篇文章主要介紹了Android底部導(dǎo)航欄的三種風(fēng)格實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-06-06
基于Alarmmanager實(shí)現(xiàn)簡(jiǎn)單鬧鐘功能
這篇文章主要為大家詳細(xì)介紹了基于Alarmmanager實(shí)現(xiàn)簡(jiǎn)單鬧鐘功能,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-06-06
Android實(shí)現(xiàn)一個(gè)完美的倒計(jì)時(shí)功能
在Adroid應(yīng)用中,倒計(jì)時(shí)的功能使用的很多,例如點(diǎn)擊獲取短信驗(yàn)證碼之后的倒計(jì)時(shí)等等,這篇文章主要給大家介紹了關(guān)于利用Android如何實(shí)現(xiàn)一個(gè)完美的倒計(jì)時(shí)功能的相關(guān)資料,需要的朋友可以參考下2021-11-11
Android使用GridView實(shí)現(xiàn)橫向滾動(dòng)效果
這篇文章主要為大家詳細(xì)介紹了Android使用GridView實(shí)現(xiàn)橫向滾動(dòng)效果,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-07-07
Android 獲取服務(wù)器與客戶端時(shí)差的實(shí)例代碼
下面小編就為大家分享一篇Android 獲取服務(wù)器與客戶端時(shí)差的實(shí)例代碼,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2018-01-01

