Skip to content

Commit b1dd25f

Browse files
author
rxsun
committed
first
0 parents  commit b1dd25f

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

100 files changed

+27952
-0
lines changed

README.md

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
# 转盘抽奖
2+
转盘抽奖,在 h5 页面中还是比较常见的,并且用 css+js,vue,react 分别实现了。
3+
4+
## 页面截图(vue、react)
5+
![./screenshot/turntable.png](./screenshot/turntable.png)
6+
7+
vue、react 实现的效果一致,只是运用了不同的框架。其中 vue 实现的已经应用于实际,而 react 实现的算是“练手”。
8+
9+
## 页面截图(css+js)
10+
![./screenshot/turntable-css-js.png](./screenshot/turntable-css-js.png)
11+
12+
用 css 布局实现转盘效果,包括闪烁的灯、转盘、指针等,没有应用任何图片,js 实现抽奖效果。目的是练习自己的基本功,css布局 + 原生js的能力。
13+
14+
## 项目预览
15+
1. vue demo:[vue](./dist/index.html)
16+
2. 原生 js demo: [js](./demo/index.html)
17+
3. react demo: [react](./dist/index.html)

css-js-turntable/index.html

+350
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,350 @@
1+
<!doctype html>
2+
<html>
3+
<head>
4+
<meta charset="utf-8"/>
5+
<title>简单转盘</title>
6+
<style>
7+
* {
8+
margin: 0;
9+
padding: 0;
10+
}
11+
html, body {
12+
height: 100%;
13+
}
14+
ul {
15+
list-style: none;
16+
}
17+
18+
@keyframes white-to-yellow {
19+
0% {
20+
background: #fff;
21+
}
22+
100% {
23+
background: #d7a945;
24+
}
25+
}
26+
@keyframes heartbeat {
27+
0% {
28+
transform: scale(1);
29+
}
30+
25% {
31+
transform: scale(0.8);
32+
}
33+
50% {
34+
transform: scale(1.2);
35+
}
36+
75% {
37+
transform: scale(0.9);
38+
}
39+
100% {
40+
transform: scale(1);
41+
}
42+
}
43+
44+
.turntable-wrap {
45+
position: relative;
46+
overflow: hidden;
47+
margin: 50px;
48+
width: 340px;
49+
height: 340px;
50+
border: 7px solid #b2a98d;
51+
border-radius: 50%;
52+
box-shadow: 0 0 20px #b2a98d;
53+
}
54+
.turntable-wrap .light {
55+
position: absolute;
56+
top: 0;
57+
left: 0;
58+
width: 100%;
59+
height: 100%;
60+
background: #e0ddd1;
61+
animation: rotate 5s linear infinite;
62+
}
63+
.turntable-wrap .light span {
64+
position: absolute;
65+
top: 0;
66+
left: 0;
67+
right: 0;
68+
margin: 0 auto;
69+
width: 10px;
70+
height: 100%;
71+
border-radius: 50%;
72+
transform-origin: center center;
73+
}
74+
.turntable-wrap .light span:before {
75+
content: '';
76+
position: absolute;
77+
top: 5px;
78+
left: 0;
79+
right: 0;
80+
margin: 0 auto;
81+
width: 10px;
82+
height: 10px;
83+
border-radius: 50%;
84+
}
85+
.turntable-wrap .light span:nth-of-type(even):before {
86+
background: #fff;
87+
animation: white-to-yellow 1s linear infinite;
88+
}
89+
.turntable-wrap .light span:nth-of-type(odd):before {
90+
background: #d7a945;
91+
animation: white-to-yellow 1s linear reverse infinite;
92+
}
93+
94+
.turntable-wrap .turntable {
95+
position: absolute;
96+
margin: 20px;
97+
width: 300px;
98+
height: 300px;
99+
border-radius: 50%;
100+
background: #fff;
101+
}
102+
.turntable-wrap .turntable .bg {
103+
position: absolute;
104+
top: 0;
105+
left: 0;
106+
width: 100%;
107+
height: 100%;
108+
background: #fff;
109+
border: 1px solid #dfd8be;
110+
border-radius: 50%;
111+
transform: rotate(90deg);
112+
}
113+
.turntable-wrap .turntable .bg li {
114+
position: absolute;
115+
top: 0;
116+
left: 0;
117+
right: 0;
118+
margin: 0 auto;
119+
width: 1px;
120+
height: 100%;
121+
background: #dfd8be;
122+
transform-origin: center center;
123+
}
124+
125+
.turntable-wrap .turntable .gift {
126+
position: relative;
127+
width: 100%;
128+
height: 100%;
129+
transform: rotate(45deg);
130+
}
131+
.turntable-wrap .turntable .gift li {
132+
position: absolute;
133+
top: 5%;
134+
left: 5%;
135+
width: 45%;
136+
height: 45%;
137+
transform-origin: right bottom;
138+
}
139+
.turntable-wrap .turntable .gift li span {
140+
position: absolute;
141+
top: 0;
142+
left: 0;
143+
right: 0;
144+
bottom: 0;
145+
display: block;
146+
width: 50px;
147+
height: 70px;
148+
margin: auto;
149+
background: yellow;
150+
transform: rotate(-45deg);
151+
text-align: center;
152+
line-height: 80px;
153+
border-radius: 5px;
154+
background: #f23c3c;
155+
color: #fff;
156+
font-size: 24px;
157+
}
158+
.turntable-wrap .turntable .gift li:not(.no-gift) span:before {
159+
content: '';
160+
position: absolute;
161+
top: 15px;
162+
left: 0;
163+
width: 50px;
164+
height: 1px;
165+
background: #fff;
166+
}
167+
.turntable-wrap .turntable .gift li.no-gift span {
168+
background: #fff;
169+
line-height: 70px;
170+
color: #bfa74f;
171+
font-size: 12px;
172+
}
173+
174+
.turntable-wrap .pointer {
175+
box-sizing: border-box;
176+
position: absolute;
177+
top: 50%;
178+
left: 0;
179+
right: 0;
180+
margin: -23px auto;
181+
width: 46px;
182+
height: 46px;
183+
border-radius: 50%;
184+
background: #fff;
185+
border: 5px solid #fff;
186+
box-shadow: 0 0 0 5px #b9a046;
187+
text-align: center;
188+
line-height: 16px;
189+
color: #b9a046;
190+
font-size: 14px;
191+
font-weight: 700;
192+
cursor: pointer;
193+
}
194+
.turntable-wrap .pointer:before {
195+
content: '';
196+
position: absolute;
197+
top: -58px;
198+
left: 0;
199+
right: 0;
200+
margin: 0 auto;
201+
width: 0;
202+
border-style: solid;
203+
border-color: transparent transparent #b9a046 transparent;
204+
border-width: 25px 10px 25px 10px;
205+
}
206+
</style>
207+
</head>
208+
<body>
209+
<div class="turntable-wrap">
210+
<div class="light" id="turntable_light"></div>
211+
<div class="turntable" id="turntable">
212+
<ul class="bg" id="turntable_bg"></ul>
213+
<ul class="gift" id="turntable_gift"></ul>
214+
</div>
215+
<div class="pointer disabled" id="turntable_pointer">点击抽奖</div>
216+
</div>
217+
218+
<script>
219+
// 单例模式,转盘唯一,用对象字面量方式表示
220+
let turntable = {
221+
itemNum: 6, // 转盘平均分几块
222+
223+
lightNum: 18, // 转盘上的灯
224+
light: null, // 转盘旋转灯容器
225+
226+
turntable: null, // 转盘
227+
228+
bg: null, // 转盘背景
229+
230+
gift: null, // 转盘上的中奖结果图
231+
232+
pointer: null, // 转盘指针
233+
234+
lottery: [], // 中奖数据
235+
236+
typeMap: {1: '¥', 2: '谢谢参与'},
237+
typeClassMap: {1: '', 2: 'no-gift'},
238+
239+
isGoing: false, // 游戏是否开始
240+
241+
init () {
242+
if (!this.lottery.length) {
243+
this.pointer.style.display = 'none';
244+
throw new Error('请设置中奖结果数据');
245+
}
246+
247+
// 初始化灯
248+
let lightFragment = document.createDocumentFragment();
249+
for (let i = 0; i < this.lightNum; i++) {
250+
let lightItem = document.createElement('span');
251+
let deg = (360 / this.lightNum) * i
252+
lightItem.style.transform = `rotate(${deg}deg)`;
253+
lightFragment.appendChild(lightItem);
254+
}
255+
this.light.appendChild(lightFragment);
256+
257+
// 初始化转盘背景、转盘上的中奖图(用不同背景色代替)
258+
let bgFragment = document.createDocumentFragment();
259+
let bgItemWidth = this.bg.offsetWidth / this.num;
260+
let giftFragment = document.createDocumentFragment();
261+
for (let i = 0; i < this.itemNum; i++) {
262+
let bgItem = document.createElement('li');
263+
let deg = (360 / this.itemNum) * i
264+
bgItem.style.transform = `rotate(${deg}deg)`;
265+
bgFragment.appendChild(bgItem);
266+
267+
let giftItem = document.createElement('li');
268+
giftItem.style.transform = `rotate(${deg}deg)`;
269+
giftItem.className = this.typeClassMap[this.lottery[i].type];
270+
let span = document.createElement('span');
271+
span.innerHTML = this.typeMap[this.lottery[i].type];
272+
giftItem.appendChild(span);
273+
giftFragment.appendChild(giftItem)
274+
}
275+
this.bg.appendChild(bgFragment);
276+
this.gift.appendChild(giftFragment);
277+
278+
// 给点击抽奖按钮添加点击事件
279+
this.pointer.onclick = this.gameStart.bind(this)
280+
},
281+
282+
gameStart () {
283+
if (this.isGoing) {
284+
return
285+
}
286+
this.isGoing = true;
287+
288+
// 1. 随机中奖结果
289+
// 从1-100之间得到一个随机数,看这个随机数在中奖设置的范围,得到最终中奖的项
290+
let randomRate = ~~(Math.random() * 100) // ~~ == Math.floor()
291+
// 设置中奖数据的概率范围
292+
let num = 0
293+
this.lottery.forEach(item => {
294+
item.min = num;
295+
num += item.rate;
296+
item.max = num;
297+
})
298+
// 根据随机数,得到中奖结果
299+
let res = this.lottery.filter(item => {
300+
return randomRate >= item.min && randomRate < item.max;
301+
})[0];
302+
// 这儿可以根据实际情况,可重置中奖结果
303+
304+
// 2. 计算旋转角度, 需要多转5圈,达转1圈用时1s, 到旋转的效果
305+
let rotateItemDeg = (res.location - 1) * (360 / this.lottery.length); // 每个item旋转角度, 第一个不用旋转
306+
let rotate = rotateItemDeg + 5 * 360;
307+
let rotateSpeed = (rotateItemDeg / 360 * 1 + 5).toFixed(2);
308+
// 重置转盘样式
309+
this.turntable.removeAttribute('style');
310+
// 保证下一次旋转动画生效
311+
setTimeout(() => {
312+
this.turntable.style.transform = `rotate(${rotate}deg)`;
313+
this.turntable.style.transition = `transform ${rotateSpeed}s ease-out`;
314+
}, 10)
315+
316+
// 3. 动画结束,显示中奖结果,中奖结果如何显示,视实际情况而定
317+
setTimeout(() => {
318+
this.isGoing = false;
319+
console.log('中奖结果:', randomRate, res, this.typeMap[res.type]);
320+
}, rotateSpeed * 1000);
321+
}
322+
}
323+
324+
let lottery = [
325+
{
326+
location: 1, // 位置
327+
type: 1, // 中奖
328+
rate: 30,
329+
},
330+
{
331+
location: 2,
332+
type: 2, // 未中奖
333+
rate: 20
334+
},
335+
{ location: 3, type: 1, rate: 10 },
336+
{ location: 4, type: 2, rate: 20 },
337+
{ location: 5, type: 1, rate: 10 },
338+
{ location: 6, type: 2, rate: 10 }
339+
];
340+
341+
turntable.turntable = document.querySelector('#turntable');
342+
turntable.light = document.querySelector('#turntable_light');
343+
turntable.bg = document.querySelector('#turntable_bg');
344+
turntable.gift = document.querySelector('#turntable_gift');
345+
turntable.pointer = document.querySelector('#turntable_pointer');
346+
turntable.lottery = lottery;
347+
turntable.init();
348+
</script>
349+
</body>
350+
</html>

react-turntable/.gitignore

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2+
3+
# dependencies
4+
/node_modules
5+
/.pnp
6+
.pnp.js
7+
8+
# testing
9+
/coverage
10+
11+
# production
12+
build
13+
14+
# misc
15+
.DS_Store
16+
.env.local
17+
.env.development.local
18+
.env.test.local
19+
.env.production.local
20+
21+
npm-debug.log*
22+
yarn-debug.log*
23+
yarn-error.log*

0 commit comments

Comments
 (0)