Android实现粒子中心扩散动画效果

 更新时间:2024年02月25日 08:37:23   作者:时光少年  
粒子动画效果相比其他动画来说是非常复杂了的,主要涉及三个方面,粒子初始化、粒子位移、粒子回收等问题,本篇将实现两种动画效果,代码基本相同,只是旋转速度不一样,需要的朋友可以参考下
(福利推荐:【腾讯云】服务器最新限时优惠活动,云服务器1核2G仅99元/年、2核4G仅768元/3年,立即抢购>>>:9i0i.cn/qcloud

(福利推荐:你还在原价购买阿里云服务器?现在阿里云0.8折限时抢购活动来啦!4核8G企业云服务器仅2998元/3年,立即抢购>>>:9i0i.cn/aliyun

前言

粒子动画效果相比其他动画来说是非常复杂了的,主要涉及三个方面,粒子初始化、粒子位移、粒子回收等问题,其中特别是粒子位移是最复杂的,涉及到的数学逻辑非常多,主要是各种三角函数、物理学公式等。

本篇将实现两种动画效果,代码基本相同,只是旋转速度不一样,因此,本篇其实可以看作一篇模板文章,具体效果可以通过调节参数生成各种动画

第一种动画

第二种动画

实现步骤

其实和以往的粒子效果一样,粒子需要被管理起来,因此我们需要有容器、也需要粒子对象

粒子对象定义

下面是创建粒子对象的逻辑,基本属性在注释中了

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));
        }
    }

}

以上就是Android实现粒子中心扩散动画效果的详细内容,更多关于Android粒子中心扩散的资料请关注程序员之家其它相关文章!

相关文章

  • Android EditText长按菜单中分享功能的隐藏方法

    Android EditText长按菜单中分享功能的隐藏方法

    Android EditText控件是经常使用的控件,下面这篇文章主要给大家介绍了关于Android中EditText长按菜单中分享功能的隐藏方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面来一起学习学习吧
    2019-02-02
  • android RadioButton和CheckBox组件的使用方法

    android RadioButton和CheckBox组件的使用方法

    本次实验中主要是学习如何使用RadioGroup,CheckBox,RadioButton和Toast这几个控件,android UI开发中也会经常用到他们
    2013-11-11
  • Android ListView控件使用方法

    Android ListView控件使用方法

    这篇文章主要为大家详细介绍了Android ListView控件的使用方法,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2017-11-11
  • Android实现Tab切换界面功能详解

    Android实现Tab切换界面功能详解

    这篇文章主要为大家详细介绍了Android如何实现Tab切换界面的功能,以及对Tab变化事件进行监听。文中示例代码讲解详细,感兴趣的可以了解一下
    2022-05-05
  • Android 自定义弹性ListView控件实例代码(三种方法)

    Android 自定义弹性ListView控件实例代码(三种方法)

    关于在Android中实现ListView的弹性效果,有很多不同的方法,网上一搜,也有很多,下面贴出在项目中经常用到的两种实现ListView弹性效果的方法(基本上拿来就可以用),需要的朋友参考下本段代码
    2016-01-01
  • Android中webview与JS交互、互调方法实例详解

    Android中webview与JS交互、互调方法实例详解

    这篇文章主要介绍了Android中webview与JS交互、互调方法实例详解的相关资料,需要的朋友可以参考下
    2017-03-03
  • Android解析JSON格式数据的两种方式(JSONObject和Gson)

    Android解析JSON格式数据的两种方式(JSONObject和Gson)

    json数据的解析相对而言,还是比较容易的,实现的代码也十分简单,下面这篇文章主要给大家介绍了关于Android解析JSON格式数据的两种方式,文中通过实例代码介绍的非常详细,需要的朋友可以参考下
    2022-07-07
  • Android开发之StackView用法和遇到的坑分析

    Android开发之StackView用法和遇到的坑分析

    这篇文章主要介绍了Android开发之StackView用法和遇到的坑,结合实例形式分析了Android StackView图片操作用法及常见问题解决方法,需要的朋友可以参考下
    2019-03-03
  • Android如何实现时间线效果

    Android如何实现时间线效果

    这篇文章主要介绍了?Android如何实现时间线效果,下面文章围绕?Android如何实现时间线效果的相关资料展开详细内容,具有一定的参考价值?,需要的朋友可以参考一下
    2021-11-11
  • Android开发中Eclipse报错及对应处理方法总结

    Android开发中Eclipse报错及对应处理方法总结

    这篇文章主要介绍了Android开发中Eclipse报错及对应处理方法,实例汇总了使用eclipse开发Android项目过程中常见的错误提示及对应的处理技巧,需要的朋友可以参考下
    2015-12-12

最新评论

?


http://www.vxiaotou.com