Loading...
首页专栏正文

鸿蒙粒子破碎效果组件,已开源!

把酒临风 发布于 2021-07-20 13:59:48 浏览 2459 点赞 96 收藏 0

基于安卓平台的粒子破碎效果组件 Azexplosion, 实现了鸿蒙的功能化迁移和重构,代码已开源,欢迎各位开发者下载使用并提出宝贵意见!

开源代码: https://gitee.com/isrc_ohos/azexplosion_ohos

Azexplosion_ohos 是一个实现粒子破碎动画效果的组件,用户可以通过点击手机屏幕上的破碎对象(一般是指手机屏幕上显示的图片或文字),来达到将该对象破碎的效果。

该组件可以设置手机屏幕上的对象是否具有破碎效果,同时也可以更换破碎对象和破碎对象的背景。

Azexplosion_ohos 组件视觉效果突出、使用方便、可扩展性强,与小米手机删除 APP 时的动态效果类似。

组件效果展示

组件应用仅包含一个主界面,在界面中存在图片和文字两种破碎对象。

当手指触碰图片(或文字)时,该图片(或文字)在视觉上呈现破碎效果,且破碎粒子的颜色与原图片(或文字)的颜色相对应。

如图 1 所示,效果看起来很解压,非常好用:

Sample 解析

Azexplosion_ohos 组件的核心功能主要被封装在 Library 中,Sample 的功能实现很简洁,只需要构建整体的布局,并调用 Library 提供的监听接口为整体显示布局设置监听,即可实现效果执行的对象的破碎效果。

具体的实现步骤如下:

步骤 1:创建布局。

步骤 2:设置整体显示布局。

步骤 3:导入相关类并实例化对象。

步骤 4:为整体显示布局设置监听。

接下来我们来看一下每一个步骤涉及的详细操作。

①创建布局

首先在 XML 文件中创建一个 DirectionalLayout 的布局,宽度和高度都跟随父控件变化而调整。

后在 DirectionalLayout 加入需要的破碎对象,可以是图片或文字,如图 1 所示,代码如下所示: <DirectionalLayout xmlns:ohos="http://schemas.huawei.com/res/ohos" ohos:id="$+id:root" ohos:width="match_parent" ohos:height="match_parent" ohos:orientation="vertical"> <Text ohos:width="match_content" //文字破碎对象 ohos:text="破碎效果"
ohos:text_size="60vp" ohos:top_margin="10vp" ohos:left_margin="20vp" ohos:bottom_margin="15vp" ohos:right_padding="10vp" ohos:left_padding="40vp" ohos:height="match_content"/>

<DirectionalLayout ohos:id="$+id:group1"
        ohos:width="match_parent"
        ohos:height="100vp"
        ohos:top_margin="10vp"
        ohos:orientation="horizontal">

    <Image                           //图片破碎对象
        ohos:id="$+id:qq"
        ohos:width="match_content"
        ohos:height="match_content"
        ohos:image_src="$media:qq"
        ohos:left_margin="25vp"
        ohos:right_margin="25vp"
        ohos:top_margin="15vp"/>
    ......

②设置整体显示布局

在 MainAbility 文件的 onStart() 方法中,通过 setUIContent 为应用设置整体显示布局,将步骤(1)中的布局设置为应用的主界面布局。

为了显示的美观性,可以通过 setBackground 设置主界面的背景颜色:

//directionalLayout 指向步骤(1)中的布局 DirectionalLayout directionalLayout = (DirectionalLayout) LayoutScatter.getInstance(this).parse(ResourceTable.Layout_mian_activity, null, false); ShapeElement element = new ShapeElement();
element.setRgbColor(new RgbColor(255,239,213)); directionalLayout.setBackground(element); //背景颜色设置 super.setUIContent(directionalLayout); //设置显示布局

③导入相关类并实例化对象

在 MainAbility 中,通过 import 关键字导入 Library 中的 ExplosionField 类,并在 onStart() 方法中实例化 ExplosionField 类对象。

ExplosionField explosionField = new ExplosionField(this);

④为整体显示布局设置监听

