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 相关文章推荐
用 Javascript 验证表单(form)中的单选(radio)值
Sep 08 Javascript
jQuery-Tools-overlay 使用介绍
Jul 14 Javascript
JS正则中的RegExp对象对象
Nov 07 Javascript
jquery判断元素是否隐藏的多种方法
May 06 Javascript
浅谈javascript中基本包装类型
Jun 03 Javascript
jQuery预加载图片常用方法
Jun 15 Javascript
JavaScript截取指定长度字符串点击可以展开全部代码
Dec 04 Javascript
BootStrap下拉菜单和滚动监听插件实现代码
Sep 26 Javascript
jsp 网站引入外部css或者js失效问题解决
Oct 31 Javascript
vue增加强缓存和版本号的实现方法
May 01 Javascript
vue实现在线预览pdf文件和下载(pdf.js)
Nov 26 Javascript
微信小程序自定义顶部组件customHeader的示例代码
Jun 03 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下把数组保存为文件格式的实例应用
2010/02/08 PHP
PHP SPL标准库之接口(Interface)详解
2015/05/11 PHP
详解 PHP加密解密字符串函数附源码下载
2015/12/18 PHP
PHP中的Trait 特性及作用
2016/04/03 PHP
Laravel 加载第三方类库的方法
2018/04/20 PHP
Laravel核心解读之异常处理的实践过程
2019/02/24 PHP
PHP Beanstalkd消息队列的安装与使用方法实例详解
2020/02/21 PHP
yii框架结合charjs实现统计30天数据的方法
2020/04/04 PHP
用javascript实现自定义标签
2007/05/08 Javascript
CodeMirror2 IE7/IE8 下面未知运行时错误的解决方法
2012/03/29 Javascript
屏蔽script注入小例子
2013/11/12 Javascript
js与运算符和或运算符的妙用
2014/02/14 Javascript
JavaScript检测弹出窗口是否已经关闭的方法
2015/03/24 Javascript
JavaScript 数组some()和filter()的用法及区别
2016/05/20 Javascript
js实现弹窗暗层效果
2017/01/16 Javascript
纯js实现的积木(div层)拖动功能示例
2017/07/19 Javascript
JavaScript创建对象方式总结【工厂模式、构造函数模式、原型模式等】
2018/12/19 Javascript
关于element-ui的隐藏组件el-scrollbar的使用
2019/05/29 Javascript
[02:49:21]2019完美盛典全程录像
2019/12/08 DOTA
[01:06:30]DOTA2-DPC中国联赛定级赛 Phoenix vs DLG BO3第二场 1月9日
2021/03/11 DOTA
Python实现Linux下守护进程的编写方法
2014/08/22 Python
Python的爬虫框架scrapy用21行代码写一个爬虫
2017/04/24 Python
python 集合 并集、交集 Series list set 转换的实例
2018/05/29 Python
python 计算两个列表的相关系数的实现
2019/08/29 Python
python中文分词库jieba使用方法详解
2020/02/11 Python
在django中实现choices字段获取对应字段值
2020/07/12 Python
浅析python 通⽤爬⾍和聚焦爬⾍
2020/09/28 Python
请解释在new与override的区别
2012/10/29 面试题
单位人事专员介绍信
2014/01/11 职场文书
《美丽的丹顶鹤》教学反思
2014/04/22 职场文书
离婚财产处理协议书
2014/09/30 职场文书
大学生操行评语大全
2014/12/31 职场文书
从事会计工作年限证明
2015/06/23 职场文书
毕业酒会致辞
2015/07/29 职场文书
基于JavaScript实现省市联动效果
2021/06/22 Javascript
Python实战实现爬取天气数据并完成可视化分析详解
2022/06/16 Python