javascript+HTML5 Canvas绘制转盘抽奖


Posted in Javascript onMay 16, 2020

之前做过的项目中,有需要抽奖转盘功能的。项目已经完工一段时间了,也没出现什么严重的bug,所以现在拎出来分享给大家。

功能需求

1、转盘要美观,转动效果流畅。
2、转盘上需要显示奖品图片,并且奖品是后台读取的照片和名字。
3、转动动画完成后要有相应提示。
4、获取的奖品具体算法在数据库里操作,前端只提供最后的效果展示。 

知识要点

1、引用了一个jq插件:awardRotate,用来实现更智能化的转动。

2、使用canvas标签和对应的html5 api 进行操作。

正文

引用大转盘样式

.lunck_draw_wrap{display:block;width:95%;margin-right:auto;}
 .lunck_draw_wrap .turnplate{display:block;width:106%; position:relative;}
 .lunck_draw_wrap .turnplate canvas.item{left:1px;
 position: relative;
 top:9px;
 width:100%;}
 .lunck_draw_wrap .turnplate img.pointer{ height:37.5%;
 left:34.6%;
 position: absolute;
 top:30%;
 width:31.5%;}

转盘插件所需参数:

var turnplate ={
 restaraunts:[],//大转盘奖品名称
 lucky:[],//奖品内容
 colors:[],//大转盘奖品区块对应背景颜色
 goodsimgArr:[],//奖品图片页面标签
 outsideRadius:175,//大转盘外圆的半径
 textRadius:140,//大转盘奖品位置距离圆心的距离
 insideRadius:65,//大转盘内圆的半径
 startAngle:0,//开始角度
 bRotate:false//false:停止;ture:旋转
 };

由参数可知,我们需要从服务端获取相应的奖品名称,奖品内容,奖品图片页面标签等信息,再对大转盘进行渲染。
所以我们的第一步操作就是向服务端发送请求获取对应的奖品信息,并且遍历到生成大转盘所需的数组参数里:

$.each(data.list,function(key, value){
 turnplate.restaraunts.push(value.data0);
 turnplate.lucky.push(value.data1);
 turnplate.goodsimgArr.push(getLuckyImg + value.data4);
 if(key %2==0)
 turnplate.colors.push("#fff");
 else
 turnplate.colors.push("#5fcbd4");
 })

data.list是我获取来的奖品json数据:

[
 {
 "data0":"一等奖",
 "data1":"iphone6s",
 "data2":"0",
 "data3":"0",
 "data4":"201510161406303384.png",
 "data5":"XXXX网络科技",
 "data6":"浙江省衢州市柯城区XXXXX",
 "data7":"0570-XXXXXX"
 },......
 ]

由于客户要求奖品没有“谢谢参与”,所以最低奖品也为“优胜奖”,所以在遍历奖品之后,插入有关“优胜奖”的渲染描述即可:

turnplate.goodsimgArr.push('../images/hongbao.png')
 turnplate.restaraunts.push("优胜奖");
 turnplate.colors.push("#5fcbd4");
 //页面所有元素加载完毕后执行drawRouletteWheel()方法对转盘进行渲染
 preloadimages(turnplate.goodsimgArr).done(function(images){
 drawRouletteWheel();
 });

因为图片加载需要时间,而使用canvas复制图片需要图片加载完成后才能绘制,所以我使用了preloadimages,让所有奖品图片都加载完毕后进行大转盘的渲染工作:

//对奖品图片预加载
 function preloadimages(arr){
 var newimages =[], loadedimages =0
 var postaction =function(){}//此处增加了一个postaction函数
 var arr =(typeof arr !="object")?[arr]: arr
 function imageloadpost(){
 loadedimages++
 if(loadedimages == arr.length){
 postaction(newimages)//加载完成用我们调用postaction函数并将newimages数组做为参数传递进去
 }
 }
 for(var i =0; i < arr.length; i++){
 newimages[i]=newImage()
 newimages[i].src = arr[i]
 newimages[i].onload =function(){
 imageloadpost()
 }
 newimages[i].onerror =function(){
 imageloadpost()
 }
 }
 return{//此处返回一个空白对象的done方法
 done:function(f){
 postaction = f || postaction
 }
 }
 }

绘制转盘代码:

