让CSS3中Transform属性带你一文实现炫酷的转盘抽奖效果
Posted 修复BUG中
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了让CSS3中Transform属性带你一文实现炫酷的转盘抽奖效果相关的知识,希望对你有一定的参考价值。
前端时间有个需求是客户端双端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);
// 返回奖品ID
return $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 BusinessExcepti以上是关于让CSS3中Transform属性带你一文实现炫酷的转盘抽奖效果的主要内容,如果未能解决你的问题,请参考以下文章