欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

基于Android實現(xiàn)煙花效果

 更新時間:2023年11月21日 08:54:55   作者:時光少年  
這篇文章文章我們將介紹如何通過Canvas 2D坐標系實現(xiàn)粒子效果,文中通過代碼示例和圖文結合介紹的非常詳細,對大家的學習或工作有一定的幫助,感興趣的同學可以自己動手嘗試一下

一、效果預覽

無中心版本

有中心版本

二、實現(xiàn)

2.1 均勻分布

我們首要解決的問題是計算出粒子運動方向,保證粒子能正常擴散到目標范圍和區(qū)域,另外還有保證粒子盡可能隨機和均勻分布在任意方向。

方法是:

粒子擴散的范圍是一個圓的范圍內(nèi),我們要盡可能利用圓的旋轉(zhuǎn)半徑和夾角之間的關系,屬于高中數(shù)學知識。另外也要控制粒子的數(shù)量,防止堆疊過多的問題。

int t = i % 12;  
double degree = random.nextFloat() * 30 + t * 30;  // 12等分圓,沒等分都保證產(chǎn)生粒子 
// 360 /12 = 30 ,意味著每等分30度區(qū)域內(nèi)需要產(chǎn)生一定的粒子

2.2 速度計算

我們上一篇說過,計算出速度是最難的,要結合場景,這里我們采樣計算終點的方式,目的有2個,限制粒子運動出大圓,限制時間。

float minRadius = maxRadius * 1f / 2f;
double radians = Math.toRadians(degree);
int radius = (int) (random.nextFloat() * maxRadius / 2f);
float x = (float) (Math.cos(radians) * (radius + minRadius));
float y = (float) (Math.sin(radians) * (radius + minRadius));
float speedX = (x - 0) / dt;
float speedY = (y - 0) / dt;

2.3 顏色

顏色選擇自己喜歡的就可以,我喜歡五彩繽紛,所以隨機生成

int color = argb(random.nextFloat(), random.nextFloat(), random.nextFloat());

2.4 定義粒子對象

    static class Star {
        private final boolean fromCenter;
        private final int color;
        private double radians;
        private float r;
        float speedX;
        float speedY;
        long startTime;
        Path path = new Path();
        int type = TYPE_QUAD;


        public Star(float speedX, float speedY, long clockTime, float r, double radians, int color, boolean fromCenter, int type) {
            this.speedX = speedX;
            this.speedY = speedY;
            this.startTime = clockTime;
            this.r = r;
            this.radians = radians;
            this.fromCenter = fromCenter;
            this.color = color;
            this.type = type;
        }
        public void draw(Canvas canvas,Paint paint,long clockTime){

      }
}

2.4 基礎骨架

        public void drawBase(Canvas canvas, Paint paint, long clockTime) {
            long costTime = clockTime - startTime;

            float dx = speedX * costTime;
            float dy = speedY * costTime;

            double currentRadius = Math.sqrt(dx * dx + dy * dy);

            paint.setColor(color);

            if (currentRadius > 0) {
                double asin = Math.asin(r / currentRadius);
                //利用反三角函數(shù)計算出切線與圓的夾角
                int t = 1;
                for (int i = 0; i < 2; i++) {
                    double aspectRadius = Math.abs(Math.cos(asin) * currentRadius);  //切線長度
                    float ax = (float) (aspectRadius * Math.cos(radians + asin * t));
                    float ay = (float) (aspectRadius * Math.sin(radians + asin * t));
                    if (fromCenter) {
                        canvas.drawLine(0, 0, ax, ay, paint);
                    } else {
                        canvas.drawLine(dx / 3, dy / 3, ax, ay, paint);
                    }
                    t = -1;
                }

            }
            canvas.drawCircle(dx, dy, r, paint);
        }

2.5 進一步優(yōu)化

