您现在的位置是:亿华云 > 应用开发

HarmonyOS烟花特效组件开发

亿华云2025-10-02 21:28:40【应用开发】9人已围观

简介想了解更多内容,请访问:和华为官方合作共建的鸿蒙技术社区https://harmonyos.51cto.com前言之前看到“粒子消散”的特效组件,于是就产生想法(自己也弄个特效组件应该挺有意思的)。这

想了解更多内容,花特请访问:

和华为官方合作共建的效组鸿蒙技术社区

https://harmonyos.51cto.com

前言

之前看到“粒子消散”的特效组件,于是花特就产生想法(自己也弄个特效组件应该挺有意思的)。这个烟花特效可以添加到游戏胜利的效组界面中,可能还有其他应用场景哈哈~。花特这也算是效组我做的第一个组件原创demo啦。

概述

效果图如下:

有三种模式可以选择,花特一种是效组图案只有五角星,一种是花特图案只有三角形,还有一种是效组图案既有五角星又有三角星。颜色有10种,花特还有背景音乐(自己DIY的效组烟花音效)!话不多说,开整!

正文

1.创建一个空白的花特工程

DevEco Studio下载安装成功后,打开DevEco Studio,效组点击左上角的花特File,点击New,再选择New Project,选择Empty Ability,然后点击Next,给项目命名Framework,选择设备类型Phone,选择语言类型JS最后点击Finish。

代码删除的源码下载部分

在entry>src>main>js>default>pages.index>index.hml 文件里把以下代码删掉

<text class="title">         { {  $t(strings.hello) }} { {  title }}     </text> 

 在entry>src>main>js>default>pages.index>index.js 文件里把以下代码删掉

title:" "    onInit() {          this.title = this.$t(strings.world);     } 

 在entry>src>main>js>default>pages.index>index.css 文件里把container部分以下的代码删掉

2.布局设计

index.hml

该组件是画布组件,画布的大小是整个屏幕,而按钮是显示画布上方的,所以要添加个栈组件,依次放入画布组件和按钮组件。代码如下👇,注意这里画布组件添加了触摸事件touchstartfunc,下文会讲解这步。

<stack class="stack">       <canvas class="canvas " ref="canvas " @touchstart=touchstartfunc ></canvas>       <input type="button" class="STAR"  value="五角星" onclick="click_star"/>       <input type="button" class="TRIANGLE"  value="三角形" onclick="click_triangle"/>       <input type="button" class="MIX"  value="混合" onclick="click_mix"/>   </stack> 

index.css

给画布组件和按钮组件设置属性,代码如下👇

.canvas{      width:100%;     height: 100%;     background-color:black; } .STAR{      width: 80px;     height: 38px;     font-size: 20px;     background-color:blue;     border-color: blue;     text-color: aquamarine;     top:660px;     left: 40px; } .TRIANGLE{      width: 80px;     height: 38px;     font-size: 20px;     background-color:blue;     border-color: blue;     text-color: aquamarine;     top:660px;     left: 150px; } .MIX{      width: 80px;     height: 38px;     font-size: 20px;     background-color:blue;     border-color: blue;     text-color: aquamarine;     top:660px;     left: 260px; } 

此时打开模拟器,你就能得到上面效果图左1图,接下来做功能实现部分。

3.绘制图案

五角星

函数 draw_star 传的参数分别是烟花释放的圆心坐标,图案的颜色,图案移动的斜率,图案是否填充颜色。定义的变量中,x和y是图案中心的坐标,根据时间推移(会设定定时器,亿华云计算下文会讲)move_times增加,图案会沿着传进来的斜率方向作直线移动,以达到烟花绽放的效果。变量a-h都是为了便于绘制五角星的图案而设的公式参数值,全局变量r_star是五角星的边长,最后根据公式去绘制单个五角星图案

index.js

先在export default上方定义变量

var ctx; var move_times=1; var r_star = 5; var r_triangle=14;      

 然后在export default下方添加代码:

onShow() {         ctx = this.$refs.canvas.getContext(2d);    }, draw_Star(x_1,y_1,color,x_2,y_2,fill) {         let x = x_1 + move_times * x_2;        let y = y_1 + move_times * y_2;        let a = r_star * Math.sin(Math.PI / 10);        let b = r_star * Math.cos(Math.PI / 10);        let c = (r_star + a) * Math.tan(Math.PI / 10);        let d = (r_star + a) * Math.tan(Math.PI / 5) - c;        let e = r_star * Math.sin(Math.PI / 5);        let f = r_star * Math.cos(Math.PI / 5);        let g = (r_star + 2 * a) * Math.cos(2 * Math.PI / 5);        let h = (r_star + 2 * a) * Math.sin(2 * Math.PI / 5);        ctx.lineWidth=1;        ctx.beginPath();        ctx.moveTo(x - r_star - a, y - c);        ctx.lineTo(x - a, y - c);        ctx.lineTo(x, y - b - c);        ctx.lineTo(x + a, y - c);        ctx.lineTo(x + a + r_star, y - c);        ctx.lineTo(x + a + r_star - f, y + e - c);        ctx.lineTo(x + a + g, y + h - c);        ctx.lineTo(x, y + d);        ctx.lineTo(x - a - g, y + h - c);        ctx.lineTo(x - a - r_star + f, y + e - c);        ctx.closePath();        ctx.stroke();        move_times=move_times+1;    }, 

