主要描述的是如何运用 css 绘制一个抽奖转盘,并运用原生 js 实现转盘抽奖效果。

先来张效果图:

布局

一般来说,转盘一般有四个部分组成:外层闪烁的灯、内层旋转的圆盘、圆盘上的中奖结果、指针。

所以html的结构如下:

<div class="turntable-wrap">
<div class="light" id="turntable_light"></div>
<div class="turntable" id="turntable">
<ul class="bg" id="turntable_bg"></ul>
<ul class="gift" id="turntable_gift"></ul>
</div>
<div class="pointer disabled" id="turntable_pointer">点击抽奖</div>
</div>

其中灯需要一直闪烁,而抽奖的时候转盘需要转动连带其中的中奖结果,而转盘指针不需要转动是固定的。因此 light 、 turntable 、 pointer放在同一级别。

外层容器利用border-radius:50%实现从正方形变圆形的效果。border: 7px solid #b2a98d加个边框,box-shadow: 0 0 20px #b2a98d

加点阴影,美化一下。

.turntable-wrap {
position: relative;
overflow: hidden;
margin: 50px;
width: 340px;
height: 340px;
border: 7px solid #b2a98d;
border-radius: 50%;
box-shadow: 0 0 20px #b2a98d;
}

1. 闪烁的灯

  • 利用 js 动态的给 light 生成多个小圆点,lightNum = 18,感觉这个数字,转盘每小块显示 3 个灯,感觉效果最好
// 初始化灯
let lightFragment = document.createDocumentFragment();
for (let i = 0; i < this.lightNum; i++) {
let lightItem = document.createElement('span');
let deg = (360 / this.lightNum) * i
lightItem.style.transform = `rotate(${deg}deg)`;
lightFragment.appendChild(lightItem);
}
this.light.appendChild(lightFragment);
  • 接下来就是如何让这些小圆点,显示在“正确的位置上”

透露一下,主要利用的是transform:rotate()transform-origin: center center这两个属性。

给 light 宽、高和外层容器一致,里面添加元素,而元素的宽度给和小圆点一致的宽度,而这些元素旋转一定的角度((360 / this.lightNum) * i),而它们的旋转中心设置在中心点。达到的效果就是这样的:

接下来就是绘制小圆点了,利用:before属性,将小圆点绘制到顶部。而闪烁的灯,有两种颜色,再利用:nth-type-of(event/odd)实现。最后就是闪烁了,利用的是@keyframesanimation

最终实现:

@keyframes white-to-yellow {
0% {
background: #fff;
}
100% {
background: #d7a945;
}
} .turntable-wrap .light {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: #e0ddd1;
animation: rotate 5s linear infinite;
}
.turntable-wrap .light span {
position: absolute;
top: 0;
left: 0;
right: 0;
margin: 0 auto;
width: 10px;
height: 100%;
transform-origin: center center;
}
.turntable-wrap .light span:before {
content: '';
position: absolute;
top: 5px;
left: 0;
right: 0;
margin: 0 auto;
width: 10px;
height: 10px;
border-radius: 50%;
}
.turntable-wrap .light span:nth-of-type(even):before {
background: #fff;
animation: white-to-yellow 1s linear infinite;
}
.turntable-wrap .light span:nth-of-type(odd):before {
background: #d7a945;
animation: white-to-yellow 1s linear reverse infinite;
}

转盘的背景

转盘的背景的布局原理和灯的布局原理是一致的。就不重复了。itemNum = 6,表示转盘分6块。

// 初始化转盘背景
let bgFragment = document.createDocumentFragment();
for (let i = 0; i < this.itemNum; i++) {
let bgItem = document.createElement('li');
let deg = (360 / this.itemNum) * i
bgItem.style.transform = `rotate(${deg}deg)`;
bgFragment.appendChild(bgItem);
}
this.bg.appendChild(bgFragment);

主要的 css 如下:

.turntable-wrap .turntable .bg {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: #fff;
border: 1px solid #dfd8be;
border-radius: 50%;
transform: rotate(90deg);
}
.turntable-wrap .turntable .bg li {
position: absolute;
top: 0;
left: 0;
right: 0;
margin: 0 auto;
width: 1px;
height: 100%;
background: #dfd8be;
transform-origin: center center;
}

效果图:

转盘的中奖图

利用的也是transform:rotate()transform-origin属性,达到旋转布局的。但是又有一点点不一样。

为了让中奖图显示在转盘每个块的正中间

所以每个块是按transorm-origin:right bottom这个方向旋转角度,而外层旋转transform: rotate(45deg),里面的每个小图旋转transform: rotate(-45deg)再纠正过来。

ps:其实按照灯的transorm-origin:center center应该也能实现,但开始想到的就是这种方案。

