效果展示

此外,转盘的奖项的数量,内容都是可以变动的(菜单就是用来编辑奖项的,后续完善),如下:

主要功能
- 实现转盘抽奖功能,可以设定中奖概率。
- 奖项的数量、内容可自由设定。
- 原生html\css\js代码,没有使用资源文件,可复用。
设计时考虑到的问题
1.控件是使用现有图片还是通过CSS画出?
先是用的图片充当控件,考虑到奖项的内容可编辑性,还是老老实实画控件比较好。
2.每个奖项的概率如何设计?
先生成一个随机数,根据随机数取值大小,决定奖品内容。假设所有奖项的取值范围坐落到0100的数轴上,并且1号奖品的取值范围是010,2号:10~30, 3号:30~35,。。。通过设定每个奖项取值区间的大小来决定中奖的权重,这样就能控制中奖概率了。
3.如何实现奖项可编辑?
我将所有奖项存放在一个数据数组中,先能通过遍历数组中奖项信息,画出转盘,这是第一步。
之后,通过菜单功能提供一个列表控件,使其能够对数组中的信息进行增删改查,这是第二步。
在界面加载的onShow()函数中进行初始化,这样每次界面显示的时候就能更新转盘了。
具体代码
index.hml
<div class="container">
<text class="title"> 幸运大转盘 </text>
<div class="outer" id="outer">
<!--画布-->
<canvas id="canvas" class="canvas"></canvas>
<!--内圆-->
<div class="circle"></div>
<!--长方形-->
<div class="rectangle"></div>
<!--正方形箭头-->
<div class="square"></div>
</div>
<div class="btns">
<button class="button" type="capsule" onclick="start"> 抽奖 </button>
<button class="button" type="capsule" onclick="menu"> 菜单 </button>
</div>
</div>
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
outer就是转盘整体,包含转盘和箭头。我箭头是通过将圆+长方形+正方形平移、旋转组合而成的(虽然有点笨,没有想到其它办法)。转盘是一个画布canvas,通过移动画笔起点,旋转,一个扇区接一个扇区画出的。按键有两个,抽奖就是转动转盘,实现抽奖逻辑。菜单按键跳转到新的界面,实现奖项内容的编辑,当然还没写完。。。
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;
}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
- 22.
- 23.
- 24.
- 25.
- 26.
- 27.
- 28.
- 29.
- 30.
- 31.
- 32.
- 33.
- 34.
- 35.
- 36.
- 37.
- 38.
- 39.
- 40.
- 41.
- 42.
- 43.
- 44.
- 45.
- 46.
- 47.
- 48.
- 49.
- 50.
- 51.
- 52.
- 53.
- 54.
- 55.
- 56.
- 57.
- 58.
canvas中的宽、高决定了转盘大小,代码中将转盘的半径设置为画布一半宽的长度。同时,由于箭头是由圆、长方形、正方形平移旋转组成,那他们的偏移量、大小也是相对.canvas的属性取的,如果大小有变动需要调整。
为什么不将箭头也画出来?
如果将箭头也画在画布上,那么我不能实现转盘转动,箭头不动的动画了,画布是一个整体。
index.js
import prompt from '@system.prompt';
import router from '@system.router';
export default {
data: {
//1.1创建奖项信息
infoArr: [
{ name: '1号奖品' },
{ name: '2号奖品' },
{ name: '3号奖品' },
{ name: '4号奖品' },
{ name: '5号奖品' },
{ name: '6号奖品' },
{ name: '7号奖品' },
{ name: '未中奖' },
],
//1.2画布大小
circleHeight: 400,
circleWidth: 360,
//1.3扇区弧度
arcAngle: 0,
//1.4扇区角度
jiaoDu: 0,
//1.4动画参数
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.设定参数
//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扇区角度
this.jiaoDu = 360 / this.infoArr.length;
//2.5定义起始弧度,箭头向上,初始度数需要-90deg
var beginAngle = this.arcAngle * 0.5 - 90 * Math.PI / 180;
//3.遍历,绘制扇区
for (var i = 0; i < this.infoArr.length; i++) {
//3.1结束弧度
var endAngle = beginAngle + this.arcAngle;
//3.2开启路径
ctx.beginPath();
//3.3起点
ctx.moveTo(x0, y0);
//3.4绘制扇区
ctx.arc(x0, y0, radius, beginAngle, endAngle);
//3.5设置颜色
if (i == this.infoArr.length - 1) {
ctx.fillStyle = '#2f4f4f'; //未中奖灰色
} 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文字坐标
var textX = x0 + (radius * 2 / 3) * Math.cos(textAngle);
var textY = y0 + (radius * 2 / 3) * Math.sin(textAngle);
//4.3平移画布起点到文字位置
ctx.translate(textX, textY);
//4.4旋转画布
ctx.rotate((this.jiaoDu * (i + 1) - 90) * Math.PI / 180);
//4.5设置文字字号和字体
ctx.font = "25px '微软雅黑'";
//4.6文字居中对齐
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
//4.7绘制文字
ctx.strokeText(text, 0, 0);
//4.8还原旋转、平移,方便下次旋转
ctx.rotate(-(this.jiaoDu * (i + 1) - 90) * Math.PI / 180);
ctx.translate(-textX, -textY);
//5.更新起始弧度, 将当前扇形的结束弧度作为下一个扇形的起始弧度
beginAngle = endAngle;
}
},
start: function () {
//6.旋转事件
//6.1奖品总数
let count = this.infoArr.length;
//6.2生成随机数
let randomNum = Math.floor(Math.random() * count);
//6.3转动角度(+ 360*3)
let deg = randomNum * this.jiaoDu + 360 * 3 + "deg";
//6.4奖品名
let index = count - randomNum - 1;
let name = this.infoArr[index].name;
console.log("name == " + name);
//6.5动画帧
var frames = [
{
transform: {
rotate: '0deg'
},
},
{
transform: {
rotate: deg
},
}
];
//6.5动画绑定
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调用播放开始的方法
this.animation.play();
},
menu: function () {
router.push ({
uri: 'pages/menuPage/menuPage',
});
},
}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
- 22.
- 23.
- 24.
- 25.
- 26.
- 27.
- 28.
- 29.
- 30.
- 31.
- 32.
- 33.
- 34.
- 35.
- 36.
- 37.
- 38.
- 39.
- 40.
- 41.
- 42.
- 43.
- 44.
- 45.
- 46.
- 47.
- 48.
- 49.
- 50.
- 51.
- 52.
- 53.
- 54.
- 55.
- 56.
- 57.
- 58.
- 59.
- 60.
- 61.
- 62.
- 63.
- 64.
- 65.
- 66.
- 67.
- 68.
- 69.
- 70.
- 71.
- 72.
- 73.
- 74.
- 75.
- 76.
- 77.
- 78.
- 79.
- 80.
- 81.
- 82.
- 83.
- 84.
- 85.
- 86.
- 87.
- 88.
- 89.
- 90.
- 91.
- 92.
- 93.
- 94.
- 95.
- 96.
- 97.
- 98.
- 99.
- 100.
- 101.
- 102.
- 103.
- 104.
- 105.
- 106.
- 107.
- 108.
- 109.
- 110.
- 111.
- 112.
- 113.
- 114.
- 115.
- 116.
- 117.
- 118.
- 119.
- 120.
- 121.
- 122.
- 123.
- 124.
- 125.
- 126.
- 127.
- 128.
- 129.
- 130.
- 131.
- 132.
- 133.
- 134.
- 135.
- 136.
- 137.
- 138.
- 139.
- 140.
- 141.
- 142.
- 143.
- 144.
- 145.
- 146.
js中存放主要逻辑,所以对注释也比较详细。下面是个人踩坑中学习的点:
//1.1创建奖项信息
可以增加减少奖项来预览将要实现的菜单功能,不要搞事情哈,奖项至少为1,代码中没有除0保护。
//1.4动画参数
duration是时长。easing,是描述动画的时间曲线,实现动画由快变慢。fill:forwards在动画结束后,目标将保留动画结束时的状态。
//3.4绘制扇区
x0, y0,扇区的起点坐标。radius,扇区半径。beginAngle,扇区起始的弧度,endAngle,扇区结束的弧度。
//3.5设置颜色
每个扇区设置两个相间的颜色,未中奖特殊扇区用灰色调标识。
//4.2文字坐标
由于文字在扇区中间,所以需要利用正弦余弦计算坐标,再进行画面旋转,才能调整正确的文字方向。
//4.8还原旋转、平移,方便下次旋转
translate函数是基于当前坐标进行偏移,旋转也是基于当前坐标进行旋转。所以当一个扇区的文字填写结束后,需要将坐标还原,这样才方便定位到一下处扇区位置。
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.