三角星

同样, draw_triangle 是绘制单个三角形并沿设定斜率移动的函数,函数的参数类型及作用与五角星的一致。全局变量r_triangle为三角形的边长,代码如下👇

draw_Triangle(x_1,y_1,color,x_2,y_2,fill){         let x = x_1 + move_times * x_2;        let y = y_1 + move_times * y_2;        ctx.lineWidth=1;        ctx.beginPath();        ctx.moveTo(x-r_triangle/2, y + Math.sqrt(3)*r_triangle/6);        ctx.lineTo(x, y - Math.sqrt(3)*r_triangle/3);        ctx.lineTo(x+r_triangle/2, y + Math.sqrt(3)*r_triangle/6);        ctx.closePath();        ctx.stroke();        move_times=move_times+1;    }, 

图案的美化

设置了10种颜色的颜色字典,通过绘制图案函数draw中的参数 color 去控制颜色(黑色是作保护作用),颜色可通过单独设置数字1-10来选择,也可以通过随机数(1~10)去随机变化颜色。颜色填充也是如此,1为不填充,2为填充,云服务器也可随机产生1或2来随机变化是否填充颜色。

var drawcolors=[0,1,2,3,4,5,6,7,8,9,10]; const COLORS={      "0":black,     "1":"#FF2E10",     "2":"#FB8D15",     "3":"#F4ED1C",     "4":"#C5F31D",     "5":"#51F11F",     "6":"#18F8F8",     "7":"#1166FF",     "8":"#9833DD",     "9":"#FC14EB",     "10":"#C64A6A" } 

draw函数中,在ctx.lineWidth下方,在ctx.beginPath上方添加代码:

ctx.strokeStyle = COLORS[drawcolors[color].toString()]; 

 在ctx.stroke下方添加代码

if(fill==2) {              ctx.fillStyle = COLORS[drawcolors[color].toString()];             ctx.fill();         }; 

draw开头的函数是绘制单个图案,接下来的Draw函数是绘制8个或10个图案围成圆形向外同速率扩展的图像,run开头的函数是被循环的函数

4.绘制烟花

烟花的布局

绽放的烟花的形状是一个圆形,火花为单个图案。我设计了两种烟花绽放数量,一种是一个圆中有8个图案的,一种是一个圆中有10个图案的,它们的斜率都通过数学公式定义好了(如下的全局变量所示),单个图案沿斜率方向每次移动的距离为全局变量R的数值。

