前端时间有个需求是客户端双端APP内嵌入整个转盘抽奖的web子系统,具体是要在后台能够控制大转盘抽奖的奖项数,和用户免费抽奖的次数,并且免费抽奖使用完,用户可以观看广告进行抽奖或使用积分抽奖。正好最近有空,出了这篇教程,解析转盘抽奖的实现过程。

此子系统整体开发的话由我负责,其中前端技术:H5+CSS3+JS;后端技术:YII2+Redis。

转盘演示视频效果如下所示:

让CSS3中Transform属性带你一文实现炫酷的转盘抽奖效果

下面分两个部分介绍下这个转盘抽奖部分:

一、前端样式及抽奖操作开发

关于前端展示主要使用Transform来实现,下面是关于Transform属性【大家可以去菜鸟教程等看下其详细用法,感觉这个属性效果很炫酷】的介绍,其中主要用到的是rotate(定义 2D 旋转,在参数中规定角度)、skewX(定义沿着 X 轴的 2D 倾斜转换)

以我们代码为例:在prize-list内我们先做出整个转盘的背景图,prize-reward内展示每一个奖品信息。在首次进入页面时,调用接口获取转盘奖品信息,如:名称、icon图、奖品ID等,然后循环组装div标签,组装完成后,根据返回的奖品信息计算奖品条数,然后设置Transform属性的rotate和skewX,因为背景颜色是间隔展示,所以展示的奖品数为偶数,最多展示的奖项最大值为12。当时做的时候没有找到每次赋值的规律,所以2-12的情景值,是我尝试出来的,到这里的话整个的样式其实就已经出来了,视频展示的为6个奖品项效果,接口返回的奖品条数不同,此页面展示不同效果。

此部分就是转盘部分的布局:

<div class="wheelSurf"><div class="wheel"><div class="wheel-icon"><img src="../images/wheel/wheel_back.png" alt=""></div><div class="prize-box"><div class="prize-content"><!-- 转盘的背景盒子,JS填充奖品背景 --><div class="prize-list" id="prize-list"></div><!-- 转盘的奖品盒子,JS填充奖品 --><div class="prize-reward" id="prize-reward"></div></div><!-- 点击抽奖按钮 --><div class="prize-button" id="prize-button"><img src="../images/wheel/start_button.png" alt=""></div></div><div class="score"><div class="spend_score"><span class="consume-score">0</span>积分/次</div><div class="my_score">我的积分:<span id="my-score">0</span></div></div></div><div class="activity_rule"><div class="rule_title">活动规则</div><div class="rule_list" id="activity-rule"></div></div></div>

此部分就是转盘奖品展示的代码:

/**
* 大转盘填充奖项,初始化展示转盘背景
* @param data
*/
function prize(data) {let list = '';let reward = '';let count = data.length;let rotate = 360 / count;prizeCount = count;prizeDeg   = 360 / count;let itemRotate = 360 / 2 / count;let liRotate   = 0;// 设置4、6、8、10、12数量奖品的旋转角度值let width;switch (count) {case 2:liRotate   = 0;width = 4;break;case 4:liRotate   = 0;width = 3;break;case 6:liRotate   = 30;width = 2;break;case 8:liRotate   = 45;width = 1.5;break;case 10:liRotate   = 54;width = 1.3;break;case 12:liRotate   = 60;width = 1;break;default:liRotate   = 0;width = 1;}// 组装背景和奖品的标签,且设置奖品的旋转角度for(let i=0; i < count; i++) {list += "<div class=\"prize-li\"></div>";reward += "<div class=\"prize-item\" data-id=\"" + data[i]['id'] + "\" style=\"transform: rotate(" + (rotate*(i+1) - itemRotate) + "deg) translateX(-50%);width: " + width + "rem;\">\n" +"                                <div class=\"prize-name\">\n" +data[i]['title'] +"                                </div>\n" +"                                <div class=\"prize-icon\">\n" +"                                    <img src=\"" + data[i]['icon'] + "\">\n" +"                                </div>\n" +"                            </div>"}// 填充内容$('#prize-list').html(list);$('#prize-reward').html(reward);// 获取所以的背景标签,循环设置背景颜色及旋转的角度值和倾斜转换值[].slice.call(document.querySelectorAll('.prize-li'), 0).forEach(function (item, i) {if(count == 2) {return false;}item.style.backgroundColor = '#ffffff';if(i%2 == 0) {item.style.backgroundColor = '#FCE9C1';}item.style.transform = 'rotate(' + (360 / count * i + liRotate) + 'deg) skewX(' + liRotate +'deg)';});// 如果只有两个奖品则单独处理下样式if(count == 2) {$('#prize-list').find('.prize-li').css('width', '2.49rem');$('#prize-list').find('.prize-li').css('height', '4.98rem');$('#prize-list').find('.prize-li').css('top', '0rem');$('#prize-list').find('.prize-li').css('left', '0rem');$('#prize-list').find('.prize-li').eq(0).css('background', '#FCE9C1');$('#prize-list').find('.prize-li').eq(1).css('left', '2.49rem');}
}