js 初始化块的元素代码如下:

// 初始化转盘上的中奖图
let giftFragment = document.createDocumentFragment();
for (let i = 0; i < this.itemNum; i++) {
let giftItem = document.createElement('li');
giftItem.style.transform = `rotate(${deg}deg)`;
giftItem.className = this.typeClassMap[this.lottery[i].type];
let span = document.createElement('span');
span.innerHTML = this.typeMap[this.lottery[i].type];
giftItem.appendChild(span);
giftFragment.appendChild(giftItem)
}
this.gift.appendChild(giftFragment);

其中元素span完全用来显示具体内容的,比如红包。

typeMap: {1: '¥', 2: '谢谢参与'},
typeClassMap: {1: '', 2: 'no-gift'},

css 布局代码如下:

.turntable-wrap .turntable .gift {
position: relative;
width: 100%;
height: 100%;
transform: rotate(45deg);
}
.turntable-wrap .turntable .gift li {
position: absolute;
top: 5%;
left: 5%;
width: 45%;
height: 45%;
transform-origin: right bottom;
}
.turntable-wrap .turntable .gift li span {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
display: block;
width: 50px;
height: 70px;
margin: auto;
background: yellow;
transform: rotate(-45deg);
text-align: center;
line-height: 80px;
border-radius: 5px;
background: #f23c3c;
color: #fff;
font-size: 24px;
}
.turntable-wrap .turntable .gift li:not(.no-gift) span:before {
content: '';
position: absolute;
top: 15px;
left: 0;
width: 50px;
height: 1px;
background: #fff;
}
.turntable-wrap .turntable .gift li.no-gift span {
background: #fff;
line-height: 70px;
color: #bfa74f;
font-size: 12px;
}

转盘指针

其实这个还是比较简单的,利用border绘制的三角形。

.turntable-wrap .pointer {
box-sizing: border-box;
position: absolute;
top: 50%;
left: 0;
right: 0;
margin: -23px auto;
width: 46px;
height: 46px;
border-radius: 50%;
background: #fff;
border: 5px solid #fff;
box-shadow: 0 0 0 5px #b9a046;
text-align: center;
line-height: 16px;
color: #b9a046;
font-size: 14px;
font-weight: 700;
cursor: pointer;
}
.turntable-wrap .pointer:before {
content: '';
position: absolute;
top: -58px;
left: 0;
right: 0;
margin: 0 auto;
width: 0;
border-style: solid;
border-color: transparent transparent #b9a046 transparent;
border-width: 25px 10px 25px 10px;
}

原生 js 实现抽奖效果

主要代码如下:

gameStart () {
if (this.isGoing) {
return
}
this.isGoing = true; // 1. 随机中奖结果
// 从1-100之间得到一个随机数,看这个随机数在中奖设置的范围,得到最终中奖的项
let randomRate = ~~(Math.random() * 100) // ~~ == Math.floor()
// 设置中奖数据的概率范围
let num = 0
this.lottery.forEach(item => {
item.min = num;
num += item.rate;
item.max = num;
})
// 根据随机数,得到中奖结果
let res = this.lottery.filter(item => {
return randomRate >= item.min && randomRate < item.max;
})[0];
// 这儿可以根据实际情况,可重置中奖结果 // 2. 计算旋转角度, 需要多转5圈,达转1圈用时1s, 到旋转的效果
let rotateItemDeg = (res.location - 1) * (360 / this.lottery.length); // 每个item旋转角度, 第一个不用旋转
let rotate = rotateItemDeg + 5 * 360;
let rotateSpeed = (rotateItemDeg / 360 * 1 + 5).toFixed(2);
// 重置转盘样式
this.turntable.removeAttribute('style');
// 保证下一次旋转动画生效
setTimeout(() => {
this.turntable.style.transform = `rotate(${rotate}deg)`;
this.turntable.style.transition = `transform ${rotateSpeed}s ease-out`;
}, 10) // 3. 动画结束,显示中奖结果,中奖结果如何显示,视实际情况而定
setTimeout(() => {
this.isGoing = false;
console.log('中奖结果:', randomRate, res, this.typeMap[res.type]);
}, rotateSpeed * 1000);
}

由于代码中的注释还是比较全的,就不赘述了。

以下是中奖结果的配置,实际中,可通过接口获取。rate 是中奖比例的控制。

 let lottery = [
{
location: 1, // 位置
type: 1, // 中奖
rate: 30, // 中奖比例 1-100
},
{
location: 2,
type: 2, // 未中奖
rate: 20
},
{ location: 3, type: 1, rate: 10 },
{ location: 4, type: 2, rate: 20 },
{ location: 5, type: 1, rate: 10 },
{ location: 6, type: 2, rate: 10 }
];