var R = 0.25; var s= R*Math.cos(Math.PI/5); var t= R*Math.sin(Math.PI/5); var v= R*Math.cos(Math.PI/2.5); var w= R*Math.sin(Math.PI/2.5);  Draw_Star_8(click_x,click_y){         this.draw_Star(click_x,click_y,1,-R,0,Math.floor(Math.random()*2 + 1));        this.draw_Star(click_x,click_y,2,-R/Math.sqrt(2),-R/Math.sqrt(2),Math.floor(Math.random()*2 + 1));        this.draw_Star(click_x,click_y,3,0,-R,Math.floor(Math.random()*2 + 1));        this.draw_Star(click_x,click_y,4,R/Math.sqrt(2),-R/Math.sqrt(2),Math.floor(Math.random()*2 + 1));        this.draw_Star(click_x,click_y,5,R,0,Math.floor(Math.random()*2 + 1));        this.draw_Star(click_x,click_y,6,R/Math.sqrt(2),R/Math.sqrt(2),Math.floor(Math.random()*2 + 1));        this.draw_Star(click_x,click_y,7,0,R,Math.floor(Math.random()*2 + 1));        this.draw_Star(click_x,click_y,8,-R/Math.sqrt(2),R/Math.sqrt(2),Math.floor(Math.random()*2 + 1));    },    Draw_Star_10(click_x,click_y,fill){         this.draw_Star(click_x,click_y,Math.floor(Math.random()*10 + 1),-R,0,fill);        this.draw_Star(click_x,click_y,Math.floor(Math.random()*10 + 1),-s,-t,fill);        this.draw_Star(click_x,click_y,Math.floor(Math.random()*10 + 1),-v,-w,fill);        this.draw_Star(click_x,click_y,Math.floor(Math.random()*10 + 1),v,-w,fill);        this.draw_Star(click_x,click_y,Math.floor(Math.random()*10 + 1),s,-t,fill);        this.draw_Star(click_x,click_y,Math.floor(Math.random()*10 + 1),R,0,fill);        this.draw_Star(click_x,click_y,Math.floor(Math.random()*10 + 1),s,t,fill);        this.draw_Star(click_x,click_y,Math.floor(Math.random()*10 + 1),v,w,fill);        this.draw_Star(click_x,click_y,Math.floor(Math.random()*10 + 1),-v,w,fill);        this.draw_Star(click_x,click_y,Math.floor(Math.random()*10 + 1),-s,t,fill);    },     Draw_Triangle_8(click_x,click_y,fill){         this.draw_Triangle(click_x,click_y,Math.floor(Math.random()*10 + 1),-R,             0,              fill);        this.draw_Triangle(click_x,click_y,Math.floor(Math.random()*10 + 1),-R/Math.sqrt(2),-R/Math.sqrt(2),fill);        this.draw_Triangle(click_x,click_y,Math.floor(Math.random()*10 + 1), 0,            -R,              fill);        this.draw_Triangle(click_x,click_y,Math.floor(Math.random()*10 + 1), R/Math.sqrt(2),-R/Math.sqrt(2),fill);        this.draw_Triangle(click_x,click_y,Math.floor(Math.random()*10 + 1), R,             0,              fill);        this.draw_Triangle(click_x,click_y,Math.floor(Math.random()*10 + 1), R/Math.sqrt(2),R/Math.sqrt(2), fill);        this.draw_Triangle(click_x,click_y,Math.floor(Math.random()*10 + 1), 0,             R,              fill);        this.draw_Triangle(click_x,click_y,Math.floor(Math.random()*10 + 1),-R/Math.sqrt(2),R/Math.sqrt(2), fill);    },    Draw_Triangle_10(click_x,click_y){         this.draw_Triangle(click_x,click_y,1,-R,0, Math.floor(Math.random()*2 + 1));        this.draw_Triangle(click_x,click_y,2,-s,-t,Math.floor(Math.random()*2 + 1));        this.draw_Triangle(click_x,click_y,3,-v,-w,Math.floor(Math.random()*2 + 1));        this.draw_Triangle(click_x,click_y,4,v,-w, Math.floor(Math.random()*2 + 1));        this.draw_Triangle(click_x,click_y,5,s,-t, Math.floor(Math.random()*2 + 1));        this.draw_Triangle(click_x,click_y,6,R,0,  Math.floor(Math.random()*2 + 1));        this.draw_Triangle(click_x,click_y,7,s,t,  Math.floor(Math.random()*2 + 1));        this.draw_Triangle(click_x,click_y,8,v,w,  Math.floor(Math.random()*2 + 1));        this.draw_Triangle(click_x,click_y,9,-v,w, Math.floor(Math.random()*2 + 1));        this.draw_Triangle(click_x,click_y,10,-s,t,Math.floor(Math.random()*2 + 1));    }, 

火花的移动

上述提过一个movetimes,火花的移动无非就是坐标的变化,通过设置一个定时器,循环绘制图案就能实现移动的效果,先构造一个被循环的函数run_star(举五角星的实例,三角形同理;位置,美化等的参数随意),代码如下

run_star(){       this.Draw_Star_10(200,300,1);      this.Draw_Star_10(150,200,2);      this.Draw_Star_8(300,218);      this.Draw_Star_8(110,380);   }, 

 然后添加定时器

var timer_star=null;                    var timer_triangle=null;                 var timer_mix=null; 

 点击按钮“五角星”时会释放图案为五角星的烟花

click_star(){         timer_star=setInterval(this.run_star,120);    }, 

此时,打开模拟器,你会看到图案移动了,但上一个图案没有清除

所以要给被循环的函数添加一个清空操作(为了保护清空函数,要先在清空前加点东西),在this.Draw函数之前添加以下代码:

this.draw_Star(0,0,0,0,0,0);   ctx.clearRect(0,0,400,800); 

烟花的结束

按上述步骤下来,会发现烟花的圆形越来越大,最终出界。

为了实现烟花的结束,可以根据movetimes的增加次数来控制烟花绽放范围的大小。通过透明度的递减,最终透明度减为0,图案消失

var o = 1;    

 在draw函数里的开头位置添加以下代码:

if ((move_times >= 230 && move_times <= 330)) {            o = o - 0.01;           ctx.globalAlpha = o;       }; 

 烟花的循环绽放

