logo头像
Snippet 博客主题

微信小程序之大转盘

本文于340天之前发表,文中内容可能已经过时。

前言

最近小程序的游戏挺火的,本节呢就用小程序做一个简单的大转盘

效果图

设计思路

  • 大转盘转动,指针不动
  • 要在小程序里面新建一个画布,并且铺满屏幕
  • 画两个圆,一个绘制大转盘,一个绘制指针
  • 获取用户点击的坐标,看是否点击到了指针
  • 随机一个奖励,真正业务中应该是后端随机一个结果告诉前端
  • 计算奖励的角度
  • 旋转大转盘
  • 计算角度减速大转盘
  • 旋转完回调函数处理

完整代码

pages/main/main.js

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
const utils = require('utils.js');
const Animation = require('Animation.js');
const Circle = require('Circle.js');
Page({
/**
* 页面的初始数据
*/
data: {
wheelImg: 'assets/wheel.png',
pointImg: 'assets/point.png',
touch: { x: 0, y: 0, isPressed: false }
},

touchMove: function (event) {

},

canvasTouchStart: function (event) {
var touch = event.changedTouches[0];
touch.isPressed = true;
this.setData({
touch: touch
})
},

canvasTouchEnd: function (event) {
var touch = event.changedTouches[0];
touch.isPressed = false;
this.setData({
touch: touch
})
},

/**
* 生命周期函数--监听页面加载
*/
onLoad: function (options) {
var that = this;
// 把设备的尺寸赋值给画布,以做到全屏效果
wx.getSystemInfo({
success: function (res) {
that.setData({
windowWidth: res.windowWidth,
windowHeight: res.windowHeight
});
},
})
},

/**
* 生命周期函数--监听页面初次渲染完成
*/
onReady: function () {
var that = this,
fps = 100,
slicePrizes = ["恭喜中了大奖", "50 积分", "500 积分",
"谢谢参与", "200 积分", "100 积分", "150 积分", "谢谢参与"],
w = this.data.windowWidth,
h = this.data.windowHeight,
context = wx.createCanvasContext('canvas'),
wheel = new Circle(w / 2, h / 2.5, 229),
point = new Circle(w / 2, h / 2.5, 37.5),
animation = new Animation(wheel)
;

wheel.img = that.data.wheelImg;
wheel.width = 458;
wheel.height = 458;
point.img = that.data.pointImg;
point.width = 150;
point.height = 150;

// 缩小比例
wheel.scale(0.6, 0.6);
point.scale(0.6, 0.6);

// 启用事件
point.inputEvent = true;
point.onInputDown = run;

// 更新动画
var update = function () {
// 清空
context.clearRect(0, 0, w, h);
// 画转盘
wheel.draw(context);
// 画指针
point.draw(context);

// 更新数据
animation.update();

// 获取手指点击
var touch = that.data.touch;
if (point.inputEvent && touch.isPressed && point.onInputDown) {
// 如果点击到了指针
if (point.contains(touch)) {
// 调用点击回调方法
point.onInputDown();
}
}

// 绘图
context.draw();
};

setInterval(update, 1000 / fps, 1000 / fps);

// 开始转
function run() {
// 避免重复调用
if (animation.isRun) return;

// 随机一个奖品
var prizeIndex = utils.getRandom(slicePrizes.length - 1);
var prize = slicePrizes[prizeIndex];

// 计算奖品角度
var degrees = utils.getRandom(prizeIndex * 45 + 45, prizeIndex * 45);

// 当动画完成时
animation.onComplete = function () {
wx.showToast({
title: prize
})

setTimeout(function () {
wx.hideToast()
}, 1000)
};

animation.tween(5, degrees);
}
}
})

pages/main/Animation.js

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
/**
* 动画
* @author qiao
* @version 2017/12/30
*/
function Animation(circle) {
this.circle = circle;
// 角速度
this.speed = 0;
// 最大速度
this.maxSpeed = 10;
// 摩擦力
this.friction = 0.98;
// 加速度
this.acceleration = 0.1;
// 是否开始运行
this.isRun = false;
// 圈数
this.rounds = 0;
//角度
this.degrees = 0;
// 当前角度
this.angle = 0;
// 开始减速
this.speedDown = false;
// 开始加速
this.speedUp = true;
// 顺时针还是逆时针
this.anticlockwise = false;
// 完成
this.onComplete = null;
}

