Android實現粒子爆炸特效
簡介
最近在閒逛的時候,發現了一款粒子爆炸特效的控制元件,覺得比較有意思,效果也不錯。
但是程式碼不好擴充套件,也就是說如果要提供不同的爆炸效果,需要修改的地方比較多。於是我對原始碼進行了一些重構,將爆炸流程和粒子運動分離。
上面兩套程式碼,其實結構都是一樣的,但是實現的效果不同(其實就是粒子運動的演算法不同)。
本篇文章,將給大家介紹粒子爆炸特效的實現方式,替大家理清實現思路。
實現效果如下:
類設計
類設計圖如下:
ExplosionField,爆炸效果發生的場地,是一個View。當一個控制元件需要爆炸時,需要為控制元件生成一個ExplosionField,這個ExplosionField**覆蓋整個螢幕**,於是我們才能看到完整的爆炸效果。
ExplosionAnimator,爆炸動畫,其實是一個計時器,繼承自ValueAnimator。1024s內,完成爆炸動畫,每次計時,就更新所有粒子的運動狀態。draw()方法是它最重要的方法,也就是使所有粒子重繪自身,從而實現動畫效果。
ParticleFactory,是一個抽象類。用於產生粒子陣列,不同的ParticleFactory可以產生不同型別的粒子陣列。
Particle,抽象的粒子類。代表粒子本身,必須擁有的屬性包括,當前自己的cx,cy座標和顏色color。必須實現兩個方法,d**raw()方法選擇怎麼繪製自身(圓形還是方形等),**caculate()計算當前時間,自己所處的位置。
控制元件的使用
控制元件使用很簡單,首先要實現不同的爆炸效果,需要給ExplosionField傳入不同的ParticleFactory工廠,產生不同的粒子。
ExplosionField explosionField = new ExplosionField(this,new FallingParticleFactory());
然後哪個控制元件需要爆炸效果,就這樣新增
explosionField.addListener(findViewById(R.id.text)); explosionField.addListener(findViewById(R.id.layout1));
這樣就為兩個控制元件新增了爆炸效果,注意layout1代表的是一個viewgroup,那麼我們就會為viewgroup中的每個view新增爆炸效果。
我們可以想象,在ExplosionField的建構函式中,傳入不同的ParticleFactory,就可以生成不同的爆炸效果。
爆炸實現思路
1、獲取當前控制元件背景bitmap
例如,例子中使用的是imageview,對於這個控制元件,我提供了一個工具類,可以獲得其背景的Bitmap物件
public static Bitmap createBitmapFromView(View view) { view.clearFocus(); Bitmap bitmap = createBitmapSafely(view.getWidth(), view.getHeight(), Bitmap.Config.ARGB_8888, 1); if (bitmap != null) { synchronized (sCanvas) { Canvas canvas = sCanvas; canvas.setBitmap(bitmap); view.draw(canvas); canvas.setBitmap(null); } } return bitmap; } public static Bitmap createBitmapSafely(int width, int height, Bitmap.Config config, int retryCount) { try { return Bitmap.createBitmap(width, height, config); } catch (OutOfMemoryError e) { e.printStackTrace(); if (retryCount > 0) { System.gc(); return createBitmapSafely(width, height, config, retryCount - 1); } return null; } }
上面的方法,簡而言之,就是將控制元件的Bitmap物件複製了一份,然後返回。
我們知道,bitmap可以看成是一個畫素矩陣,矩陣上面的點,就是一個個帶有顏色的畫素,於是我們可以獲取每個點(未必需要每個)的顏色和位置,組裝成一個物件Particle,這麼一來,Particle就代表帶有顏色的點了。
2、將背景bitmap轉換成Particle陣列
獲取Bitmap以後,我們交給ParticleFactory進行加工,根據Bitmap生產Particle陣列。
public abstract class ParticleFactory { public abstract Particle[][] generateParticles(Bitmap bitmap, Rect bound); }
例如我們來看一個簡單實現類,也是gif圖中,第一個下落效果的工廠類
public class FallingParticleFactory extends ParticleFactory{ public static final int PART_WH = 8; //預設小球寬高 public Particle[][] generateParticles(Bitmap bitmap, Rect bound) { int w = bound.width();//場景寬度 int h = bound.height();//場景高度 int partW_Count = w / PART_WH; //橫向個數 int partH_Count = h / PART_WH; //豎向個數 int bitmap_part_w = bitmap.getWidth() / partW_Count; int bitmap_part_h = bitmap.getHeight() / partH_Count; Particle[][] particles = new Particle[partH_Count][partW_Count]; Point point = null; for (int row = 0; row < partH_Count; row ++) { //行 for (int column = 0; column < partW_Count; column ++) { //列 //取得當前粒子所在位置的顏色 int color = bitmap.getPixel(column * bitmap_part_w, row * bitmap_part_h); float x = bound.left + FallingParticleFactory.PART_WH * column; float y = bound.top + FallingParticleFactory.PART_WH * row; particles[row][column] = new FallingParticle(color,x,y,bound); } } return particles; } }
其中Rect型別的bound,是代表原來View控制元件的寬高資訊。
根據我們設定的每個粒子的大小,和控制元件的寬高,我們就可以計算出,有多少個粒子組成這個控制元件的背景。
我們取得每個粒子所在位置的顏色,位置,用於生產粒子,這就是FallingParticle。
3、生成爆炸場地,開始爆炸動畫流程
爆炸時需要場地的,也就是繪製粒子的地方,我們通過給當前螢幕,新增一個覆蓋全螢幕的ExplosionField來作為爆炸場地。
public class ExplosionField extends View{ ... /** * 給Activity加上全屏覆蓋的ExplosionField */ private void attach2Activity(Activity activity) { ViewGroup rootView = (ViewGroup) activity.findViewById(Window.ID_ANDROID_CONTENT); ViewGroup.LayoutParams lp = new ViewGroup.LayoutParams( ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT); rootView.addView(this, lp); } ... }
爆炸場地新增以後,我們響應控制元件的點選事件,開始動畫
首先是震動動畫
/** * 爆破 * @param view 使得該view爆破 */ public void explode(final View view) { //防止重複點選 if(explosionAnimatorsMap.get(view)!=null&&explosionAnimatorsMap.get(view).isStarted()){ return; } if(view.getVisibility()!=View.VISIBLE||view.getAlpha()==0){ return; } //為了正確繪製粒子 final Rect rect = new Rect(); view.getGlobalVisibleRect(rect); //得到view相對於整個螢幕的座標 int contentTop = ((ViewGroup)getParent()).getTop(); Rect frame = new Rect(); ((Activity) getContext()).getWindow().getDecorView().getWindowVisibleDisplayFrame(frame); int statusBarHeight = frame.top; rect.offset(0, -contentTop - statusBarHeight);//去掉狀態列高度和標題欄高度 //震動動畫 ValueAnimator animator = ValueAnimator.ofFloat(0f, 1f).setDuration(150); animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { Random random = new Random(); @Override public void onAnimationUpdate(ValueAnimator animation) { view.setTranslationX((random.nextFloat() - 0.5f) * view.getWidth() * 0.05f); view.setTranslationY((random.nextFloat() - 0.5f) * view.getHeight() * 0.05f); } }); animator.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { super.onAnimationEnd(animation); explode(view, rect);//爆炸動畫 } }); animator.start(); }
震動動畫很簡單,就是x,y方向上,隨機產生一些位移,使原控制元件發生移動即可。
在震動動畫的最後,呼叫了爆炸動畫,於是爆炸動畫開始。
private void explode(final View view,Rect rect) { final ExplosionAnimator animator = new ExplosionAnimator(this, Utils.createBitmapFromView(view), rect,mParticleFactory); explosionAnimators.add(animator); explosionAnimatorsMap.put(view, animator); animator.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationStart(Animator animation) { //縮小,透明動畫 view.animate().setDuration(150).scaleX(0f).scaleY(0f).alpha(0f).start(); } @Override public void onAnimationEnd(Animator animation) { view.animate().alpha(1f).scaleX(1f).scaleY(1f).setDuration(150).start(); //動畫結束時從動畫集中移除 explosionAnimators.remove(animation); explosionAnimatorsMap.remove(view); animation = null; } }); animator.start(); }
爆炸動畫首先將原控制元件隱藏。
我們來看爆炸動畫的具體實現
public class ExplosionAnimator extends ValueAnimator { ... public ExplosionAnimator(View view, Bitmap bitmap, Rect bound,ParticleFactory particleFactory) { mParticleFactory = particleFactory; mPaint = new Paint(); mContainer = view; setFloatValues(0.0f, 1.0f); setDuration(DEFAULT_DURATION); mParticles = mParticleFactory.generateParticles(bitmap, bound); } //最重要的方法 public void draw(Canvas canvas) { if(!isStarted()) { //動畫結束時停止 return; } //所有粒子運動 for (Particle[] particle : mParticles) { for (Particle p : particle) { p.advance(canvas,mPaint,(Float) getAnimatedValue()); } } mContainer.invalidate(); } @Override public void start() { super.start(); mContainer.invalidate(); } }
實現很簡單,就是根據工廠類,生成粒子陣列。
而其實質是一個ValueAnimator,在一定時間內,從0數到1。
然後提供了一個draw()方法,方法裡面呼叫了每個粒子的advance()方法,並且傳入了當前數到的數字(是一個小數)。
advance()方法裡,其實呼叫了draw()方法和caculate()方法。
上面的實現,其實是一個固定的流程,新增了爆炸場地以後,我們就開始從0數到1,在這個過程中,粒子會根據當前時間,繪製自己的位置,所以粒子的位置,其實是它自己決定的,和流程無關。
也就是說,我們只要用不同的演算法,繪製粒子的位置即可,實現了流程和粒子運動的分離。
4、怎麼運動?粒子自己最清楚
舉個例子,gif圖中,下落效果的粒子是這樣運動的
public class FallingParticle extends Particle{ static Random random = new Random(); float radius = FallingParticleFactory.PART_WH; float alpha = 1.0f; Rect mBound; /** * @param color 顏色 * @param x * @param y */ public FallingParticle(int color, float x, float y,Rect bound) { super(color, x, y); mBound = bound; } ... protected void caculate(float factor){ cx = cx + factor * random.nextInt(mBound.width()) * (random.nextFloat() - 0.5f); cy = cy + factor * random.nextInt(mBound.height() / 2); radius = radius - factor * random.nextInt(2); alpha = (1f - factor) * (1 + random.nextFloat()); } }
caculate(float factor)方法,根據當前時間,計算粒子的下一個位置
我們可以看到,在這個粒子中,cy也就是豎直方向上是不斷增加的,cx也就是水平方向上,是隨機增加或者減少,這樣就形成了下落效果。
計算出當前位置以後,粒子就將自己繪製出來
protected void draw(Canvas canvas,Paint paint){ paint.setColor(color); paint.setAlpha((int) (Color.alpha(color) * alpha)); //這樣透明顏色就不是黑色了 canvas.drawCircle(cx, cy, radius, paint); }
怎麼擴充套件?
從上面的程式碼結構可以看出,爆炸流程和粒子具體運動無關,最重要的是,我們要實現自己的caculate()方法,決定粒子的運動形態。
而不同的粒子可以由對應的工廠產生,所以要擴充套件爆炸特性,只需要定義一個粒子類,和生成粒子類的工廠即可。
相關文章
- Android製作粒子爆炸特效Android特效
- Android在canvas中實現高效能的煙花/粒子特效AndroidCanvas特效
- 粒子類特效SDK,電影級的逼真特效特效
- HTML5 canvas圖片爆炸特效HTMLCanvas特效
- 手把手教你做一個超寫實爆炸特效特效
- Android 中 View 炸裂特效的實現分析AndroidView特效
- 可以隨心所欲的canvas粒子特效Canvas特效
- Flutter實現簡單爆炸效果Flutter
- Flutter動畫實現粒子漂浮效果Flutter動畫
- WebGL 實現雨水特效實驗Web特效
- Canvas 實現炫麗的粒子運動效果(粒子生成文字)Canvas
- 2-66. 製作石頭和稻草的粒子特效特效
- 實現爆炸後的振動效果 (轉)
- 用canvas實現流星特效Canvas特效
- CSS實現開關特效CSS特效
- Android粒子篇之文字的粒子化運動Android
- 紅巨星粒子特效合集外掛:Trapcode Suite 18 for Mac特效UIMac
- 時間軸的實現(簡單到爆炸)
- 用jQuery實現翻書特效jQuery特效
- 8款驚豔的HTML5粒子動畫特效HTML動畫特效
- Android撩妹特效系列!仿instagram文字自動排版功能實現!Android特效
- .滑鼠點選愛心特效的實現特效
- CSS3實現全景圖特效CSSS3特效
- 7款讓人驚歎的HTML5粒子動畫特效HTML動畫特效
- 在 iOS 中使用 GLSL 實現抖音特效iOS特效
- Jquery實現微博分享評論表情特效jQuery特效
- jQuery實現圖示特效(精靈圖)jQuery特效
- 直播特效的實現原理與難點特效
- 雅蛙網ajax特效jQuery實現方法特效jQuery
- 特效實現用查表法實現對水波的模擬(轉)特效
- CSS3實現王者匹配時的粒子動畫效果CSSS3動畫
- canvas 中普通動效與粒子動效的實現Canvas
- 教你如何用WPF實現文字粒子閃爍動畫效果動畫
- vue particles.js 登入背景實現粒子動效VueJS
- 5分鐘用動效工廠實現粒子動畫動畫
- canvas實現具有粒子效果的動態進度條Canvas
- OpenGL ES3 0實現簡單粒子火焰效果S3
- android Toast五種特效AndroidAST特效