HarmonyOS煙花特效組件開發(fā)
51CTO和華為官方合作共建的鴻蒙技術(shù)社區(qū)
前言
之前看到“粒子消散”的特效組件,于是就產(chǎn)生想法(自己也弄個(gè)特效組件應(yīng)該挺有意思的)。這個(gè)煙花特效可以添加到游戲勝利的界面中,可能還有其他應(yīng)用場景哈哈~。這也算是我做的第一個(gè)組件原創(chuàng)demo啦。
概述
效果圖如下:
有三種模式可以選擇,一種是圖案只有五角星,一種是圖案只有三角形,還有一種是圖案既有五角星又有三角星。顏色有10種,還有背景音樂(自己DIY的煙花音效)!話不多說,開整!
正文
1.創(chuàng)建一個(gè)空白的工程
DevEco Studio下載安裝成功后,打開DevEco Studio,點(diǎn)擊左上角的File,點(diǎn)擊New,再選擇New Project,選擇Empty Ability,然后點(diǎn)擊Next,給項(xiàng)目命名Framework,選擇設(shè)備類型Phone,選擇語言類型JS最后點(diǎn)擊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.布局設(shè)計(jì)
index.hml
該組件是畫布組件,畫布的大小是整個(gè)屏幕,而按鈕是顯示畫布上方的,所以要添加個(gè)棧組件,依次放入畫布組件和按鈕組件。代碼如下👇,注意這里畫布組件添加了觸摸事件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
給畫布組件和按鈕組件設(shè)置屬性,代碼如下👇
- .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;
- }
此時(shí)打開模擬器,你就能得到上面效果圖左1圖,接下來做功能實(shí)現(xiàn)部分。
3.繪制圖案
五角星
函數(shù) draw_star 傳的參數(shù)分別是煙花釋放的圓心坐標(biāo),圖案的顏色,圖案移動的斜率,圖案是否填充顏色。定義的變量中,x和y是圖案中心的坐標(biāo),根據(jù)時(shí)間推移(會設(shè)定定時(shí)器,下文會講)move_times增加,圖案會沿著傳進(jìn)來的斜率方向作直線移動,以達(dá)到煙花綻放的效果。變量a-h都是為了便于繪制五角星的圖案而設(shè)的公式參數(shù)值,全局變量r_star是五角星的邊長,最后根據(jù)公式去繪制單個(gè)五角星圖案
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 是繪制單個(gè)三角形并沿設(shè)定斜率移動的函數(shù),函數(shù)的參數(shù)類型及作用與五角星的一致。全局變量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;
- },
圖案的美化
設(shè)置了10種顏色的顏色字典,通過繪制圖案函數(shù)draw中的參數(shù) color 去控制顏色(黑色是作保護(hù)作用),顏色可通過單獨(dú)設(shè)置數(shù)字1-10來選擇,也可以通過隨機(jī)數(shù)(1~10)去隨機(jī)變化顏色。顏色填充也是如此,1為不填充,2為填充,也可隨機(jī)產(chǎn)生1或2來隨機(jī)變化是否填充顏色。
- 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函數(shù)中,在ctx.lineWidth下方,在ctx.beginPath上方添加代碼:
- ctx.strokeStyle = COLORS[drawcolors[color].toString()];
在ctx.stroke下方添加代碼
- if(fill==2) {
- ctx.fillStyle = COLORS[drawcolors[color].toString()];
- ctx.fill();
- };
draw開頭的函數(shù)是繪制單個(gè)圖案,接下來的Draw函數(shù)是繪制8個(gè)或10個(gè)圖案圍成圓形向外同速率擴(kuò)展的圖像,run開頭的函數(shù)是被循環(huán)的函數(shù)
4.繪制煙花
煙花的布局
綻放的煙花的形狀是一個(gè)圓形,火花為單個(gè)圖案。我設(shè)計(jì)了兩種煙花綻放數(shù)量,一種是一個(gè)圓中有8個(gè)圖案的,一種是一個(gè)圓中有10個(gè)圖案的,它們的斜率都通過數(shù)學(xué)公式定義好了(如下的全局變量所示),單個(gè)圖案沿斜率方向每次移動的距離為全局變量R的數(shù)值。
- 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));
- },
火花的移動
上述提過一個(gè)movetimes,火花的移動無非就是坐標(biāo)的變化,通過設(shè)置一個(gè)定時(shí)器,循環(huán)繪制圖案就能實(shí)現(xiàn)移動的效果,先構(gòu)造一個(gè)被循環(huán)的函數(shù)run_star(舉五角星的實(shí)例,三角形同理;位置,美化等的參數(shù)隨意),代碼如下
- 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);
- },
然后添加定時(shí)器
- var timer_star=null;
- var timer_triangle=null;
- var timer_mix=null;
點(diǎn)擊按鈕“五角星”時(shí)會釋放圖案為五角星的煙花
- click_star(){
- timer_star=setInterval(this.run_star,120);
- },
此時(shí),打開模擬器,你會看到圖案移動了,但上一個(gè)圖案沒有清除

所以要給被循環(huán)的函數(shù)添加一個(gè)清空操作(為了保護(hù)清空函數(shù),要先在清空前加點(diǎn)東西),在this.Draw函數(shù)之前添加以下代碼:
- this.draw_Star(0,0,0,0,0,0);
- ctx.clearRect(0,0,400,800);
煙花的結(jié)束
按上述步驟下來,會發(fā)現(xiàn)煙花的圓形越來越大,最終出界。
為了實(shí)現(xiàn)煙花的結(jié)束,可以根據(jù)movetimes的增加次數(shù)來控制煙花綻放范圍的大小。通過透明度的遞減,最終透明度減為0,圖案消失
- var o = 1;
在draw函數(shù)里的開頭位置添加以下代碼:
- if ((move_times >= 230 && move_times <= 330)) {
- o = o - 0.01;
- ctx.globalAlpha = o;
- };
煙花的循環(huán)綻放
在draw函數(shù)里的開頭位置添加以下代碼:
- if(move_times==342){
- o=1;
- ctx.globalAlpha = o;
- move_times=1;
- };
同理可以設(shè)置“三角形”和“混合”的被循環(huán)函數(shù)
- 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.點(diǎn)擊處綻放煙花
先獲取點(diǎn)擊處相對于畫布組件左上角的坐標(biāo),然后作為新煙花綻放的圓心坐標(biāo)傳參,這里的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.煙花圖案的切換
通過設(shè)定布爾型變量來控制點(diǎn)擊另一個(gè)按鈕時(shí),清空上一個(gè)按鈕運(yùn)行的定時(shí)器。
- 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();
- }
- },
別忘了生命周期的設(shè)置,在應(yīng)用啟動時(shí)自動播放音頻,在應(yīng)用隱藏的時(shí)候暫停播放音頻并清空所有定時(shí)器,在應(yīng)用銷毀時(shí)清空所有定時(shí)器,停止播放音頻。
- 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();
- },
結(jié)語
以上就是我這次的小分享啦❀❀!自己的第一個(gè)demo開發(fā),略微粗糙。
文章相關(guān)附件可以點(diǎn)擊下面的原文鏈接前往下載:
https://harmonyos.51cto.com/resource/1376
51CTO和華為官方合作共建的鴻蒙技術(shù)社區(qū)
【編輯推薦】