调用 ExplosionField 类对象的内部方法 addListener,用于为整体显示布局设置监听。

整体显示布局设置监听后,用户点击布局内某个组件,组件就会出现破碎现象。

explosionField.addListener((ComponentContainer)findComponentById(ResourceTable.Id_root));

Library 解析

在 Sample 中介绍了为整体显示布局设置监听后,点击布局内的组件就会出现破碎现象。本节,我们来讲组件破碎现象形成的详细原理。

先来看看 Azexplosion_ohos 组件的 Library 组成结构,如图 2 所示,该部分主要由三个类组成,分别是 ExplosionAnimator、ExplosionField、Particle。

ExplosionAnimator 主要用于生成粒子并执行粒子破碎动画,改变不同时刻的粒子状态;ExplosionField 主要功能负责粒子集的画布显示;Particle 类主要用于描述粒子的颜色、透明度等属性。

下面我们介绍 Library 内部逻辑的执行步骤。当用户点击破碎对象后,Library 负责生成破碎对象对应的矩阵图像(PixelMap),然后把矩阵图像分解成若干个粒子,最后再让粒子动起来形成破碎的动画。

具体流程如图 3 所示:

①图像或文字转换成 PixelMap

为整体显示布局添加 listener 后,会通过 for 循环的方式,为每一个布局内的组件添加点击监听。

在 getOnClickListener() 方法中,会继续调用 ExplosionField 类 createBitmapFromView() 方法,而 createBitmapFromView() 方法就是完成图像或文字转换成 PixelMap(位图)的关键方法。

在 createBitmapFromView() 方法中首先会创建一个 100*100 的空的 PixelMap。

若破碎对象为图片,则通过调用 ExplosionField 类的 getPixelMap() 方法获取 Image 的 PixelMap;若破碎对象为文字,则直接返回空的 PixelMap。

此时,图片的 PixelMap 自带图片原本的像素信息,而由于文字得到的 PixelMap 是空的,所以默认显示黑色的破碎效果。

//为每一个布局内的组件添加点击监听 public void addListener(Component view) { if (view instanceof ComponentContainer) { ComponentContainer viewGroup = (ComponentContainer) view; int count = viewGroup.getChildCount(); // 逐个取出布局内的破碎对象 for (int i = 0 ; i < count; i++) { addListener(viewGroup.getComponentAt(i)); } } else { //为每一个破碎对象设置点击监听 view.setClickable(true); view.setClickedListener(getOnClickListener()); } }

//将每一个破碎对象转换为PixelMap private PixelMap createBitmapFromView(Component view) { //PixelMap参数初始化操作 PixelMap.InitializationOptions options = new PixelMap.InitializationOptions(); options.size = new Size(100,100); //创建位图对象 PixelMap = PixelMap.create(options); if(view.getName().equals("Id_qq")){ bitmap =getPixelMap(ResourceTable.Media_qq); //qq的PixelMap } if(view.getName().equals("Id_qzone")) bitmap =getPixelMap(ResourceTable.Media_qzone); //qzone的PixelMap if(view.getName().equals("Id_vx")) bitmap =getPixelMap(ResourceTable.Media_vx); //微信的PixelMap ......
return bitmap; //将获取的PixelMap返回 }

②生成破碎粒子

生成破碎粒子是 ExplosionAnimator 的功能之一,主要是对来自 ExplosionField 类的 PixelMap 进行处理。

首先根据 PixelMap 的宽高,算出横竖粒子的个数。然后计算出粒子所在位置的颜色。

接着调用 Particle 类的 generateParticle() 方法生成粒子:

//生成粒子 private Particle[][] generateParticles(PixelMap bitmap, Rect bound) { int w = bound.getWidth(); //PixelMap的宽 int h = bound.getHeight(); // PixelMap的高 int partW_Count = w / Particle.PART_WH; //横向粒子个数 int partH_Count = h / Particle.PART_WH; //竖向粒子个数 //粒子的宽 int bitmap_part_w = bitmap.getImageInfo().size.width / partW_Count; //粒子的高 int bitmap_part_h = bitmap.getImageInfo().size.height / 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.readPixel(new Position(column bitmap_part_w, row bitmap_part_h)); point = new Point(column, row); //x是列,y是行 particles[row][column] = Particle.generateParticle(color, bound, point); } } return particles; //返回粒子矩阵 }

