OpenHarmony ArkUI+原生繪圖之幸運(yùn)大轉(zhuǎn)盤
51CTO和華為官方合作共建的鴻蒙技術(shù)社區(qū)
效果展示

此外,轉(zhuǎn)盤的獎(jiǎng)項(xiàng)的數(shù)量,內(nèi)容都是可以變動(dòng)的(菜單就是用來編輯獎(jiǎng)項(xiàng)的,后續(xù)完善),如下:

主要功能
- 實(shí)現(xiàn)轉(zhuǎn)盤抽獎(jiǎng)功能,可以設(shè)定中獎(jiǎng)概率。
- 獎(jiǎng)項(xiàng)的數(shù)量、內(nèi)容可自由設(shè)定。
- 原生html\css\js代碼,沒有使用資源文件,可復(fù)用。
設(shè)計(jì)時(shí)考慮到的問題
1.控件是使用現(xiàn)有圖片還是通過CSS畫出?
先是用的圖片充當(dāng)控件,考慮到獎(jiǎng)項(xiàng)的內(nèi)容可編輯性,還是老老實(shí)實(shí)畫控件比較好。
2.每個(gè)獎(jiǎng)項(xiàng)的概率如何設(shè)計(jì)?
先生成一個(gè)隨機(jī)數(shù),根據(jù)隨機(jī)數(shù)取值大小,決定獎(jiǎng)品內(nèi)容。假設(shè)所有獎(jiǎng)項(xiàng)的取值范圍坐落到0100的數(shù)軸上,并且1號(hào)獎(jiǎng)品的取值范圍是010,2號(hào):10~30, 3號(hào):30~35,。。。通過設(shè)定每個(gè)獎(jiǎng)項(xiàng)取值區(qū)間的大小來決定中獎(jiǎng)的權(quán)重,這樣就能控制中獎(jiǎng)概率了。
3.如何實(shí)現(xiàn)獎(jiǎng)項(xiàng)可編輯?
我將所有獎(jiǎng)項(xiàng)存放在一個(gè)數(shù)據(jù)數(shù)組中,先能通過遍歷數(shù)組中獎(jiǎng)項(xiàng)信息,畫出轉(zhuǎn)盤,這是第一步。
之后,通過菜單功能提供一個(gè)列表控件,使其能夠?qū)?shù)組中的信息進(jìn)行增刪改查,這是第二步。
在界面加載的onShow()函數(shù)中進(jìn)行初始化,這樣每次界面顯示的時(shí)候就能更新轉(zhuǎn)盤了。
具體代碼
index.hml
- <div class="container">
- <text class="title"> 幸運(yùn)大轉(zhuǎn)盤 </text>
- <div class="outer" id="outer">
- <!--畫布-->
- <canvas id="canvas" class="canvas"></canvas>
- <!--內(nèi)圓-->
- <div class="circle"></div>
- <!--長方形-->
- <div class="rectangle"></div>
- <!--正方形箭頭-->
- <div class="square"></div>
- </div>
- <div class="btns">
- <button class="button" type="capsule" onclick="start"> 抽獎(jiǎng) </button>
- <button class="button" type="capsule" onclick="menu"> 菜單 </button>
- </div>
- </div>
outer就是轉(zhuǎn)盤整體,包含轉(zhuǎn)盤和箭頭。我箭頭是通過將圓+長方形+正方形平移、旋轉(zhuǎn)組合而成的(雖然有點(diǎn)笨,沒有想到其它辦法)。轉(zhuǎn)盤是一個(gè)畫布canvas,通過移動(dòng)畫筆起點(diǎn),旋轉(zhuǎn),一個(gè)扇區(qū)接一個(gè)扇區(qū)畫出的。按鍵有兩個(gè),抽獎(jiǎng)就是轉(zhuǎn)動(dòng)轉(zhuǎn)盤,實(shí)現(xiàn)抽獎(jiǎng)邏輯。菜單按鍵跳轉(zhuǎn)到新的界面,實(shí)現(xiàn)獎(jiǎng)項(xiàng)內(nèi)容的編輯,當(dāng)然還沒寫完。。。
index.css
- .container {
- flex-direction: column;
- align-items: center;
- justify-content: space-between;
- }
- .title {
- font-size: 38px;
- font-weight: 600;
- height: 20%;
- }
- .outer {
- position: relative;
- }
- .canvas {
- width: 360px;
- height: 400px;
- }
- .circle {
- position: absolute;
- width: 40px;
- height: 40px;
- background-color: darkred;
- border-radius: 20px;
- transform: translate(160px,180px);
- }
- .rectangle {
- position: absolute;
- width: 20px;
- height: 40px;
- background-color: darkred;
- transform: translate(170px,150px);
- }
- .square {
- position:absolute;
- width: 20px;
- height: 20px;
- background-color: darkred;
- top: 140px;
- left: 170px;
- transform: rotate(45deg);
- }
- .btns {
- justify-content:space-around;
- }
- .button{
- margin-top: 10%;
- height: 10%;
- font-size: 30px;
- font-weight: 600;
- }
canvas中的寬、高決定了轉(zhuǎn)盤大小,代碼中將轉(zhuǎn)盤的半徑設(shè)置為畫布一半寬的長度。同時(shí),由于箭頭是由圓、長方形、正方形平移旋轉(zhuǎn)組成,那他們的偏移量、大小也是相對.canvas的屬性取的,如果大小有變動(dòng)需要調(diào)整。
為什么不將箭頭也畫出來?
如果將箭頭也畫在畫布上,那么我不能實(shí)現(xiàn)轉(zhuǎn)盤轉(zhuǎn)動(dòng),箭頭不動(dòng)的動(dòng)畫了,畫布是一個(gè)整體。
index.js
- import prompt from '@system.prompt';
- import router from '@system.router';
- export default {
- data: {
- //1.1創(chuàng)建獎(jiǎng)項(xiàng)信息
- infoArr: [
- { name: '1號(hào)獎(jiǎng)品' },
- { name: '2號(hào)獎(jiǎng)品' },
- { name: '3號(hào)獎(jiǎng)品' },
- { name: '4號(hào)獎(jiǎng)品' },
- { name: '5號(hào)獎(jiǎng)品' },
- { name: '6號(hào)獎(jiǎng)品' },
- { name: '7號(hào)獎(jiǎng)品' },
- { name: '未中獎(jiǎng)' },
- ],
- //1.2畫布大小
- circleHeight: 400,
- circleWidth: 360,
- //1.3扇區(qū)弧度
- arcAngle: 0,
- //1.4扇區(qū)角度
- jiaoDu: 0,
- //1.4動(dòng)畫參數(shù)
- animation: '',
- options: {
- duration: 5000,
- fill: 'forwards',
- easing: 'cubic-bezier(.2,.93,.43,1);',
- },
- },
- onShow() {
- const ca = this.$element('canvas');
- const ctx = ca.getContext('2d');
- //2.設(shè)定參數(shù)
- //2.1定義圓心,顯示在畫布中間
- var x0 = this.circleWidth * 0.5;
- var y0 = this.circleHeight * 0.5;
- //2.2定義半徑
- var radius = this.circleWidth * 0.5;
- //2.3扇形弧度
- this.arcAngle = 360 / this.infoArr.length * Math.PI / 180;
- //2.4扇區(qū)角度
- this.jiaoDu = 360 / this.infoArr.length;
- //2.5定義起始弧度,箭頭向上,初始度數(shù)需要-90deg
- var beginAngle = this.arcAngle * 0.5 - 90 * Math.PI / 180;
- //3.遍歷,繪制扇區(qū)
- for (var i = 0; i < this.infoArr.length; i++) {
- //3.1結(jié)束弧度
- var endAngle = beginAngle + this.arcAngle;
- //3.2開啟路徑
- ctx.beginPath();
- //3.3起點(diǎn)
- ctx.moveTo(x0, y0);
- //3.4繪制扇區(qū)
- ctx.arc(x0, y0, radius, beginAngle, endAngle);
- //3.5設(shè)置顏色
- if (i == this.infoArr.length - 1) {
- ctx.fillStyle = '#2f4f4f'; //未中獎(jiǎng)灰色
- } else if (i % 2) {
- ctx.fillStyle = '#ffa500';
- } else {
- ctx.fillStyle = '#ff4500';
- }
- //3.6填充顏色
- ctx.fill();
- //4.繪制文字
- //4.1文字弧度
- var textAngle = beginAngle + this.arcAngle * 0.5;
- var text = this.infoArr[i].name;
- //4.2文字坐標(biāo)
- var textX = x0 + (radius * 2 / 3) * Math.cos(textAngle);
- var textY = y0 + (radius * 2 / 3) * Math.sin(textAngle);
- //4.3平移畫布起點(diǎn)到文字位置
- ctx.translate(textX, textY);
- //4.4旋轉(zhuǎn)畫布
- ctx.rotate((this.jiaoDu * (i + 1) - 90) * Math.PI / 180);
- //4.5設(shè)置文字字號(hào)和字體
- ctx.font = "25px '微軟雅黑'";
- //4.6文字居中對齊
- ctx.textAlign = 'center';
- ctx.textBaseline = 'middle';
- //4.7繪制文字
- ctx.strokeText(text, 0, 0);
- //4.8還原旋轉(zhuǎn)、平移,方便下次旋轉(zhuǎn)
- ctx.rotate(-(this.jiaoDu * (i + 1) - 90) * Math.PI / 180);
- ctx.translate(-textX, -textY);
- //5.更新起始弧度, 將當(dāng)前扇形的結(jié)束弧度作為下一個(gè)扇形的起始弧度
- beginAngle = endAngle;
- }
- },
- start: function () {
- //6.旋轉(zhuǎn)事件
- //6.1獎(jiǎng)品總數(shù)
- let count = this.infoArr.length;
- //6.2生成隨機(jī)數(shù)
- let randomNum = Math.floor(Math.random() * count);
- //6.3轉(zhuǎn)動(dòng)角度(+ 360*3)
- let deg = randomNum * this.jiaoDu + 360 * 3 + "deg";
- //6.4獎(jiǎng)品名
- let index = count - randomNum - 1;
- let name = this.infoArr[index].name;
- console.log("name == " + name);
- //6.5動(dòng)畫幀
- var frames = [
- {
- transform: {
- rotate: '0deg'
- },
- },
- {
- transform: {
- rotate: deg
- },
- }
- ];
- //6.5動(dòng)畫綁定
- this.animation = this.$element('canvas').animate(frames, this.options);
- //6.6添加完成事件
- this.animation.onfinish = function () {
- if (randomNum % count) {
- prompt.showDialog({
- message: "恭喜抽中" + name + "!"
- });
- } else {
- prompt.showDialog({
- message: "下次再來!"
- });
- }
- };
- //6.7調(diào)用播放開始的方法
- this.animation.play();
- },
- menu: function () {
- router.push ({
- uri: 'pages/menuPage/menuPage',
- });
- },
- }
js中存放主要邏輯,所以對注釋也比較詳細(xì)。下面是個(gè)人踩坑中學(xué)習(xí)的點(diǎn):
- //1.1創(chuàng)建獎(jiǎng)項(xiàng)信息
- 可以增加減少獎(jiǎng)項(xiàng)來預(yù)覽將要實(shí)現(xiàn)的菜單功能,不要搞事情哈,獎(jiǎng)項(xiàng)至少為1,代碼中沒有除0保護(hù)。
- //1.4動(dòng)畫參數(shù)
- duration是時(shí)長。easing,是描述動(dòng)畫的時(shí)間曲線,實(shí)現(xiàn)動(dòng)畫由快變慢。fill:forwards在動(dòng)畫結(jié)束后,目標(biāo)將保留動(dòng)畫結(jié)束時(shí)的狀態(tài)。
- //3.4繪制扇區(qū)
- x0, y0,扇區(qū)的起點(diǎn)坐標(biāo)。radius,扇區(qū)半徑。beginAngle,扇區(qū)起始的弧度,endAngle,扇區(qū)結(jié)束的弧度。
- //3.5設(shè)置顏色
- 每個(gè)扇區(qū)設(shè)置兩個(gè)相間的顏色,未中獎(jiǎng)特殊扇區(qū)用灰色調(diào)標(biāo)識(shí)。
- //4.2文字坐標(biāo)
- 由于文字在扇區(qū)中間,所以需要利用正弦余弦計(jì)算坐標(biāo),再進(jìn)行畫面旋轉(zhuǎn),才能調(diào)整正確的文字方向。
- //4.8還原旋轉(zhuǎn)、平移,方便下次旋轉(zhuǎn)
- translate函數(shù)是基于當(dāng)前坐標(biāo)進(jìn)行偏移,旋轉(zhuǎn)也是基于當(dāng)前坐標(biāo)進(jìn)行旋轉(zhuǎn)。所以當(dāng)一個(gè)扇區(qū)的文字填寫結(jié)束后,需要將坐標(biāo)還原,這樣才方便定位到一下處扇區(qū)位置。
51CTO和華為官方合作共建的鴻蒙技術(shù)社區(qū)