public void drawCircleCCW(Canvas canvas, Paint paint, long clockTime) {
            long costTime = clockTime - startTime;

            float dx = speedX * costTime;
            float dy = speedY * costTime;

            double currentRadius = Math.sqrt(dx * dx + dy * dy);
            path.reset();

            if (currentRadius > 0) {

                if (fromCenter) {
                    path.moveTo(0, 0);
                } else {
                    path.moveTo(dx / 3, dy / 3);
                }

                //1、利用反三角函數(shù)計算出小圓切線與所有小圓原點與(0,0)點的夾角
                double asin = Math.asin(r / currentRadius);

                //2、計算出切線長度
                double aspectRadius = Math.abs(Math.cos(asin) * currentRadius);

                float axLeft = (float) (aspectRadius * Math.cos(radians - asin));
                float ayLeft = (float) (aspectRadius * Math.sin(radians - asin));
                path.lineTo(axLeft, ayLeft);

                float axRight = (float) (aspectRadius * Math.cos(radians + asin));
                float ayRight = (float) (aspectRadius * Math.sin(radians + asin));
                path.lineTo(axRight, ayRight);
                path.addCircle(dx, dy, r, Path.Direction.CCW);

            }
            path.close();
            paint.setColor(color);
            canvas.drawPath(path, paint);
        }

有點樣子了,但是問題是,Path動畫并沒有和粒子圓點閉合,這樣就會有問題,后續(xù)如果要使用Shader著色 (為啥要用Shader著色,主要是火焰效果很難畫出來,還得借助一些其他工具),必然產(chǎn)生不均勻問題。為了實現(xiàn)開頭的效果,最初是計算切線和小圓的夾角讓Path閉合,但是計算量和難度太大了,直接使用貝塞爾曲線更省事。

     public void drawQuad(Canvas canvas, Paint paint, long clockTime) {
            long costTime = clockTime - startTime;

            float dx = speedX * costTime;
            float dy = speedY * costTime;

            double currentRadius = Math.sqrt(dx * dx + dy * dy);
            path.reset();

            if (currentRadius > 0) {
                if (fromCenter) {
                    path.moveTo(0, 0);
                } else {
                    path.moveTo(dx / 3, dy / 3);
                }

                //1、利用反三角函數(shù)計算出小圓切線與所有小圓原點與(0,0)點的夾角
                double asin = Math.asin(r / currentRadius);

                //2、計算出切線長度
                double aspectRadius = Math.abs(Math.cos(asin) * currentRadius);

                float axLeft = (float) (aspectRadius * Math.cos(radians - asin));
                float ayLeft = (float) (aspectRadius * Math.sin(radians - asin));
                path.lineTo(axLeft, ayLeft);

                float axRight = (float) (aspectRadius * Math.cos(radians + asin));
                float ayRight = (float) (aspectRadius * Math.sin(radians + asin));

                float cx = (float) (Math.cos(radians) * (currentRadius + 2 * r));
                float cy = (float) (Math.sin(radians) * (currentRadius + 2 * r));
                //如果使用三角函數(shù)計算切線可能很復雜,這里使用貝塞爾曲線簡化邏輯
                path.quadTo(cx, cy, axRight, ayRight);
                path.lineTo(axRight, ayRight);

            }
            path.close();
            paint.setColor(color);
            canvas.drawPath(path, paint);
        }

三、全部代碼

public class FireworksView extends View implements Runnable {

    private static final long V_SYNC_TIME = 30;
    private final DisplayMetrics mDM;
    private TextPaint mArcPaint;
    private long displayTime = 500L; //控制時間,防止逃出邊界
    private long clockTime = 0;
    private boolean isNextDrawingTimeScheduled = false;
    private TextPaint mDrawerPaint = null;
    private Random random;

    final int maxStartNum = 50;
    Star[] stars = new Star[maxStartNum];
    private boolean isRefresh = true;


    public static final int TYPE_BASE = 1;
    public static final int TYPE_QUAD = 2;
    public static final int TYPE_RECT = 3;
    public static final int TYPE_CIRCLE_CCW = 4;


    public FireworksView(Context context) {
        this(context, null);
    }

    public FireworksView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public FireworksView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        mDM = getResources().getDisplayMetrics();
        initPaint();

        setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                startPlay();
            }
        });
    }


    public static int argb(float red, float green, float blue) {
        return ((int) (1 * 255.0f + 0.5f) << 24) |
                ((int) (red * 255.0f + 0.5f) << 16) |
                ((int) (green * 255.0f + 0.5f) << 8) |
                (int) (blue * 255.0f + 0.5f);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, 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;
        }
        random = new Random(SystemClock.uptimeMillis());

        setMeasuredDimension(widthSize, heightSize);
    }


    public float dp2px(float dp) {
        return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, mDM);
    }

    public float sp2px(float dp) {
        return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, dp, mDM);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        int width = getWidth();
        int height = getHeight();
        if (width <= 10 || height <= 10) {
            return;
        }
        int saveCount = canvas.save();

        int maxRadius = Math.min(width, height) / 2;
        canvas.translate(width / 2, height / 2);

        long clockTime = getClockTime();
        if (isRefresh) {
            float dt = 1000;
            float r = 5;

            for (int i = 0; i < maxStartNum; i++) {

                int t = i % 12;
                double degree = random.nextFloat() * 30 + t * 30;  // 12等分圓

                float minRadius = maxRadius * 1f / 2f;

                double radians = Math.toRadians(degree);
                int radius = (int) (random.nextFloat() * maxRadius / 2f);

                float x = (float) (Math.cos(radians) * (radius + minRadius));
                float y = (float) (Math.sin(radians) * (radius + minRadius));

                float speedX = (x - 0) / dt;
                float speedY = (y - 0) / dt;

                int color = argb(random.nextFloat(), random.nextFloat(), random.nextFloat());
                stars[i] = new Star(speedX, speedY, clockTime, r, radians, color, false, TYPE_QUAD);
            }
            isRefresh = false;
        }

        for (int i = 0; i < maxStartNum; i++) {
            Star star = stars[i];
            star.draw(canvas, mDrawerPaint, clockTime);
        }

        if (!isNextDrawingTimeScheduled) {
            isNextDrawingTimeScheduled = true;
            postDelayed(this, V_SYNC_TIME);
        }
        canvas.restoreToCount(saveCount);

    }


    @Override
    public void run() {
        isNextDrawingTimeScheduled = false;
        clockTime += 32;
        if (clockTime > displayTime) {
            clockTime = displayTime;
        }
        postInvalidate();
    }

    private long getClockTime() {
        return clockTime;
    }

    public void startPlay() {
        clockTime = 0;
        isRefresh = true;
        removeCallbacks(this);
        run();
    }


    private void initPaint() {
        // 實例化畫筆并打開抗鋸齒
        mArcPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG);
        mArcPaint.setAntiAlias(true);
        mArcPaint.setStyle(Paint.Style.STROKE);
        mArcPaint.setStrokeCap(Paint.Cap.ROUND);

        mDrawerPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG);
        mDrawerPaint.setAntiAlias(true);
        mDrawerPaint.setStyle(Paint.Style.FILL);
        mDrawerPaint.setStrokeCap(Paint.Cap.ROUND);

    }

    static class Star {
        private final boolean fromCenter;
        private final int color;
        private double radians;
        private float r;
        float speedX;
        float speedY;
        long startTime;
        Path path = new Path();
        int type = TYPE_QUAD;


        public Star(float speedX, float speedY, long clockTime, float r, double radians, int color, boolean fromCenter, int type) {
            this.speedX = speedX;
            this.speedY = speedY;
            this.startTime = clockTime;
            this.r = r;
            this.radians = radians;
            this.fromCenter = fromCenter;
            this.color = color;
            this.type = type;
        }


        public void draw(Canvas canvas, Paint paint, long clockTime) {

            switch (type) {
                case TYPE_BASE:
                    drawBase(canvas, paint, clockTime);
                    break;
                case TYPE_RECT:
                    drawRect(canvas, paint, clockTime);
                    break;
                case TYPE_CIRCLE_CCW:
                    drawCircleCCW(canvas, paint, clockTime);
                    break;
                case TYPE_QUAD:
                    drawQuad(canvas, paint, clockTime);
                    break;
            }

        }

        public void drawQuad(Canvas canvas, Paint paint, long clockTime) {
            long costTime = clockTime - startTime;

            float dx = speedX * costTime;
            float dy = speedY * costTime;

            double currentRadius = Math.sqrt(dx * dx + dy * dy);
            path.reset();

            if (currentRadius > 0) {
                if (fromCenter) {
                    path.moveTo(0, 0);
                } else {
                    path.moveTo(dx / 3, dy / 3);
                }

                //1、利用反三角函數(shù)計算出小圓切線與所有小圓原點與(0,0)點的夾角
                double asin = Math.asin(r / currentRadius);

                //2、計算出切線長度
                double aspectRadius = Math.abs(Math.cos(asin) * currentRadius);

                float axLeft = (float) (aspectRadius * Math.cos(radians - asin));
                float ayLeft = (float) (aspectRadius * Math.sin(radians - asin));
                path.lineTo(axLeft, ayLeft);

                float axRight = (float) (aspectRadius * Math.cos(radians + asin));
                float ayRight = (float) (aspectRadius * Math.sin(radians + asin));

                float cx = (float) (Math.cos(radians) * (currentRadius + 2 * r));
                float cy = (float) (Math.sin(radians) * (currentRadius + 2 * r));
                //如果使用三角函數(shù)計算切線可能很復雜,這里使用貝塞爾曲線簡化邏輯
                path.quadTo(cx, cy, axRight, ayRight);
                path.lineTo(axRight, ayRight);

            }
            path.close();
            paint.setColor(color);
            canvas.drawPath(path, paint);
        }

        public void drawCircleCCW(Canvas canvas, Paint paint, long clockTime) {
            long costTime = clockTime - startTime;

            float dx = speedX * costTime;
            float dy = speedY * costTime;

            double currentRadius = Math.sqrt(dx * dx + dy * dy);
            path.reset();

            if (currentRadius > 0) {

                if (fromCenter) {
                    path.moveTo(0, 0);
                } else {
                    path.moveTo(dx / 3, dy / 3);
                }

                //1、利用反三角函數(shù)計算出小圓切線與所有小圓原點與(0,0)點的夾角
                double asin = Math.asin(r / currentRadius);

                //2、計算出切線長度
                double aspectRadius = Math.abs(Math.cos(asin) * currentRadius);

                float axLeft = (float) (aspectRadius * Math.cos(radians - asin));
                float ayLeft = (float) (aspectRadius * Math.sin(radians - asin));
                path.lineTo(axLeft, ayLeft);

                float axRight = (float) (aspectRadius * Math.cos(radians + asin));
                float ayRight = (float) (aspectRadius * Math.sin(radians + asin));
                path.lineTo(axRight, ayRight);
                path.addCircle(dx, dy, r, Path.Direction.CCW);

            }
            path.close();
            paint.setColor(color);
            canvas.drawPath(path, paint);
        }


        public void drawBase(Canvas canvas, Paint paint, long clockTime) {
            long costTime = clockTime - startTime;

            float dx = speedX * costTime;
            float dy = speedY * costTime;

            double currentRadius = Math.sqrt(dx * dx + dy * dy);

            paint.setColor(color);

            if (currentRadius > 0) {
                double asin = Math.asin(r / currentRadius);
                //利用反三角函數(shù)計算出切線與圓的夾角
                int t = 1;
                for (int i = 0; i < 2; i++) {
                    double aspectRadius = Math.abs(Math.cos(asin) * currentRadius);  //切線長度
                    float ax = (float) (aspectRadius * Math.cos(radians + asin * t));
                    float ay = (float) (aspectRadius * Math.sin(radians + asin * t));
                    if (fromCenter) {
                        canvas.drawLine(0, 0, ax, ay, paint);
                    } else {
                        canvas.drawLine(dx / 3, dy / 3, ax, ay, paint);
                    }
                    t = -1;
                }

            }
            canvas.drawCircle(dx, dy, r, paint);
        }

        public void drawRect(Canvas canvas, Paint paint, long clockTime) {
            long costTime = clockTime - startTime;

            float dx = speedX * costTime;
            float dy = speedY * costTime;

            paint.setColor(color);
            RectF rectF = new RectF(dx - r, dy - r, dx + r, dy + r);
            canvas.drawRect(rectF, paint);
         //   canvas.drawCircle(dx,dy,r,paint);

        }
    }

}

