写一个简单的转转小游戏
最近由于个人好奇,想实现一个H5的转转小游戏,于是写了一个简单的demo,可自行可定义转圈数,时间,以及加速减速等效果。
效果图片
以下代码可直接运行
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>数字转转小游戏</title>
<style>
.container {
display: flex;
flex-wrap: wrap;
justify-content: space-around;
grid-row: 3;
width: 100px;
height: 100px;
}
.container .item {
width: 30%;
height: 30%;
border: 1px solid #ccc;
display: flex;
justify-content: center;
align-items: center;
}
.container .item0 {
border: 1px solid transparent;
}
.container .active {
background-color: #ccc;
}
</style>
</head>
<body>
<div class="container">
<div class="item item1">1</div>
<div class="item item2">2</div>
<div class="item item3">3</div>
<div class="item item8">8</div>
<div class="item item0">
<button id="startBtn" onclick="handleOnStart()">start</button>
</div>
<div class="item item4">4</div>
<div class="item item7">7</div>
<div class="item item6">6</div>
<div class="item item5">5</div>
</div>
<script>
/***********************
* Rolls - 数字滚动组件 *
* 组件需求分析:
* 旋转的实质就是数字按照一定的顺序随着时间变化
* 1、数字变化;
* 2、时间加速和时间减速;
* 3、时间结束;
* 4、生成一个随机值;(假设在旋转开始生成随机值)
*
**********************/
var Rolls = function ({
count,
time,
iloop,
speed,
startCB,
rollingCB,
endCB,
} = {}) {
this.count = 8; // 有多少个循环值(从1开始)
if (count) {
this.count = count;
}
this.time = 5000; // 持续时间
if (time) {
this.time = time;
}
this.iloop = 12; // 循环次数
if (iloop) {
this.iloop = iloop;
}
//加速因子 (0-1)
// 1、例如:第一个值比最后一个值多50%的时间
// 2、例如:最后一个值比第一个值多50%的时间
this.speed = 0.5;
if (speed) {
this.speed = speed;
}
this.timer = null; // 定时器
this.random = -1; // 抽奖值
this.rolling = false; // 是否启动中
// 执行中的回调
this.startCB = () => {};
if (startCB) {
this.startCB = startCB;
}
// 执行中的回调
this.rollingCB = (value) => {};
if (rollingCB) {
this.rollingCB = rollingCB;
}
// 执行结束回调
this.endCB = () => {};
if (endCB) {
this.endCB = endCB;
}
};
Rolls.prototype = {
/**
* 启动函数
* @param {random} - 抽奖位置值 如果未传值,就采用随机值
*/
start: function (random) {
// 如果启动了,就只能等启动完成在启动
if (this.rolling) {
return false;
} else {
this.rolling = true;
}
if (random) {
this.random = random;
} else {
this.random = parseInt(Math.random() * this.count, 10) + 1;
}
var arr = this.generateActives(this.random);
this.roll(arr, 0, this.rollingCB);
this.startCB && this.startCB();
},
/**
* 抽奖主动结束 ??? 待定需求
*/
end: function (cb) {},
/**
* 滚动
* @param {Array} arr - 滚动动作
* @param {Array} i - 滚动动作下标
* @param {Array} cb - 滚动外部接口回调函数
*/
roll: function (arr, i, cb) {
var that = this;
if (that.timer) clearTimeout(that.timer);
that.timer = setTimeout(() => {
cb && cb(arr[i].value);
i += 1;
if (arr[i] === undefined) {
this.endCB && this.endCB();
this.rolling = false;
return false;
}
this.roll(arr, i, cb);
}, arr[i].time);
},
/**
* 生成动作
*/
generateActives: function (random) {
var list = []; // 动作
var arr = []; // 动作值
for (let i = 1; i < this.count + 1; i++) {
arr.push(i);
}
for (let i = 0; i < this.iloop; i++) {
for (let x = 0, len = arr.length; x < len; x++) {
list.push({
value: arr[x],
time: 0,
});
}
}
// 根据结果动作值添加最终动作
for (let x = 0, len = arr.length; x < len; x++) {
list.push({
value: arr[x],
time: 0,
});
if (random === arr[x]) {
break;
}
}
return this.formatActiveTime(list);
},
/**
* 给动作分配时间
* @param {Array} - list
* 1、定义每个动作的时间
* 2、假如将所有的时间和数量分为 10等份
* 3、前10% 与 后10% 占用 80%的时间
* 4、然后将前后10%的数据的时间 结合加减速因子再 进行对折=》前一半生成加速数据,后一半生成减速数据
*/
formatActiveTime: function (list) {
var activeList = [].concat(list);
var a = list.length / 10;
var b = this.time / 10;
var startList = [];
var middleList = [];
var endList = [];
// 前后两节,百分之10的数量各占40%的时间
var t1 = (4 * b) / a;
for (var i = 0; i < Math.floor(a); i++) {
startList.push(t1);
endList.push(t1);
}
startList = this.formatSameNumbersToScale(
startList,
this.speed,
"desc"
); // 降序
endList = this.formatSameNumbersToScale(endList, this.speed, "asc"); // 升序
// 中间部分,80%的数量占用20%的时间
var t2 = (2 * b) / (8 * a);
for (var i = 0; i < Math.floor(8 * a) - 1; i++) {
middleList.push(t2);
}
// 赋值时间
startList
.concat(middleList)
.concat(endList)
.forEach((item, index) => {
if (activeList[index].time !== undefined) {
activeList[index].time = item;
}
});
return list;
},
/**
* 对一组相同的数字数据进行等比格式化为升序或者降序
* @param {number[]} numbers - 数据数量
* @param {Number} scale - 比例
* @param {Number} sort - 排序方式 : asc-升序,desc-降序
* @returns {number[]} list
*/
formatSameNumbersToScale: function (numbers, scale, sort) {
var s = sort === "desc" ? 1 : -1; //
var list = [].concat(numbers);
var len = list.length;
var avgSpeed = scale / len;
for (let i = 0; i < len / 2; i++) {
var n = list[i];
var balance = s * (len - i) * avgSpeed * n;
list[i] = n + balance;
list[len - i] = n - balance;
}
return list;
},
};
// 实例一个数组转转组件
var rolls = new Rolls({
startCB: startCB,
rollingCB: rollingCB,
endCB: endCB,
});
function handleOnStart() {
rolls.start();
}
function startCB() {
document.getElementById("startBtn").style.opacity = "0.5";
}
function endCB() {
document.getElementById("startBtn").style.opacity = "1";
}
// 业务转动回调
function rollingCB(value) {
var activeEl = document.querySelector(".container .active");
var currentEl = document.querySelector(".container .item" + value);
if (activeEl) {
activeEl.className = activeEl.className.replace(" active", "");
}
currentEl.className = currentEl.className + " active";
}
</script>
</body>
</html>