生成的破碎粒子如图 4 所示:

图 4:破碎粒子效果图

③执行破碎动画

接下来我们需要为粒子加上动画,让它们动起来,实现一个完整的动态效果。

动画效果的实现需要依赖 ExplosionAnimator 类,ExplosionAnimator 类继承自 AnimatorValue 类,可用于绘制动画。

下面我们具体分析动画效果是如何实现的:

(1)创建 ExplosionAnimator 类对象

在创建 ExplosionAnimator 类对象的过程中,将被点击的破碎对象的 PixelMap 作为参数传入,得到 ExplosionAnimator 类对象的成员变量包含上述 PixelMap 生成的粒子集。

//创建元素为列表ExplosionAnimator类对象的数组列表 private ArrayList explosionAnimators; explosionAnimators = new ArrayList();
//创建ExplosionAnimator 类对象 final ExplosionAnimator animator = new ExplosionAnimator(this, createBitmapFromView(view), rect);
//ExplosionAnimator 类对象添加到列表中 explosionAnimators.add(animator);

(2)start()

当监听器监听到屏幕被触碰时,通过(1)中创建的 ExplosionAnimator 类对象调用 start() 方法,通过 invalidate() 来刷新将要破碎的图片所对应的区块,invalidate() 方法会调用 onDraw() 方法进行动画绘制。

public void start() { super.start(); mContainer.invalidate(); }

(3)onDraw()

在 onDraw() 方法里,首先保存画布的绘制状态并修正因为状态栏导致的错位,然后循环调用 ExplosionAnimator 的 draw() 方法。

public void onDraw(Component component, Canvas canvas) { canvas.save(); // 保存画布的绘制状态 canvas.translate(0,positions[1]); //修正因为状态栏导致的错位 for (ExplosionAnimator animator : explosionAnimators) { animator.draw(canvas); } canvas.restore(); }

(4)draw()

在 draw 方法中,每次绘制都调用一次 advance() 方法让粒子“前进一步”(逐渐向下扩散),然后设置画笔的新属性并重新绘制。

public void draw(Canvas canvas) { //动画结束时停止 if(!isRunning()) { return; } for (Particle[] particle : mParticles) { for (Particle p : particle) { p.advance(myvalue); mPaint.setColor(new Color(p.color)); //只是这样设置,透明色会显示为黑色 mPaint.setAlpha((int) (p.alpha)); canvas.drawCircle(p.cx, p.cy, p.radius, mPaint); } } mContainer.invalidate(); }

最后,我们总结一下整体的动画绘制的过程是如何实现的。

首先在 ExplosionField 中调用 ExplosionAnimator 的 start() 方法开启动画,start() 方法中会调用 invalidate() 方法来使 ExplosionField 重绘(调用 onDraw 方法)。

onDraw 方法调用 draw 方法,draw 方法中也使用 invalidate 强制 ExplosionField 重绘(调用 onDraw 方法),每一次循环完成一次重绘。

这样两者相互调用,不停地刷新,直到所有粒子都绘制完成,刷新停止,动画绘制流程如图 5 所示:

项目贡献人:胡鹏达、郑森文、朱伟、陈美汝、蔡志杰、李珂

*本文仅代表作者观点,不代表易百纳技术社区立场。系作者授权易百纳技术社区发表,未经许可不得转载。

精彩评论

内容存在敏感词
打赏
打赏作者
把酒临风
您的支持将鼓励我继续创作!
金额:
¥1 ¥5 ¥10 ¥50 ¥100
支付方式:
微信支付
支付宝支付
微信支付
打赏成功!

感谢您的打赏,如若您也想被打赏,可前往 发表专栏 哦~

易百纳技术社区
确定要删除此文章、专栏、评论吗?
确定
取消
易百纳技术社区