
离线 TA的专栏
- 打卡等级:无名新人
- 打卡总天数:1
- 打卡月天数:0
- 打卡总奖励:19
- 最近打卡:2024-04-03 04:13:10
|
目录
- 前言
- 实现步骤
- 粒子对象定义
- 粒子更新
- 粒子绘制方法
- 粒子回收
- View逻辑
- 绘制逻辑
- 更新粒子
- 效果调节
- 总结
- 本篇代码
前言
粒子动画效果相比其他动画来说是非常复杂了的,主要涉及三个方面,粒子初始化、粒子位移、粒子回收等问题,其中特别是粒子位移是最复杂的,涉及到的数学逻辑非常多,主要是各种三角函数、物理学公式等。
本篇将实现两种动画效果,代码基本相同,只是旋转速度不一样,因此,本篇其实可以看作一篇模板文章,具体效果可以通过调节参数生成各种动画
第一种动画
第二种动画
实现步骤
其实和以往的粒子效果一样,粒子需要被管理起来,因此我们需要有容器、也需要粒子对象
粒子对象定义
下面是创建粒子对象的逻辑,基本属性在注释中了 - 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粒子中心扩散的资料请关注晓枫资讯其它相关文章!
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作! |
晓枫资讯-科技资讯社区-免责声明
免责声明:以上内容为本网站转自其它媒体,相关信息仅为传递更多信息之目的,不代表本网观点,亦不代表本网站赞同其观点或证实其内容的真实性。
1、注册用户在本社区发表、转载的任何作品仅代表其个人观点,不代表本社区认同其观点。
2、管理员及版主有权在不事先通知或不经作者准许的情况下删除其在本社区所发表的文章。
3、本社区的文章部分内容可能来源于网络,仅供大家学习与参考,如有侵权,举报反馈:  进行删除处理。
4、本社区一切资源不代表本站立场,并不代表本站赞同其观点和对其真实性负责。
5、以上声明内容的最终解释权归《晓枫资讯-科技资讯社区》所有。
|