最后的最后

  • 文字描述的可能不是很清晰,可看github上的源码实现。也用 vue、react实现了一遍效果,其中 vue 版本的是已经运用于实际中的。
  • 实现方案可能不是最佳的,有更好方案的可提供建议。_

css 如何“画”一个抽奖转盘的更多相关文章

  1. 用CSS实现一个抽奖转盘

    效果 基本是用CSS实现的,没有用图片,加一丢丢JS.完全没有考虑兼容性. 首先画一个转盘, <!DOCTYPE html> <html lang="en"> ...

  2. Effective前端3:用CSS画一个三角形

    p { text-indent: 2em } .triangle-container p { text-indent: 0 } img { margin: 15px 0 } 三角形的场景很常见,打开一 ...

  3. 用CSS画一个带阴影的三角形的示例代码

    1. 思路 怎么用CSS3画一个带阴影的三角形呢 ? 有童鞋说, 这还不简单吗 网上有很多解决方案, 但其实大多都是实现不太完美的, 存在一些问题 假设我们做一个向下的三角形箭头 常见的方法大致有两种 ...

  4. 使用css画一个箭头

    <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <meta name ...

  5. Effective前端1---chapter 2 用CSS画一个三角形

    1.CSS画三角形的画法 第一步:三角形可以用border画出来,首先一个有四个border的div长这样: <div class="triangle"></di ...

  6. Effective前端(3)用CSS画一个三角形

    来源:https://zhuanlan.zhihu.com/p/26160325 三角形的场景很常见,打开一个页面可以看到各种各样的三角形: 由于div一般是四边形,要画个三角形并不是那么直观.你可以 ...

  7. css画一个提示框

    用css画一个如下图的提示框: 代码如下: <!DOCTYPE html> <html lang="en"> <head> <meta c ...

  8. 【前端切图】用css画一个卡通形象-小猪佩奇

    最近在腾讯云技术社区遇到了一位奇才,用css画出了一个社会人小猪佩奇,不得不服.研究了一下他的文章https://segmentfault.com/a/1190000014909658,感觉甚是有趣, ...

  9. CSS画一个三角形,CSS绘制空心三角形,CSS实现箭头

     壹 ❀ 引 这两天因为项目工作较少,闲下来去看了GitHub上关于面试题日更收录的文章,毕竟明年有新的打算.在CSS收录中有一题是 用css创建一个三角形,并简述原理 .当然对于我来说画一个三角形是 ...

随机推荐

  1. windows from docker 安装部署spring jar包方法

    1.安装docker for windows,去官网下载就可以了,按照官网安装 2.把jar和dockerfile放在一个目录下(target 目录下) Dockerfile: FROM java:8 ...

  2. SAP 图形页面

    话不多说,先上炫图. *&---------------------------------------------------------------------* *& Repor ...

  3. Docker 简介,入门

    1.简介 Docker 使用 Google 公司推出的 Go 语言 进行开发实现,基于 Linux 内核的 cgroup,namespace,以及 AUFS 类的 Union FS 等技术,对进程进行 ...

  4. oracle可重复执行脚本(添加字段)

    --添加债券期限字段 declare cn integer; begin cn := 0; select count(*) into cn from user_tab_cols t where t.t ...

  5. linux服务器系统负载监控-shell脚本

    一.监控服务器系统负载情况: 1.用uptime命令查看当前负载情况(1分钟,5分钟,15分钟平均负载情况) # uptime   15:43:59 up 186 days, 20:04,  1 us ...

  6. Python基础-python数据类型之元祖、字典(四)

    元祖 Python的元组与列表类似,不同之处在于元组的元素不能修改.元组使用小括号,列表使用方括号. tuple=(1,2,3,4) print(tuple) 访问元祖 通过索引访问,也可以进行切片操 ...

  7. canvas(一) 基本线条绘制

    var dom = document.getElementById('canvasItem'), ctx = dom.getContext('2d'); //坐标位置默认基于 浏览器窗口(0,0),此 ...

  8. java_22 Map接口

    1Map Collection是孤立存在的,向集合中存储元素是一个一个放进去的 Map中的集合存储是成对的,可以通过键找到值.即将键映射到值的对象.一个映射不能包含重复的键:每个键最多只能映射到一个值 ...

  9. JMD Handy Baby 2 to Decode & Adding New BMW 525 ID46 Key

    Here OBD2TOOL share the guide on how to use JMD Handy Baby II to decode and add new keys for BMW 525 ...

  10. Luogu1613 跑路-倍增+Floyd

    Solution 挺有趣的一道题, 仔细想想才想出来 先用$mp[i][j][dis]$ 是否存在一条 $i$ 到 $j$ 的长度为 $2^{dis}$ 的路径. 转移 : ; dis < ba ...