四、總結

本篇我們大量使用了三角函數(shù)、反三角函數(shù),因此一定要掌握好數(shù)學基礎。

以上就是基于Android實現(xiàn)煙花效果的詳細內(nèi)容,更多關于Android煙花效果的資料請關注腳本之家其它相關文章!

相關文章

  • Android 多媒體播放API簡單實例

    Android 多媒體播放API簡單實例

    這篇文章主要介紹了Android 多媒體播放API簡單實例的相關資料,這里附有代碼實例及實現(xiàn)效果圖,需要的朋友可以參考下
    2016-12-12
  • Android EditText實現(xiàn)輸入表情

    Android EditText實現(xiàn)輸入表情

    editText是TextView的子類,TextView能用的工具EditText都能用,接下來通過實例代碼給大家分享Android EditText實現(xiàn)輸入表情功能,感興趣的朋友一起看看吧
    2017-08-08
  • Android-實現(xiàn)切換Fragment頁功能的實現(xiàn)代碼

    Android-實現(xiàn)切換Fragment頁功能的實現(xiàn)代碼

    本篇文章主要介紹了Android-實現(xiàn)切換Fragment頁功能的實現(xiàn)代碼,具有一定的參加價值,有興趣的可以了解一下。
    2017-02-02
  • Android 5.0以上Toast不顯示的解決方法

    Android 5.0以上Toast不顯示的解決方法

    最近在開發(fā)中我們經(jīng)常會在適配5.0以后的機型遇到各種各樣的問題,其中有一個不大不小的問題就是:Toast不顯示問題,這篇文章就給大家總結了Android 5.0以上Toast不顯示的原因與解決方法,有需要的朋友們可以參考借鑒,下面來一起看看吧。
    2016-11-11
  • 詳解android與服務端交互的兩種方式

    詳解android與服務端交互的兩種方式

    這篇文章主要介紹了詳解android與服務端交互的兩種方式,此處介紹兩種方式:使用Google原生的Gson解析json數(shù)據(jù),使用JSONObject解析json數(shù)據(jù),有興趣的可以了解一下
    2017-07-07
  • Android通過HTTP協(xié)議實現(xiàn)斷點續(xù)傳下載實例

    Android通過HTTP協(xié)議實現(xiàn)斷點續(xù)傳下載實例

    本篇文章主要介紹了Android通過HTTP協(xié)議實現(xiàn)斷點續(xù)傳下載實例,具有一定的參考價值,感興趣的小伙伴們可以參考一下。
    2017-04-04
  • Android利用Service開發(fā)簡單的音樂播放功能

    Android利用Service開發(fā)簡單的音樂播放功能

    這篇文章主要介紹了Android利用Service開發(fā)簡單的音樂播放功能,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習吧
    2023-04-04
  • Android Studio 配置忽略文件的方法實現(xiàn)

    Android Studio 配置忽略文件的方法實現(xiàn)

    這篇文章主要介紹了Android Studio 配置忽略文件的方法實現(xiàn),文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2020-10-10
  • Android實現(xiàn)拍照截圖功能

    Android實現(xiàn)拍照截圖功能

    這篇文章主要介紹了Android實現(xiàn)拍照截圖功能,簡單介紹了Android實現(xiàn)拍照截圖功能的步驟,供大家參考,感興趣的小伙伴們可以參考一下
    2016-01-01
  • Android 短信轉(zhuǎn)換成彩信的消息數(shù)量(實例代碼)

    Android 短信轉(zhuǎn)換成彩信的消息數(shù)量(實例代碼)

    本文通過實例代碼給大家介紹了Android 短信轉(zhuǎn)換成彩信的消息數(shù)量,需要的朋友可以參考下
    2017-05-05

最新評論