<div class="container">
<div id="turntable" class="turntable"></div>
<div class="arrow"></div>
<div id="lottery-btn" class="lottery-btn">抽奖</div>
</div>
.container {
width: 500px;
padding: 40px;
background: #f8fafc;
display: flex;
justify-content: center;
}
.turntable {
position: relative;
width: 400px;
height: 400px;
border-radius: 50%;
}
.prize-label {
position: absolute;
width: 100%;
height: 100%;
display: flex;
justify-content: center;
align-items: baseline;
font-weight: bold;
color: white;
line-height: 100px;
}
.arrow {
position: absolute;
top: 140px;
width: 0;
height: 0;
border-left: 15px solid transparent;
border-right: 15px solid transparent;
border-bottom: 100px solid #f8fafc;
}
.lottery-btn {
position: absolute;
top: 200px;
width: 80px;
height: 80px;
border-radius: 50%;
background: #f8fafc;
display: flex;
justify-content: center;
align-items: center;
color: #333;
font-weight: bold;
cursor: pointer;
user-select: none;
&--disabled {
color: #999;
pointer-events: none;
}
}
View Compiled
interface Prize {
label: string;
probability: number;
bgColor: string;
}
const prizes: Prize[] = [
{ label: "超级大奖", probability: 0.001, bgColor: "#b91c1c" },
{ label: "特等奖", probability: 0.009, bgColor: "#c2410c" },
{ label: "一等奖", probability: 0.01, bgColor: "#7e22ce" },
{ label: "二等奖", probability: 0.03, bgColor: "#2563eb" },
{ label: "三等奖", probability: 0.15, bgColor: "#15803d" },
{ label: "安慰奖", probability: 0.3, bgColor: "#1e293b" },
{ label: "谢谢参与", probability: 0.5, bgColor: "#3f3f46" }
];
const turntableDom = document.getElementById("turntable");
const lotteryBtnDom = document.getElementById("lottery-btn");
const proportionPerPrize = Number((100 / prizes.length).toFixed(1));
const anglePerPrize = Number((360 / prizes.length).toFixed(1));
const animationDuration = 5000;
const rotateLapsBase = 10;
let rotateLaps = 0;
// #region 划分转盘扇形区域
const turntableConicGradient = prizes.map((prize, index) => {
const from = (proportionPerPrize * index).toFixed(1);
const to =
index === prizes.length - 1
? 100
: (proportionPerPrize * (index + 1)).toFixed(1);
return `${prize.bgColor} ${from}% ${to}%`;
});
turntableDom.style.background = `conic-gradient(${turntableConicGradient.join(
","
)})`;
// #endregion
// #region 添加奖品名节点
const prizeLabels = document.createDocumentFragment();
prizes.map((prize, index) => {
const prizeLabel = document.createElement("div");
prizeLabel.classList.add("prize-label");
prizeLabel.style.transform = `rotate(${
-anglePerPrize / 2 + anglePerPrize * (index + 1)
}deg)`;
prizeLabel.innerText = prize.label;
prizeLabels.appendChild(prizeLabel);
});
turntableDom.append(prizeLabels);
// #endregion
// #region 设置转盘初始角度
const turntableBaseRotate = -anglePerPrize / 2;
turntableDom.style.transform = `rotate(${turntableBaseRotate}deg)`;
// #endregion
const getRandomNumber = (min: number, max: number, precision: number) => {
const factor = Math.pow(10, precision);
const random = Math.random() * (max - min) + min;
return Math.round(random * factor) / factor;
};
lotteryBtnDom.onclick = () => {
lotteryBtnDom.classList.add("lottery-btn--disabled");
// #region 获取中奖结果,由后端完成
let prizeIndex = 0;
let resultNum = getRandomNumber(0, 1, 3);
while (resultNum > 0) {
resultNum -= prizes[prizeIndex].probability;
if (resultNum > 0) {
prizeIndex += 1;
}
}
const prize = prizes[prizeIndex];
// #endregion
// #region 获取转盘需要旋转的角度
const turntableRotateDegFrom = Number((anglePerPrize * prizeIndex).toFixed(1));
const turntableRotateDegTo =
prizeIndex === prizes.length - 1
? 360
: Number((anglePerPrize * (prizeIndex + 1)).toFixed(1));
const turntableRotateDegEdgeThreshold = Number((anglePerPrize / 4).toFixed(1)); // 设定旋转到指定扇形区域的距边缘阈值(<= anglePerPrize / 2),防止出现指针指向太靠近边缘的情况
const turntableRotateDegBase = getRandomNumber(
turntableRotateDegFrom + turntableRotateDegEdgeThreshold,
turntableRotateDegTo - turntableRotateDegEdgeThreshold,
1
);
rotateLaps += rotateLapsBase; // 适配多次旋转的情况
const turntableRotateDeg = -(rotateLaps * 360 + turntableRotateDegBase);
// #endregion
// #region 为转盘设置旋转动画
turntableDom.style.transform = `rotate(${turntableRotateDeg}deg)`;
turntableDom.style.transition = `transform ${animationDuration}ms cubic-bezier(.3, .9, .38, 1)`;
// #endregion
setTimeout(() => {
alert(`您抽中了:${prize.label}!`);
lotteryBtnDom.classList.remove("lottery-btn--disabled");
}, animationDuration + 500);
};
View Compiled
This Pen doesn't use any external CSS resources.
This Pen doesn't use any external JavaScript resources.