接下来就是前端抽奖操作的实现,用户点击抽奖按钮进行抽奖,首先的话前端设置一个抽奖锁,在用点击抽奖按钮的时候将锁锁上,在这次抽奖过程完成后,将锁打开,用户点击抽奖,请求抽奖接口,然后接口进行抽奖逻辑处理,将最终的奖品信息返回给前端,也就是奖品的ID,然后前端根据ID拿到奖品的标签下标,前端使用transition方式渲染装盘,最终将指针停在转盘内接口返回的奖品那里,用户看到中奖信息进行下步操作。这样的话,整个前端抽奖过程就完成了

此部分是转盘抽奖部分代码

/*** 抽奖操作* @param type*/
function lottery(type) {// 请求后端接口,获取中奖信息$.ajax({url: wheel_lottery,type: 'get',headers: {"token": token,"Accept": "application/json","appid": appId},data: {type: type // 用户抽奖类型},success:function (res) {if (!res.data) {flag = true;return false;}var type    = res.data.type;var title   = res.data.title;var img     = res.data.img;var rid     = res.data.record_id;myScore     = res.data.my_score;pid         = res.data.prize_id;// 更新积分及抽奖次数等信息count--;$('#prize-count').html(count);// 获取当前奖项标签的下标,用于计算转盘转动的角度值var code = $("#prize-reward").find(".prize-item[data-id=" + pid + "]").index();if(code == -1) { // 未查询到标签flag = true;return false;}// 转盘转动,设置转盘转动的角度值var e = 3600 - (code * prizeDeg) - prizeDeg / 2;$('.prize-content').css({'transition': 'transform 6s cubic-bezier(0.25, 0.1, 0.01, 1)', 'transform': "rotate(" + e + "deg)"});setTimeout(function () { // 消息提示$('#mask').css('display', 'block');$('#my-score').html(myScore);if (type == 1) {$('#prize-score').css('display', 'block');$('#result-img').attr('src', img);$('#result-score').html(title);} else if(type == 2) {$('#prize-result').css('display', 'block');$('#result-icon').attr('src', img);$('#result-reward').html(title);$('#go-write').attr('data-id', rid)} else {$('#prize-no').css('display', 'block');}// 转盘复位$('.prize-content').css({'transition': '','transform': "rotate(0deg)"});// 打开抽奖锁flag = true;}, 6000);},error:function(res) {}})
}

二、后端接口抽奖逻辑开发

其实关于设置展示的奖项条数、获取奖项数据这些接口没啥要讲的,就是一般的增删改查操作,其中有一点需要特别强调下,一般的抽奖系统是能够控制每个奖项的中奖概率的,这里我们通过设置weight【权重】字段值来控制中奖概率,weight值越大中奖几率越高。

