下面实现抽奖算法及中奖通知的功能。
首先定义指针转动时中奖的角度和未中奖的角度,以便判断用户是否中奖,图中有6个奖项,相应有6个未中奖区域。奖项角度及其他变量定义如下。
1 var totalAngle = 0;
2 var steps = ;
3 var loseAngle = [36, 96, 156, 216, 276, 336];
4 var winAngle = [6, 66, 126, 186, 246, 306];
5 var prizeLevel;
6 var now = 0;
7 var count = 0;
8 var a = 0.01;
9 var outter, inner, timer, running = false;
用户进入抽奖界面后,点击抽奖指针,相应的代码处理如下。
1 $("#inner").click(function {
2 if (running) return;
3 if (count >= 3) {
4 alert("达到最大抽奖次数!");
5 return
6 }
7 $.ajax({
8 url: "data.php",
9 dataType: "json",
10 data: {
11 openid: "<?php echo $_GET["openid"];?>",
12 time: (new Date).valueOf
13 },
14 beforeSend: function {
15 running = true;
16 timer = setInterval(function {
17 i += 5
18 },
19 1)
20 },
21 success: function(data) {
22 // 达到最大抽奖次数
23 if (data.status == "MAX") {
24 alert("您已达到最大抽奖次数!");
25 count = 3;
26 clearInterval(timer);
27 return
28 }
29 // 有中奖时转盘转到相应位置
30 if (data.status == "WIN") {
31 $("#prizename").text(data.prizename);
32 count = 3;
33 clearInterval(timer);
34 prizeLevel = data.prizelevel;
35 start(winAngle[data.prizelevel - 1]);
36 return
37 }
38 // 未中奖则再给机会
39 running = false;
40 count++
41 prizeLevel = null;
42 start
43 },
44 // 未获取JSON返回,前台处理
45 error: function {
46 prizeLevel = null;
47 start;
48 running = false;
49 count++
50 },
51 timeout: 4000
52 })
53 })
点击事件发生时,页面将向data.php文件发送POST请求,将当前用户的OpenID和时间传递过去。data.php中在收到数据后将需要进行一系列的复杂处理,这些在24.5.6节中有详细的讲述下面是一个中奖情形的返回结果。
echo '{"status": "WIN", "prizename": "iPhone 5S", "prizelevel": "2"}';
该情形表示当前已中奖,奖品等级是2,奖品为iPhone 5S。data.php将该JSON数据返回给请求页面,原页面收到数据后,第29~37行代码将进行处理,它将抽奖次数直接置为最大抽奖次数3,并且计算转盘将要旋转的角度,而这个旋转角度也就是奖项的角度,以此确保转盘停止后,指针落点无误。
当中奖数据返回中没有要中奖的标记(第38~42行)或者没有接收到返回的JSON数据时(第44~50行),转盘也需要计算旋转角度,页面将在前台累加抽奖次数,直到达到最大抽奖次数,然后提示用户抽奖次数已经用完。如果返回的JSON数据中显示已经达到最大抽奖次数,则以JSON数据优先作为判断依据(第22~28行)。
当启动转盘转动时,本次抽奖结果已经被旋转角度确定下来了,这是通过start方法实现的。该方法带有一个参数deg。如果中奖,则deg传输进来时就已经是某奖项的角度;如果没有中奖,deg直接传空,这时将随机计算出一个非奖项的角度并赋给它。start方法的实现如下。
1 function start(deg) {
2 deg = deg || loseAngle[parseInt(loseAngle.length * Math.random)];
3 running = true;
4 clearInterval(timer);
5 totalAngle = 360 * 5 + deg;
6 steps = ;
7 now = 0;
8 countSteps;
9 requestAnimFrame(step)
在start函数中,需要根据旋转角度生成本次转动的步骤,这时往往需要添加N个360°,以便在旋转N圈后落到真正的奖项区域。而这个旋转角度以数组的方式保存。其角度差需要逐渐变小,以实现减速旋转最终停下来的效果。该数组的生成代码如下。
1 function countSteps {
2 var t = Math.sqrt(2 * totalAngle / a);
3 var v = a * t;
4 for (var i = 0; i < t; i++) {
5 steps.push((2 * v * i - a * i * i) / 2)
6 }
7 steps.push(totalAngle)
8 }
上面代码将生成一个元素个数非常大的角度列表数组,数组存储到steps中。这里为了简便,生成了一个简化版本,如表24-9所示。
表24-9 旋转角度数组
从表24-9中可以看到,转盘将从0旋转到624.9°,每一步的旋转角度差将越来越小,直到为0,这时将停留到预先生成好的角度上。
而实现旋转的动画效果,是使用HTML5中的window.requestAnimFrame方法实现的,代码如下。
1 window.requestAnimFrame = (function {
2 return window.requestAnimationFrame ||
3 window.webkitRequestAnimationFrame ||
4 window.mozRequestAnimationFrame ||
5 window.oRequestAnimationFrame ||
6 window.msRequestAnimationFrame ||
7 function(callback) {
8 window.setTimeout(callback, 1000 / 60)
9 }
10 });
当动画将每个旋转角度走完以后,需要将中奖结果提示给用户。没有中奖时只需要弹出一个消息框即可,而中奖时需要隐藏转盘、显示中奖区域、显示中奖结果。该部分代码如下。
1 function step {
2 outter.style.webkitTransform = 'rotate(' + steps[now++] + 'deg)';
3 outter.style.MozTransform = 'rotate(' + steps[now++] + 'deg)';
4 outter.style.oTransform = 'rotate(' + steps[now++] + 'deg)';
5 outter.style.msTransform = 'rotate(' + steps[now++] + 'deg)';
6 if (now < steps.length) {
7 requestAnimFrame(step)
8 } else {
9 running = false;
10 setTimeout(function {
11 if (prizeLevel != null) {
12 var levelName= new Array("", "一等奖", "二等奖", "三等奖", "四等奖",
"五等奖", "六等奖")
13 $("#prizelevel").text(levelName[prizeLevel]);
14 $("#result").slideToggle(500); // 显示中奖区域
15 $("#outercont").slideUp(500) // 隐藏转盘
16 } else {
17 alert("亲,继续努力哦!")
18 }
19 },
20 200)
21 }
最终的中奖结果页面如图24-22所示。
图24-22 大转盘中奖页面