function drawRouletteWheel(){
 var canvas = document.getElementById("wheelcanvas");
 if(canvas.getContext){
 //根据奖品个数计算圆周角度
 var arc =Math.PI /(turnplate.restaraunts.length /2);
 var ctx = canvas.getContext("2d");
 //在给定矩形内清空一个矩形
 ctx.clearRect(0,0,422,422);
 //strokeStyle 属性设置或返回用于笔触的颜色、渐变或模式
 ctx.strokeStyle ="rgba(0,0,0,0)";
 //font 属性设置或返回画布上文本内容的当前字体属性
 ctx.font ='bold 18px Microsoft YaHei';
 for(var i =0; i < turnplate.restaraunts.length; i++){
 //根据当前奖品索引 计算绘制的扇形开始弧度
 var angle = turnplate.startAngle + i * arc;
 //根据奖品参数 绘制扇形填充颜色
 ctx.fillStyle = turnplate.colors[i];
 //开始绘制扇形
 ctx.beginPath();
 //arc(x,y,r,起始角,结束角,绘制方向) 方法创建弧/曲线(用于创建圆或部分圆)
 //绘制大圆
 ctx.arc(212,212, turnplate.outsideRadius, angle, angle + arc,false);
 //绘制小圆
 ctx.arc(212,212, turnplate.insideRadius, angle + arc, angle,true);
 ctx.stroke();
 ctx.fill();
 //锁画布(为了保存之前的画布状态)
 ctx.save();
 //----绘制奖品开始----
 //奖品默认字体颜色
 ctx.fillStyle ="#fff";
 var text = turnplate.restaraunts[i];
 var lukyname = turnplate.lucky[i];
 var line_height =17;
 //translate方法重新映射画布上的 (0,0) 位置
 ctx.translate(212+Math.cos(angle + arc /2)* turnplate.textRadius,212+Math.sin(angle + arc /2)* turnplate.textRadius);
 //rotate方法旋转当前的绘图
 ctx.rotate(angle + arc /2+Math.PI /2);
 //绘制奖品图片
 var img =newImage();
 img.src = turnplate.goodsimgArr[i];
 ctx.drawImage(img,-17,35);
 //由于设计的转盘色块是交错的,所以这样可以实现相邻奖品区域字体颜色不同
 if(i %2==0){
 ctx.fillStyle ="#f7452f";
 }
 //将字体绘制在对应坐标
 ctx.fillText(text,-ctx.measureText(text).width /2,0);
 //设置字体
 ctx.font =' 14px Microsoft YaHei';
 //绘制奖品名称
 if(text !="优胜奖"){
 ctx.fillText(lukyname,-ctx.measureText(lukyname).width /2,25);
 }else{
 ctx.fillText("优麦币",-ctx.measureText("优麦币").width /2,25);
 }
 //把当前画布返回(插入)到上一个save()状态之前
 ctx.restore();
 ctx.save();
 //----绘制奖品结束----
 }
 }
 }

每一步基本上都有注释,对于canvas方法有不理解的可以百度,或者查询我上面分享的中文手册。
html代码为:

<divclass="lunck_draw_wrap">
 <divclass="turnplate"style=" background-size:100%100%;">
 <canvasclass="item"id="wheelcanvas"width="422px"height="422px"></canvas>
 <imgclass="pointer"style="top:0px; left:0px; width:100%; height:100%;"src="../images/chouzhang12.png"/>
 <imgclass="pointer"src="../images/hianji .png"/>
 </div>
 </div>

效果图:

javascript+HTML5 Canvas绘制转盘抽奖

点击事件执行代码:

$('.lunck_draw_wrap').delegate("img.pointer","click",function(){
 if(turnplate.bRotate)return;
 turnplate.bRotate =!turnplate.bRotate;
 $.getJSON("../AJAX/lottery.ashx","",function(data){
 //1090系统配置错误,1091用户未登陆或用户数据异常,1092用户剩余积分不足,1093未中奖
 hideInput("code",data.code)
 if(data.code.toString()=="1090"){
 iosalert("系统配置错误")
 }elseif(data.code.toString()=="1091"){
 iosalert("用户未登陆或用户数据异常")
 }elseif(data.code.toString()=="1092"){
 iosalert("用户剩余积分不足")
 }elseif(data.code.toString()=="1094"){
 iosalert("超过每日抽奖次数")
 }
 else{
 var upoint =0;
 upoint = parseInt($("#uPoint").html())- parseInt($("#sPoint").html());
 $("#uPoint").html(upoint);
 if(data.isWin =='true'){
 item = getArrayIndex(turnplate.restaraunts, data.name);
 rotateFn(item +1,"恭喜获得,"+ turnplate.restaraunts[item]);
 }
 else{
 rotateFn(0,"恭喜获得优胜奖!");
 }
 }
 })
 });

上面的代码实现了基本上的逻辑,还需要一个转动转盘的方法来响应服务端传过来的结果:

//旋转转盘 item:奖品位置; txt:提示语;
 var rotateFn =function(item, txt){
 //根据传进来的奖品序号 计算相应的弧度
 var angles = item *(360/ turnplate.restaraunts.length)-(360/(turnplate.restaraunts.length *2));
 if(angles <270){
 angles =270- angles;
 }else{
 angles =360- angles +270;
 }
 //强制停止转盘的转动
 $('#wheelcanvas').stopRotate();
 //调用转动方法,设置转动所需参数和回调函数
 $('#wheelcanvas').rotate({
 //起始角度
 angle:0,
 //转动角度 +1800是为了多转几圈
 animateTo: angles +1800,
 duration:8000,
 callback:function(){
 iosSuccess(txt);
 turnplate.bRotate =!turnplate.bRotate;
 if($("#code").val()!="1093"){
 delayLoad(getHttpPrefix +"graphicdetails.html?lukyid="+ $("#code").val())
 }
 }
 });
 };