Animation.prototype.tween = function (rounds, degrees) {
this.circle.rotation = 0;
this.angle = 0;
this.speedDown = false;
this.speedUp = true;
this.rounds = rounds;
this.degrees = degrees;
this.isRun = true;
this.speed = 0;
};

Animation.prototype.update = function () {
if (this.isRun) {

// 是否要减速
if (this.angle >= (this.rounds * 360 + this.degrees)) {
this.speedDown = true;
this.angle = 0;
}

// 是否要停止加速
if (this.speed >= this.maxSpeed) {
this.speedUp = false;
}

// 转动角度
this.angle += this.speed;

// 加速
if (this.speedUp) {
this.speed += this.acceleration;
}

// 减速
if (this.speedDown) {
if (Math.abs(this.angle) >= 360) {
this.isRun = false;
this.speed = 0;
if (this.onComplete) this.onComplete();
} else {
this.speed *= this.friction;
}
}

// 旋转方向
if (this.anticlockwise) {
this.circle.rotation += (Math.PI / 180) * this.speed;
} else {
this.circle.rotation -= (Math.PI / 180) * this.speed;
}
}
};

module.exports = Animation;

pages/main/Circle.js

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
/**
* 大转盘
* @author qiao
* @version 2017/12/30
*/
function Circle(x, y, radius) {
this.x = x;
this.y = y;
this.width = 0;
this.height = 0;
this.radius = radius;
this.rotation = 0;
this.img = null;
this.scaleX = 1;
this.scaleY = 1;
this.inputEvent = false;
this.onInputDown = null;
}

Circle.prototype.draw = function (context) {
// 保存
context.save();
// 移动到圆心
context.translate(this.x, this.y);
// 旋转
context.rotate(this.rotation);
// 缩放
context.scale(this.scaleX, this.scaleY);

if (this.img) {
var imgX = -this.width / 2;
var imgY = -this.height / 2;
context.drawImage(this.img, imgX, imgY);
}
// 还原
context.restore();
};

Circle.prototype.scale = function (x, y) {
this.scaleX = x;
this.scaleY = y;
};

Circle.prototype.contains = function (obj) {
return Circle.contains(this, obj.x, obj.y);
};

Circle.contains = function (a, x, y) {
// Check if x/y are within the bounds first
if (a.radius > 0 && x >= a.left && x <= a.right && y >= a.top && y <= a.bottom) {
var dx = (a.x - x) * (a.x - x);
var dy = (a.y - y) * (a.y - y);

return (dx + dy) <= (a.radius * a.radius);
}
else {
return false;
}
};

Object.defineProperty(Circle.prototype, "left", {
get: function () {
return this.x - this.radius;
},

set: function (value) {
if (value > this.x) {
this.radius = 0;
}
else {
this.radius = this.x - value;
}
}
});

Object.defineProperty(Circle.prototype, "right", {

get: function () {
return this.x + this.radius;
},

set: function (value) {
if (value < this.x) {
this.radius = 0;
}
else {
this.radius = value - this.x;
}
}
});

Object.defineProperty(Circle.prototype, "top", {
get: function () {
return this.y - this.radius;
},

set: function (value) {
if (value > this.y) {
this.radius = 0;
}
else {
this.radius = this.y - value;
}
}
});

Object.defineProperty(Circle.prototype, "bottom", {
get: function () {
return this.y + this.radius;
},
set: function (value) {
if (value < this.y) {
this.radius = 0;
}
else {
this.radius = value - this.y;
}
}
});

module.exports = Circle;

pages/main/main.wxml

1
2
3
4
5
6
7
8
<canvas 
style='width: {{windowWidth}}px; height: {{windowHeight}}px'
disable-scroll="true"
bindtouchstart="canvasTouchStart"
bindtouchmove="touchMove"
bindtouchend="canvasTouchEnd"
canvas-id="canvas"
></canvas>

pages/main/main.wxss

1
2
3
page{
height: 100%;
}

pages/main/utils.js

1
2
3
4
5
6
var utils = {};

utils.getRandom = function (max, min) {
min = arguments[1] || 0;
return Math.floor(Math.random() * (max - min + 1) + min);
};

pages/main/assets/wheel.png

pages/main/assets/point.png

关注我们

IT实战联盟是集产品、UI设计、前后端、架构、大数据和AI人工智能等为一体的实战交流服务平台!联盟嘉宾都为各互联网公司项目的核心成员,联盟主旨是“让实战更简单”,欢迎来撩~~~

上一篇