欢迎访问移动开发之家(rcyd.net),关注移动开发教程。移动开发之家  移动开发问答|  每日更新
页面位置 : > > > 内容正文

Android实现粒子漩涡动画,

来源: 开发者 投稿于  被查看 7021 次 评论:50

Android实现粒子漩涡动画,


目录
  • 前言
  • 本篇效果实现
    • 首先定义粒子对象
    • 生成粒子对象
    • 绘制雨滴
    • 更新粒子位置
      • 粒子刷新
  • 总结

    前言

    粒子动画经常用于大画幅的渲染效果,实际上难度并不高,但是在使用粒子动画时,必须要遵循的一些要素,主要是:

    • 起点
    • 矢量速度
    • 符合运动学公式

    起点之所以重要是因为其实位置决定粒子出现的位置,矢量速度则决定了快慢和方向,运动学公式属于粒子动画的一部分,当然不是物理性的,毕竟平面尺寸也就那么长,这里的物理学公式使得画面更加丝滑而无跳动感觉。

    本篇将实现下面的效果

    注意:gif图有些卡,实际上流畅很多

    本篇效果实现

    本篇效果是无数圆随机产生然后渐渐变大并外旋,另外也有雨滴,这里的雨滴相对简单一些。

    首先定义粒子对象

    定义粒子对象是非常重要的,绝大部分倾下粒子本身就是需要单独控制的,因为每个粒子的轨迹都是有所差别的。

    定义圆圈粒子

    private static class Circle {
        float x;
        float y;
        int color;
    
        float radius;
    
        Circle(float x, float y, float radius) {
            reset(x, y, radius);
        }
    
        private void reset(float x, float y, float radius) {
            this.x = x;
            this.y = y;
            this.radius = radius;
            this.color = Color.rgb((int) (Math.random() * 256), (int) (Math.random() * 256), (int) (Math.random() * 256));
        }
    }
    

    定义雨滴

    private static class RainDrop {
        float x;
        float y;
    
        RainDrop(float x, float y) {
            this.x = x;
            this.y = y;
        }
    }
    

    定义粒子管理集合

    private ArrayList<Circle> mParticles;
    private ArrayList<RainDrop> mRainDrops;
    private long mLastUpdateTime; //记录执行时间
    

    生成粒子对象

    • 生成雨滴是从顶部屏幕意外开始,而y = -50f值是雨滴的高度决定。
    • 圆圈是随机产生,在中心位置圆圈内。
    // 创建新的雨滴
    if (mRainDrops.size() < 80) {
        int num = getWidth() / padding;
        double nth = num * Math.random() * padding;
        double x = nth + padding / 2f * Math.random();
        RainDrop drop = new RainDrop((float) x, -50f);
        mRainDrops.add(drop);
    }
    
    // 创建新的粒子
    if (mParticles.size() < 100) {
        float x = (float) (getWidth() / 2f - radius + 2*radius * Math.random());
        float y = (float) (getHeight()/2f - radius + 2*radius * Math.random() );
    
        Circle particle = new Circle(x, y,5);
        mParticles.add(particle);
    }
    

    绘制雨滴

    雨滴的绘制非常简单,调用相应的canvas方法即可

    // 绘制雨滴
    mPaint.setColor(Color.WHITE);
    for (RainDrop drop : mRainDrops) {
        canvas.drawLine(drop.x, drop.y, drop.x, drop.y + 20, mPaint);
    }
    
    // 绘制粒子
    for (Circle particle : mParticles) {
        mPaint.setColor(particle.color);
        canvas.drawCircle(particle.x, particle.y, particle.radius, mPaint);
    }
    

    更新粒子位置

    雨滴的更新相对简单,但是圆圈的旋转是一个难点,一个重要的问题是如何旋转粒子的,其实有很多方法,其中最笨的方法是旋转Canvas坐标系,底层有很多矩阵计算,但是这个似乎使用Math.atan2(y,x)显然更加方便,我们只需要在当前角度加上偏移量就能旋转。

    float angle = (float) Math.atan2(dy, dx) + deltaTime * 0.65f;
    

    下面是完整的更新逻辑

    // 更新雨滴位置
    Iterator<RainDrop> rainIterator = mRainDrops.iterator();
    while (rainIterator.hasNext()) {
        RainDrop drop = rainIterator.next();
        if (drop.y > getHeight() + 50) {
            int num = getWidth() / padding;
            double nth = num * Math.random() * padding;
            double x = nth + padding * Math.random();
    
            drop.x = (float) (x);
            drop.y = -50;
        } else {
            drop.y += 20;
        }
    
    }
    
    // 更新粒子位置
    long currentTime = System.currentTimeMillis();
    float deltaTime = (currentTime - mLastUpdateTime) / 1000f;
    mLastUpdateTime = currentTime;
    
    float centerX = getWidth() / 2f;
    float centerY = getHeight() / 2f;
    
    Iterator<Circle> iterator = mParticles.iterator();
    while (iterator.hasNext()) {
        Circle particle = iterator.next();
        float dx = particle.x - centerX;
        float dy = particle.y - centerY;
        float distance = (float) Math.sqrt(dx * dx + dy * dy) + 4.5f;//  增加偏移
        float angle = (float) Math.atan2(dy, dx) + deltaTime * 0.5f;
        particle.radius += 1f;
    
        particle.x = centerX + (float) Math.cos(angle) * distance;
        particle.y = centerY + (float) Math.sin(angle) * distance;
    
        if (particle.radius > 10) {
            int maxRadius = 100;
            float fraction = (particle.radius - 10) / (maxRadius - 10);
            if (fraction >= 1) {
                fraction = 1;
            }
            particle.color = argb((int) (255 * (1 - fraction)), Color.red(particle.color), Color.green(particle.color), Color.blue(particle.color));
        }
        if (Color.alpha(particle.color) == 0) {
    
            float x = (float) (getWidth() / 2f - radius + 2* radius * Math.random());
            float y = (float) (getHeight()/2f - radius + 2*radius * Math.random() );
            particle.reset(x,y, 5);
        }
    
    }
    

    粒子刷新

    其实刷新机制我们以前经常使用,调用postInvalidate即可,本身就是View自身的方法。

    总结

    本篇主要内容总体上就是这些,下面是全部代码逻辑

    public class VortexView extends View {
    
        private Paint mPaint;
        private ArrayList<Circle> mParticles;
        private ArrayList<RainDrop> mRainDrops;
        private long mLastUpdateTime;
        private int padding = 20;
    
        public VortexView(Context context) {
            super(context);
            mPaint = new Paint();
            mParticles = new ArrayList<>();
            mRainDrops = new ArrayList<>();
            mLastUpdateTime = System.currentTimeMillis();
        }
    
        @Override
        protected void onDraw(Canvas canvas) {
            super.onDraw(canvas);
            float radius = Math.min(getWidth(), getHeight()) / 3f;
    
            // 创建新的雨滴
            if (mRainDrops.size() < 80) {
                int num = getWidth() / padding;
                double nth = num * Math.random() * padding;
                double x = nth + padding / 2f * Math.random();
                RainDrop drop = new RainDrop((float) x, -50f);
                mRainDrops.add(drop);
            }
    
            // 创建新的粒子
            if (mParticles.size() < 100) {
                float x = (float) (getWidth() / 2f - radius + 2*radius * Math.random());
                float y = (float) (getHeight()/2f - radius + 2*radius * Math.random() );
    
                Circle particle = new Circle(x, y,5);
                mParticles.add(particle);
            }
    
            // 绘制雨滴
            mPaint.setColor(Color.WHITE);
            for (RainDrop drop : mRainDrops) {
                canvas.drawLine(drop.x, drop.y, drop.x, drop.y + 20, mPaint);
            }
    
            // 绘制粒子
            for (Circle particle : mParticles) {
                mPaint.setColor(particle.color);
                canvas.drawCircle(particle.x, particle.y, particle.radius, mPaint);
            }
    
            // 更新雨滴位置
            Iterator<RainDrop> rainIterator = mRainDrops.iterator();
            while (rainIterator.hasNext()) {
                RainDrop drop = rainIterator.next();
                if (drop.y > getHeight() + 50) {
                    int num = getWidth() / padding;
                    double nth = num * Math.random() * padding;
                    double x = nth + padding * Math.random();
    
                    drop.x = (float) (x);
                    drop.y = -50;
                } else {
                    drop.y += 20;
                }
    
            }
    
            // 更新粒子位置
            long currentTime = System.currentTimeMillis();
            float deltaTime = (currentTime - mLastUpdateTime) / 1000f;
            mLastUpdateTime = currentTime;
    
            float centerX = getWidth() / 2f;
            float centerY = getHeight() / 2f;
    
            Iterator<Circle> iterator = mParticles.iterator();
            while (iterator.hasNext()) {
                Circle particle = iterator.next();
                float dx = particle.x - centerX;
                float dy = particle.y - centerY;
                float distance = (float) Math.sqrt(dx * dx + dy * dy) + 3.5f;//  增加偏移
                float angle = (float) Math.atan2(dy, dx) + deltaTime * 0.65f;
                particle.radius += 1f;
    
                particle.x = centerX + (float) Math.cos(angle) * distance;
                particle.y = centerY + (float) Math.sin(angle) * distance;
    
                if (particle.radius > 10) {
                    int maxRadius = 100;
                    float fraction = (particle.radius - 10) / (maxRadius - 10);
                    if (fraction >= 1) {
                        fraction = 1;
                    }
                    particle.color = argb((int) (255 * (1 - fraction)), Color.red(particle.color), Color.green(particle.color), Color.blue(particle.color));
                }
                if (Color.alpha(particle.color) == 0) {
    
                    float x = (float) (getWidth() / 2f - radius + 2* radius * Math.random());
                    float y = (float) (getHeight()/2f - radius + 2*radius * Math.random() );
                    particle.reset(x,y, 5);
                }
    
            }
    
            Collections.sort(mParticles, comparator);
    
            // 使view无效从而重新绘制,实现动画效果
            invalidate();
        }
        Comparator comparator = new Comparator<Circle>() {
            @Override
            public int compare(Circle left, Circle right) {
                return (int) (left.radius - right.radius);
            }
        };
    
        public static int argb(
                int alpha,
                int red,
                int green,
                int blue) {
            return (alpha << 24) | (red << 16) | (green << 8) | blue;
        }
    
        private static class Circle {
            float x;
            float y;
            int color;
    
            float radius;
    
            Circle(float x, float y, float radius) {
                reset(x, y, radius);
            }
    
            private void reset(float x, float y, float radius) {
                this.x = x;
                this.y = y;
                this.radius = radius;
                this.color = Color.rgb((int) (Math.random() * 256), (int) (Math.random() * 256), (int) (Math.random() * 256));
            }
        }
    
        private static class RainDrop {
            float x;
            float y;
    
            RainDrop(float x, float y) {
                this.x = x;
                this.y = y;
            }
        }
    }
    

    以上就是Android实现粒子漩涡动画的详细内容,更多关于Android粒子漩涡的资料请关注3672js教程其它相关文章!

    您可能感兴趣的文章:
    • Android粒子线条效果实现过程与代码
    • Android自定义UI之粒子效果
    • Android实现粒子雨效果
    • Android实现粒子爆炸效果的方法

    用户评论