在draw函数里的开头位置添加以下代码:

if(move_times==342){             o=1;            ctx.globalAlpha = o;            move_times=1;        }; 

 同理可以设置“三角形”和“混合”的被循环函数

run_triangle(){          this.draw_Triangle(0,0,0,0,0,0);         ctx.clearRect(0,0,400,800);         this.Draw_Triangle_8(200,300,1);         this.Draw_Triangle_8(150,200,2);         this.Draw_Triangle_10(300,218);         this.Draw_Triangle_10(110,380);     },  run_mix(){          this.draw_Triangle(0,0,0,0,0,0);         ctx.clearRect(0,0,400,800);         this.Draw_Triangle_8(200,300,1);         this.Draw_Star_10(150,200,2);         this.Draw_Triangle_10(300,218);         this.Draw_Star_8(110,380);     }, 

5.点击处绽放烟花

先获取点击处相对于画布组件左上角的坐标,然后作为新烟花绽放的圆心坐标传参,这里的click_b1,click_b2下文会讲解

var timer_click=null;  run_touch(){          if(click_b2==true) {              this.draw_Star(x, y, 0, 0, 0);                 ctx.clearRect(0, 0, 400, 800);             this.Draw_Star_10(x, y, 1);         }     },     touchstartfunc(msg) {          click_b1==true;         x=msg.touches[0].globalX;         y=msg.touches[0].globalY;         if(click_b1==true){          timer_click=setInterval(this.run_touch,120);             click_b1=false;             timer_click=null;         }     }, 

6.烟花图案的切换

通过设定布尔型变量来控制点击另一个按钮时,清空上一个按钮运行的定时器。

var star_b=true;                     var mix_b=true; var triangle_b=true;                var click_b1=true; var click_b2=true;  click_star(){          click_b2=false;         clearInterval(timer_triangle);         timer_triangle=null;         clearInterval(timer_mix);         timer_mix=null;         ctx.clearRect(0,0,400,800);         if(star_b==true){          timer_star=setInterval(this.run_star,120);         star_b=false;         }         triangle_b=true;         mix_b=true;     },     click_triangle(){          click_b2=false;         clearInterval(timer_star);         timer_star=null;         clearInterval(timer_mix);         timer_mix=null;         ctx.clearRect(0,0,400,800);         if(triangle_b==true){              timer_triangle=setInterval(this.run_triangle,120);             triangle_b=false;         }         star_b=true;         mix_b=true;     },     click_mix(){          click_b2=false;         clearInterval(timer_star);         timer_star=null;         clearInterval(timer_triangle);         timer_triangle=null;         ctx.clearRect(0,0,400,800);         if(mix_b==true){              timer_mix=setInterval(this.run_mix,120);             mix_b=false;         }         star_b=true;         triangle_b=true;     }, 

7.背景音乐的添加

js模板中添加音频可以去看我之前的文章。

index.hml

<video id=videoId          src=/common/flr_5_1.mp3          autoplay=true          controls="false"          onfinish=finishCallback></video> 

index.js

var video_b =true;    

在src/common/下加入音频文件。

finishCallback:function(){         if(video_b==true){             this.$element(videoId).start();        }    }, 

别忘了生命周期的设置,在应用启动时自动播放音频,在应用隐藏的时候暂停播放音频并清空所有定时器,在应用销毁时清空所有定时器,停止播放音频。

onShow() {       ctx = this.$refs.canvas.getContext(2d);      this.$element(videoId).start();  },  onHide(){   clearInterval(timer_star);  timer_star=null;  clearInterval(timer_triangle);  timer_triangle=null;  clearInterval(timer_mix);  timer_mix=null;  clearInterval(timer_click);  timer_click=null;  video_b=false;  this.$element(videoId).pause();  },  onDestroy(){       clearInterval(timer_star);      timer_star=null;      clearInterval(timer_triangle);      timer_triangle=null;      clearInterval(timer_mix);      timer_mix=null;      clearInterval(timer_click);      timer_click=null;      video_b=false;      this.$element(videoId).pause();  }, 

结语

以上就是我这次的小分享啦❀❀!自己的第一个demo开发,略微粗糙。

文章相关附件可以点击下面的原文链接前往下载:

https://harmonyos.51cto.com/resource/1376

想了解更多内容,请访问:

和华为官方合作共建的鸿蒙技术社区

https://harmonyos.51cto.com

【编辑推荐】

HarmonyOS 分布式之仿抖音应用 鸿蒙分布式1024一起玩专注游戏 鸿蒙小游戏-数字华容道自定义组件的踩坑记录 HarmonyOS 自定义View之图文标题 HarmonyOS 3.0.0开发者预览版全新发布

很赞哦!(1359)