好了 主要的功能代码都已分享完毕了,还有些工具方法不理解的,可以留言 我会补充进去的。

总结

canvas是html5很强大的一张王牌,可以实现许多绚丽的效果,希望本文可以帮到一些正在学习使用canvas的朋友们。

Javascript 相关文章推荐
escape、encodeURI 和 encodeURIComponent 的区别
Mar 02 Javascript
node.js开发中使用Node Supervisor实现监测文件修改并自动重启应用
Nov 04 Javascript
javascript实现时间格式输出FormatDate函数
Jan 13 Javascript
JS组件Bootstrap Table使用方法详解
Feb 02 Javascript
使用BootStrap和Metroui设计的metro风格微网站或手机app界面
Oct 21 Javascript
微信小程序之MaterialDesign--input组件详解
Feb 15 Javascript
JS中实现函数return多个返回值的实例
Feb 21 Javascript
jQuery实现手势解锁密码特效
Aug 14 jQuery
说说node中的可读流和可写流的区别
Jun 01 Javascript
JS浮点数运算结果不精确的Bug解决
Aug 01 Javascript
javascript实现blob加密视频源地址的方法
Aug 08 Javascript
electron实现静默打印的示例代码
Aug 12 Javascript
深入浅析JavaScript中的constructor
Apr 19 #Javascript
js点击返回跳转到指定页面实现过程
Aug 20 #Javascript
javascript html5摇一摇功能的实现
Apr 19 #Javascript
一些实用性较高的js方法
Apr 19 #Javascript
jQuery移动端日期(datedropper)和时间(timedropper)选择器附源码下载
Apr 19 #Javascript
JavaScript中创建对象的模式汇总
Apr 19 #Javascript
使用PHP+JavaScript将HTML页面转换为图片的实例分享
Apr 18 #Javascript
You might like
PHP 远程文件管理,可以给表格排序,遍历目录,时间排序
2009/08/07 PHP
thinkphp实现图片上传功能
2016/01/13 PHP
Zend Framework基本页面布局分析
2016/03/19 PHP
基于Swoole实现PHP与websocket聊天室
2016/08/03 PHP
js实现addClass,removeClass,hasClass的函数代码
2011/07/13 Javascript
JavaScript基本编码模式小结
2012/05/23 Javascript
JS取request值以及自动执行使用示例
2014/02/24 Javascript
Jquery实现自定义窗口随意的拖拽
2014/03/12 Javascript
angularjs指令中的compile与link函数详解
2014/12/06 Javascript
Javascript基础教程之break和continue语句
2015/01/18 Javascript
JS显示表格内指定行html代码的方法
2015/03/31 Javascript
[原创]jQuery实现合并/追加数组并去除重复项的方法
2018/04/11 jQuery
vue升级之路之vue-router的使用教程
2018/08/14 Javascript
微信公众号生成新浪短网址的实现(快速生成)
2019/08/18 Javascript
vue实现的多页面项目如何优化打包的步骤详解
2020/07/19 Javascript
利用Python读取文件的四种不同方法比对
2017/05/18 Python
python excel使用xlutils类库实现追加写功能的方法
2018/05/02 Python
python中的字符串内部换行方法
2018/07/19 Python
详解Python中的type和object
2018/08/15 Python
Python提取频域特征知识点浅析
2019/03/04 Python
Python使用dict.fromkeys()快速生成一个字典示例
2019/04/24 Python
Python模块的定义,模块的导入,__name__用法实例分析
2020/01/07 Python
CK巴西官方网站:Calvin Klein巴西
2019/07/19 全球购物
SQL面试题
2013/04/30 面试题
英语专业大学生求职简历的自我评价
2013/10/18 职场文书
大学生职业生涯规划书模版
2013/12/30 职场文书
十佳护士获奖感言
2014/02/18 职场文书
事业单位分类改革实施方案
2014/03/21 职场文书
《莫泊桑拜师》教学反思
2014/04/23 职场文书
《少年王勃》教学反思
2014/04/27 职场文书
高中学校对照检查材料
2014/08/31 职场文书
基层党员对照检查材料
2014/09/24 职场文书
党员查摆剖析材料
2014/10/10 职场文书
MySQL数字类型自增的坑
2021/05/07 MySQL
sql查询结果列拼接成逗号分隔的字符串方法
2021/05/25 SQL Server
详解JAVA中的OPTIONAL
2021/06/14 Java/Android