确定中奖奖品代码:

  /*** 抽奖方法,返回中奖商品id** @return mixed*/private function _getLotteryPrize(){// 所有奖品的权重和id$prize = WheelAwardPrize::select('id', 'weight')->orderBy('created_at', 'desc')->limit(Tenancy::setting('wheel.prize_num'))->get();$data = [];       // 最终抽奖奖品数组$totalWeight = 0; // 初始化总权重foreach ($prize as $item) {$weight = $item['weight']; // 当前奖品权重值if (!$weight) {           // 没有权重值跳过continue;}for ($i = 1; $i <= $weight; $i++) { // 循环添加进最终数组$data[] = $item;}$totalWeight += $weight; // 增加总权重}// 随机获取中奖奖品下标$index = rand(0, $totalWeight - 1);// 返回奖品IDreturn $data[$index]['id'];}

下面就主要讲一下抽奖逻辑的实现:首先和前端一样,设置抽奖锁,完成抽奖记得释放锁,同一个用户完成一次抽奖流程后,才能允许进行下一次抽奖。然后确定用户中奖奖品,从库中查询全部奖品ID【奖品主键值】和weight【权重】,然后循环weight,将当前weight的ID和weight放置在一个二维数组内,这样我们就能拿到一个不存放weight为0,且长度为weight总值的奖品数组,然后根据数组长度随机取一个范围内的值,这样我们就拿到当前index的值,进而拿到中奖奖品的ID。最后的话,就是根据中奖奖品ID,生成用户的中奖信息,如果是虚拟货币的话,就直接给用户充值,实物奖励的话,前端就提示用户填写收货信息,在把奖品ID和用户中奖记录的ID通过接口返回给前端。

抽奖流程代码:

/*** 进行抽奖,返回奖品ID** @return array*/
public function lottery(Request $request)
{// 加锁$lockKey = RedisKey::getApiLockKey('wheel/lottery', ['member_id' => auth()->user()->member_id]);if (Cache::has($lockKey)) { // 有锁throw new BusinessException('您的操作太过频繁,请稍后重试');}// 抽奖操作,获取中奖奖项ID$pid = $this->_getLotteryPrize();if (!$pid) {throw new BusinessException('奖品正在准备中,请稍后重试');}// 添加奖励记录$records = new WheelAwardRecord();$records->member_id = auth()->user()->member_id;$records->wheel_id  = $pid;$records->type      = $request->type;$records->date      = date('Ymd');$asset = Asset::where('member_id', auth()->user()->member_id)->first();if ($request->type == 2) { // 积分抽奖$config = Tenancy::setting('wheel');if (!$asset || ($asset->score < $config['consume_score'])) {Cache::forget($lockKey);throw new BusinessException('积分不足');}// 扣除用户积分$this->user->changeScore(-$config['consume_score'], ScoreReason::REASON_WHEEL, Task::TASK_WHEEL);}// 发放奖励$prizes = WheelAwardPrize::where('id', $pid)->first();if ($prizes->type == 1) { // 奖励虚拟货币$records->status = 3;// 增加用户积分$this->user->changeScore($config['consume_score'], ScoreReason::REASON_WHEEL, Task::TASK_WHEEL);} else if($prizes->type == 2) { // 实物奖励$records->status = 1; // 默认状态待发放} else { // 未中奖$records->status = 3; // 默认状态待发放}$records->save();// 移除锁Cache::forget($lockKey);return ['prize_id'  => $pid,'type'      => $prizes->type,'title'     => $prizes->title,'img'       => $prizes->icon,'record_id' => $records->id,'my_score'  => 600];
}

不知不觉来北京已经一年半,明天就要结束北漂的生活,准备回老家休息一段时间,然后10号左右去上海找工作。

因为我现在做的是小说阅读方面的开发,所以趁着休息的这段时间出一篇小说阅读器的博文,大概是《一文带你掌握H5小说阅读器的开发,仿起点读书阅读器》,解析小说阅读器的开发过程,这个开发的话是准备仿照起点阅读阅读器开发,从0-1搭建小说阅读器。

让CSS3中Transform属性带你一文实现炫酷的转盘抽奖效果相关推荐

  1. html中的transform属性,CSS3中transform属性

    8种机械键盘轴体对比 本人程序员,要买一个写代码的键盘,请问红轴和茶轴怎么选? 一.Transform描述: ransform是变形,改变的意思.在CSS3中transform主要包括以下几种:旋转r ...

  2. css3中transform属性及用法

    通过transform属性来实现transform的各种变形处理功能.一般Safari3.1以上浏览器.Google Chrome 8以上的浏览器.Firefox 4 以上的浏览器及Opera 10以 ...

  3. 关于——css3新增属性有哪些?css3中新增属性(部分总结)

    css3中新增属性(部分总结) 一.css3新增边框属性 border-color:为边框设置多种颜色 border-image:属性是使用图片来创建边框 border-radius:圆角边框 box ...

  4. CSS3中font-face属性的用法详解

    CSS3中font-face属性的用法详解 @font-face是CSS3中的一个模块,主要是把自定义的Web字体嵌入到你的网页中,随着@font-face模块的出现,我们在Web的开发中使用字体不怕 ...

  5. 浅谈CSS3中display属性的Flex布局,关于登陆页面属性框的设置

    声明:本文转发自三里屯柯南的浅谈CSS3中display属性的Flex布局http://www.cnblogs.com/xuyuntao/articles/6391728.html 基本概念 采用Fl ...

  6. fillcolor是什么意思_详解css3中 text-fill-color属性

    text-fill-color是什么意思呢?单单从字面上来看就是"文本填充颜色",不过它实际也是设置对象中文字的填充颜色,和color的效果很相似.如果同时设置text-fill- ...

  7. 使用 CSS3 实现转盘抽奖效果

    微信和大型商场常常会有转盘抽奖的活动,比如上海移动和教授的抽取积分活动等.我们可以通过CSS3的transform属性来实现转盘的旋转.同时,transition属性实现过渡动画,它具有四个子属性,依 ...

  8. css3中的属性选择器有哪些,CSS3中属性选择器使用方法详解

    css中属性选择器我感觉与css2有比较大的区别了,下面我整理了一些关于css3选择器的使用例子,希望对各位会带来帮助. 注释: 一.属性选择器除了IE6不支持外,其他的浏览器都能支持. 二.E[at ...

  9. CSS3中display属性的Flex布局

    编译之后的效果很明显,界面的布局也很合理,看起来很清晰.那么究竟这个属性是干嘛用的呢? Flex是Flexible Box的缩写,意为"弹性布局",用来为盒状模型提供最大的灵活性. ...

最新文章

  1. ASP.NET强大的性能
  2. 《jQuery Mobile入门经典》—— 第 1 章 了解jQuery Mobile
  3. Java解析HTML
  4. 腾讯云Centos升级python2到python3
  5. REALTEK 2.5G PCIE网卡 RTL8125BG-CG支持PXE免驱简介
  6. python消费kafka逻辑处理导致cpu升高_大数据技术之一次KAFKA消费者异常引起的思考...
  7. HDU - 4746预处理莫比乌斯反演
  8. 【Docker】Docker 启动prometheus报错 parsing YAML file /etc/prometheus/prometheus.yml: yaml: unmarshal
  9. 火狐浏览器 附件组件 Xpath 安装
  10. 仙剑奇侠传五破解方法(虚拟机版)
  11. Prometheus 结合cAdvisor、AlertManager、node-exporter 监控容器并实现邮箱告警
  12. 如何将ppt压缩到最小?
  13. Python中tkinter模块的学习记录(一)
  14. 使用JIRA管理项目工单
  15. 语言模型训练工具SRILM
  16. 最小二乘蒙特卡洛模拟方法-美式期权定价
  17. PCL与MFC的搭建(VS2013+1.8.0PCL)超详细!!!
  18. 设计模式:Decorator--装饰器模式
  19. 史上最简单OpenWRT教程!三分钟三步搞定!碉堡了
  20. Linux 权限r w s,Linux下s、t、i、a特殊权限

热门文章

  1. scala中yeild举例用法
  2. Umi + AntD Pro 项目搭建
  3. ECMAScript 6中数组新方法
  4. 嵌入式算法3---滑动平均滤波法
  5. 数据大于long long最大值,处理方法 子串加法运算
  6. input标签的tabindex属性 a标签的tabindex属性
  7. Stata 字符变量处理
  8. python基础----Anaconda Jupyter Notebook
  9. mac计算机的,Mac计算机快捷键
  10. imac装ubuntu系统步骤