位置: IT常识 - 正文

【JavaScript】制作一个抽奖转盘页面(javascript生成器)

编辑:rootadmin
【JavaScript】制作一个抽奖转盘页面

推荐整理分享【JavaScript】制作一个抽奖转盘页面(javascript生成器),希望有所帮助,仅作参考,欢迎阅读内容。

文章相关热门搜索词:javascript生成器,javascript制作,javascript制作,javascriptz,运用javascript制作网页,javascript怎么弄,javascript生成器,javascript怎么弄,内容如对您有帮助,希望把文章链接给更多的朋友!

开发H5项目,有时会遇到一个需求,需要制作抽奖转盘的网页,这个实现步骤,如果拿现成的改来做是容易的,但是想着全靠自己做是不容易的,下面会讲,全靠自己做,能掌握到吗

目录一览1.设计网页2. 编写脚本3. 编写模块4. 实现方法1. 绘制转盘2. 开始抽奖4.运行效果1.设计网页

首先创建一个网页文件,例如index.html,制作抽奖转盘页面,源代码如下,通过修改样式<style>里设置好背景色,还有转盘组件的位置,再加一个抽奖按钮,写好大概逻辑,还有需要调用的一些方法

<!DOCTYPE html><html><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><title>Turntable 转盘</title><style>body{background-color: #333;}.box{text-align: center;}.box #box{width: 280px;height: 280px;}.box button{margin-top: 20px;padding: 0.6em 2.5em;font-size: 1em;border-radius: 10px;border-color: rgba(0, 0, 0, 0.4);color: #fff;background: linear-gradient(#eee,#f50);}</style></head><body><div class="box"><div id="box"></div><div><button id="btnStart">抽奖</button></div></div><script type="module">import Turntable from './turntable.js';//引用模块window.onload = () => {//...加载脚本}</script></body></html>2. 编写脚本

接着,写一个加载脚本的处理逻辑,代码如下,使用Turntable对象创建前,需要先引用一个模块

const t = new Turntable({window,elemId: 'box',});//TODO: 最多7个const resouce = [{image: './img/e7b2e38c8d66613d1dd869b199031d8e.jpeg',title: '特等奖'},{image: './img/b5ff4601d9679f502f8f9e737bdd7049.jpeg',title: '谢谢惠顾'},{image: './img/e7b2e38c8d66613d1dd869b199031d8e.jpeg',title: '一等奖'},{image: './img/b5ff4601d9679f502f8f9e737bdd7049.jpeg',title: '谢谢惠顾'},{image: './img/e7b2e38c8d66613d1dd869b199031d8e.jpeg',title: '二等奖'},{image: './img/e7b2e38c8d66613d1dd869b199031d8e.jpeg',title: '三等奖'},{image: './img/b5ff4601d9679f502f8f9e737bdd7049.jpeg',title: '谢谢惠顾'},];//加载图片资源可能有延迟,通过异步处理Promise.all(resouce.map((item)=>{return new Promise((resolve,reject)=>{let img = new Image();img.onload = () => {item.image = img;resolve(item);};img.onerror = reject;img.src = item.image;});})).then((res)=>{t.draw({// mode:1,goods:res,});});//设置按钮点击事件document.getElementById('btnStart').onclick = () => {t.onStart({// index: 3,//抽奖概率自己写,传入预定奖品的indexsuccess:(res)=>{// console.log('ok', res);const good = res.goods[res.index];if (good.title) {if (good.title.indexOf('奖')>=0) alert(`🙂恭喜恭喜您!抽到奖品${good.title}.`)else alert('🙆‍很遗憾!未中奖.')}else {alert(`🙂恭喜恭喜您!抽到奖品${res.index+1}.`)}}});}3. 编写模块

接下来,看上面有引用的一个模块文件turntable.js,没有的就把它新建好,在一个模块中去实现上面未实现的调用方法,代码如下

export default class Turntable {//定义私有属性#elemBgImg;#elemPointerImg;#bgImgCanvas;#pointerImgCanvas;/** * 构造函数 * */constructor(e){const { document } = e.window;// 获取占位元素(盒子)const elemBox = document.getElementById(e.elemId);// 创建元素const elemBgImg = document.createElement('img');const elemPointerImg = document.createElement('img');const size = elemBox.offsetWidth;// 设置元素样式elemBox.style.display = 'inline-block';elemBox.style.position = 'relative';elemBgImg.style.transform = `rotate(0deg)`;elemBgImg.style.pointerEvents = 'none';//屏蔽触摸点击elemPointerImg.style.pointerEvents = 'none';elemBgImg.style.position = 'absolute';elemPointerImg.style.position = 'absolute';elemPointerImg.style.margin = 'auto';elemBgImg.style.margin = 'auto';elemBgImg.style.left = 0;elemBgImg.style.top = 0;elemBgImg.style.right = 0;elemBgImg.style.bottom = 0;elemPointerImg.style.left = 0;elemPointerImg.style.top = 0;elemPointerImg.style.right = 0;elemPointerImg.style.bottom = 0;elemBgImg.width = size;elemBgImg.height = size;elemPointerImg.width = size*0.3;elemPointerImg.height = size*0.3;//将元素添加到占位元素(盒子)组件中elemBox.appendChild(elemBgImg);elemBox.appendChild(elemPointerImg);this.#elemBgImg = elemBgImg;this.#elemPointerImg = elemPointerImg;//转盘const bgImgCanvas = document.createElement('canvas');bgImgCanvas.width = size;bgImgCanvas.height = size;this.#bgImgCanvas = bgImgCanvas;//指针const pointerImgCanvas = document.createElement('canvas');pointerImgCanvas.width = elemPointerImg.width;pointerImgCanvas.height = elemPointerImg.height;this.#pointerImgCanvas = pointerImgCanvas;}/** * 销毁 * */destory(){this.#bgImgCanvas.remove();this.#pointerImgCanvas.remove();}/** * 绘制转盘组件 * */draw(config){//...}/** * 开始抽奖 * */onStart(config){//...}}4. 实现方法【JavaScript】制作一个抽奖转盘页面(javascript生成器)

接下来,写方法的实现细节要复杂得多,如果看着比较吃力,就需要补充数学知识哦,关键点是三角图形学中的勾股定理,请慢慢摸索,边学边做

1. 绘制转盘

先实现绘制转盘组件方法draw(),其中用到了数学的一个知识点:三角函数,代码如下

class Turntable {//定义私有属性#goods=[];#pointerDeg = 0;#mode = 0;//.../** * 绘制转盘组件 * */draw(config){const data = {padding: 5,//组件内边距goods: ['#f00','#0f0','#00f'],//默认三基色填充礼品区pointerColor: '#fa0',//指针色borderWidth: 10,//边框大小borderColor: '#fa0',//边框色imgSize: 40,//礼品图片大小mode:0,//工作模式: 0:转动转盘;1:转动指针};Object.assign(data,config);this.#mode = data.mode==0 ? 0 : 1;const bgImgCanvas = this.#bgImgCanvas;const bgImgCtx = bgImgCanvas.getContext('2d');const coodrinte = {padding: data.padding,r: bgImgCanvas.width/2-data.padding};coodrinte.centerO = coodrinte.padding + coodrinte.r;//先绘制转盘底座bgImgCtx.strokeStyle = data.borderColor;bgImgCtx.lineWidth = data.borderWidth;bgImgCtx.fillStyle = '#eee';bgImgCtx.beginPath();bgImgCtx.arc(coodrinte.centerO,coodrinte.centerO,coodrinte.r,0,Math.PI*2);bgImgCtx.fill();bgImgCtx.stroke();//再绘制转盘上的bgImgCtx.strokeStyle = 'rgba(255,255,255,0.3)';bgImgCtx.lineWidth = Math.max(1,data.borderWidth/3);bgImgCtx.textAlign = 'center';const r = coodrinte.r-bgImgCtx.lineWidth;//转盘角度(弧边)let startAngle = 0;let endAngle = 0;data.goods.forEach((item,index)=>{let good = {proportion: Math.round(1000/data.goods.length)/1000,//默认平分概率};switch(typeof item){case 'string':if (item.charAt(0)=='#') good.bgColor=item;else good.title=item;break;case 'object':Object.assign(good,item);break;default:throw new Error('定义参数goods有误');}good.startAngle = startAngle;good.endAngle = good.startAngle+Math.PI*2*good.proportion;//计算角度let angle = (good.endAngle-good.startAngle)/2-Math.PI*0.5+(index*good.proportion*Math.PI*2);//余弦函数cosA:表示在一个直角三角形中,∠A(非直角)的邻边与三角形的斜边的比let x = Math.cos(angle)*(r/2);//正弦函数sinA:表示在一个直角三角形中,∠A(非直角)的对边与三角形的斜边的比let y = Math.sin(angle)*(r/2);// console.log('angle '+angle, 'x='+x+',y='+y);good.center = {x:coodrinte.centerO+x,y:coodrinte.centerO+y,};data.goods[index] = good;startAngle = good.endAngle;});//绘制分布在转盘中的图案data.goods.forEach((item,index)=>{if (item.bgColor){bgImgCtx.fillStyle = item.bgColor;}//画划分的区域(弧边)bgImgCtx.beginPath();bgImgCtx.moveTo(coodrinte.centerO,coodrinte.centerO);bgImgCtx.arc(coodrinte.centerO,coodrinte.centerO,r,item.startAngle-Math.PI*0.5,item.endAngle-Math.PI*0.5);bgImgCtx.closePath();if (!item.bgColor) {bgImgCtx.stroke();bgImgCtx.fillStyle = '#f50';}else{}bgImgCtx.fill();//是否是转动底盘if (this.#mode==0) {bgImgCtx.save();let cX = item.center.x;let cY = item.center.y;let angle = Math.round(Math.atan(Math.abs(coodrinte.centerO-cY)/Math.abs(coodrinte.centerO-cX))*180/Math.PI);// console.log(index+'. angle > '+angle)//TODO: 暂时适配最多7个switch(angle){case 0:if (cX<coodrinte.centerO) angle+=90;else angle-=90;break;case 90:angle=0;break;default:if (cX<coodrinte.centerO){if (cY<coodrinte.centerO){angle+=90;}else if (angle<20) {angle+=45;}else if (angle<38) {angle-=25;}else if (angle<=40) {angle+=10;}else if (angle==45) {}else if (angle<=60) {angle-=30;}else {angle+=10;}}else{if (cY<coodrinte.centerO){angle=270-angle;}else{angle-=90;}}}if (angle!=0){//旋转角度,以转盘中心点对齐angle=Math.PI*(angle/180);bgImgCtx.translate(cX,cY);bgImgCtx.rotate(angle);bgImgCtx.translate(-cX,-cY);}}if (item.image) {bgImgCtx.drawImage(item.image,item.center.x-data.imgSize*0.5,item.center.y-data.imgSize*0.5,data.imgSize,data.imgSize);}if (item.title) {bgImgCtx.fillStyle = '#fff';bgImgCtx.fillText(item.title,item.center.x,item.image ? (item.center.y+data.imgSize*0.9) : item.center.y);}if (this.#mode==0){bgImgCtx.restore();}//画辅助线// bgImgCtx.beginPath();// bgImgCtx.moveTo(coodrinte.centerO,coodrinte.centerO);// bgImgCtx.lineTo(item.center.x,item.center.y);// bgImgCtx.stroke();});this.#goods = data.goods;this.#elemBgImg.src = bgImgCanvas.toDataURL();//绘制指针const pointerImgCanvas = this.#pointerImgCanvas;const pointerImgCtx = pointerImgCanvas.getContext('2d');const pointerData = {r: pointerImgCanvas.width/2};pointerData.r1 = pointerData.r*0.36;pointerData.r2 = pointerData.r*0.60;pointerImgCtx.fillStyle = data.pointerColor;startAngle = Math.PI*1.58;endAngle = startAngle + Math.PI*1.86;pointerImgCtx.lineWidth = 2;pointerImgCtx.beginPath();pointerImgCtx.arc(pointerData.r,pointerData.r,pointerData.r2,startAngle,endAngle);pointerImgCtx.lineTo(pointerData.r,0);pointerImgCtx.closePath();pointerImgCtx.fill();pointerImgCtx.stroke();//将绘制的图形设置到图片元素this.#elemPointerImg.src = pointerImgCanvas.toDataURL();}}2. 开始抽奖

实现开始抽奖方法onStart(),可通过传入参数对象config,修改默认配置,代码如下,抽奖结果会通过回调方法succes()返回

class Turntable {//定义私有属性#animing = false;//.../** * 开始抽奖 * */onStart(config){if (this.#animing) return;//防止双击(误操作)this.#animing = true;const data = {minRotationNum: 3,//至少转动圈数duration:3,//转动耗时3ssuccess(res){},//结束时回调index: -1,//抽得预定奖品,默认随机};Object.assign(data,config);const goods = this.#goods;if (data.index<0 || data.index>=goods.length) {//抽得随机奖品data.index = Math.trunc(Math.random()*10%goods.length);}const elemActive = this.#mode==0 ? this.#elemBgImg : this.#elemPointerImg;const style = elemActive.style;const pointerDeg = this.#pointerDeg;const index = data.index;//定义动画结束监听const listener = (event) => {event.preventDefault();elemActive.removeEventListener('transitionend', listener);//重置动画样式style.transition = 'none';style.transform = `rotate(${this.#pointerDeg}deg)`;//结束和回调this.#animing = false;data.success({goods,index});};elemActive.addEventListener('transitionend', listener, false);//处理过渡动画let inDeg = Math.round(goods[index].startAngle/Math.PI*180);let outDeg = Math.round(goods[index].endAngle/Math.PI*180);let deg = (Math.round(Math.random()*(outDeg-inDeg)))+inDeg;// console.log('rand '+index, `${inDeg}°~${outDeg}° ${deg}° current:${pointerDeg}`);//转盘是反向旋转的if (this.#mode==0) deg = 360-deg;deg += (Math.round(Math.random()*10)+data.minRotationNum)*360;//简化角度this.#pointerDeg = deg%360;//修改完样式,就可开始动画style.transition = `all ${data.duration}s ease-out`;style.transform = `rotate(${deg}deg)`;}}4.运行效果

讲到最后,用浏览器打开网页index.html浏览看看,正常的话,运行效果图如下

💡小提示

试试修改传入的参数,例如

t.draw({//...mode:1,//改变为指针转动/...});//...t.onStart({index: 3,//抽奖概率自己写,传入预定奖品的indexsuccess:(res)=>{ //...}});

可根据其它的需求改

到此结束,如阅读中有遇到什么问题,请在文章结尾评论处留言,ヾ( ̄▽ ̄)ByeBye

本文链接地址:https://www.jiuchutong.com/zhishi/292337.html 转载请保留说明!

上一篇:【爬坑之路一】windows系统下更新升级node版本【亲测有效】(爬坑图片卡通)

下一篇:在树洞中休息的鸣角鸮,纽约马萨皮夸保护区 (© Vicki Jauron, Babylon and Beyond Photography/Getty Images)(在树洞中休息的英语)

  • 微博营销技巧(微博营销技巧众微博的内在美主要表现在多看多写和多少)

    微博营销技巧(微博营销技巧众微博的内在美主要表现在多看多写和多少)

  • iphone13Pro怎么开启120hz高刷(iphone13pro怎么开空调)

    iphone13Pro怎么开启120hz高刷(iphone13pro怎么开空调)

  • 微博如何知道对方拉黑自己(微博如何知道对方已读信息)

    微博如何知道对方拉黑自己(微博如何知道对方已读信息)

  • 京东双十一红包退款了怎么办(京东双十一红包在哪里)

    京东双十一红包退款了怎么办(京东双十一红包在哪里)

  • 苹果XR在通话时怎么录音(苹果xr为什么打电话的时候屏幕不亮)

    苹果XR在通话时怎么录音(苹果xr为什么打电话的时候屏幕不亮)

  • 苹果x怎么背对背充电(苹果手机怎么背对背)

    苹果x怎么背对背充电(苹果手机怎么背对背)

  • 公众号排版怎么做(公众号排版怎么好看)

    公众号排版怎么做(公众号排版怎么好看)

  • 华为p30手机屏幕上的小圆点怎么取消(华为p30手机屏幕尺寸)

    华为p30手机屏幕上的小圆点怎么取消(华为p30手机屏幕尺寸)

  • qq加好友频繁了怎么解除(QQ加好友频繁了多久恢复)

    qq加好友频繁了怎么解除(QQ加好友频繁了多久恢复)

  • 快手流量越来越低的原因(快手流量推广)

    快手流量越来越低的原因(快手流量推广)

  • 抖音认证是什么(抖音认证怎么认证?)

    抖音认证是什么(抖音认证怎么认证?)

  • 苹果x待机时间多久(苹果x待机时间多长正常)

    苹果x待机时间多久(苹果x待机时间多长正常)

  • 中燃慧生活怎么充燃气(中燃慧生活怎么变成壹品慧)

    中燃慧生活怎么充燃气(中燃慧生活怎么变成壹品慧)

  • 华为p30怎么设置美颜(华为p30怎么设置锁屏壁纸)

    华为p30怎么设置美颜(华为p30怎么设置锁屏壁纸)

  • qq怎么设置开机自启(QQ怎么设置开机自启动)

    qq怎么设置开机自启(QQ怎么设置开机自启动)

  • 安卓vsco主动扣钱吗(安卓使用vsco)

    安卓vsco主动扣钱吗(安卓使用vsco)

  • 怎么关掉抖音直播的礼物信息(怎么关掉抖音直播间)

    怎么关掉抖音直播的礼物信息(怎么关掉抖音直播间)

  • 如何下载微信聊天记录(如何下载微信聊天图片)

    如何下载微信聊天记录(如何下载微信聊天图片)

  • 华为免打扰怎么关闭(华为免打扰怎么设置快捷键)

    华为免打扰怎么关闭(华为免打扰怎么设置快捷键)

  • 尼康D850中的HDR怎样使用(尼康850 hdr)

    尼康D850中的HDR怎样使用(尼康850 hdr)

  • mysql查看表结构(MySQL查看表结构代码)

    mysql查看表结构(MySQL查看表结构代码)

  • 陌陌怎么语音聊天(陌陌语音聊天)

    陌陌怎么语音聊天(陌陌语音聊天)

  • win10系统中,新建文件夹假死怎么办?(现在我们来看看windows中的新增内容)

    win10系统中,新建文件夹假死怎么办?(现在我们来看看windows中的新增内容)

  • dlbkbmon.exe是什么进程 dlbkbmon进程有什么作用 安全吗(hipsdaemon.exe是什么)

    dlbkbmon.exe是什么进程 dlbkbmon进程有什么作用 安全吗(hipsdaemon.exe是什么)

  • 公司注销前存货低价标准
  • 税务局做社保关联带什么资料
  • 软件和硬件如何集合使用
  • 累计减税费用是什么意思
  • 冲减以前年度主营业务成本对今年有影响吗
  • 营改增后房地产企业增值税税收筹划存在问题
  • 双倍余额递减法如何计算
  • 试用期社保是按照应发工资缴纳吗
  • 员工食堂餐费可以报销吗
  • 土地评估费计入什么会计科目
  • 公司投资另一个公司的投资款怎么入账
  • 对方收到发票不付款怎么办
  • 免税企业可以开具有税率的增值税专用发票吗
  • 农产品收购发票使用范围
  • 实务操作中的税会差异有哪些?
  • 不同类型商业分布的特征
  • 企业核算方法
  • 资源费用怎么算
  • 公司投资理财产品收益怎么算
  • 大额转账人行审核通过
  • 不动产抵押登记费记什么科目
  • 反避税的意义
  • 预付加油充值卡可以报销吗
  • 公司发行股票如何做账
  • 预付办公用品费用,未收到发票
  • 总结关于现金清查的会计核算分录
  • 员工福利费会计处理
  • electron静默打印
  • zendframework3中文手册
  • 购买免税农产品的会计分录
  • 深入vue3+typescript技术栈
  • bug的定位和跟踪
  • 不良品扣款应入哪个科目
  • 增值税一般纳税人
  • 注销企业账面大量现金如何处理
  • 出纳人员收取现金合法吗
  • 自建厂房折旧算制造费用吗
  • mysql备份原理
  • 固定资产的核算内容包括
  • 印花税申报完成如何缴纳
  • 结转成本计入
  • 什么人适合单干
  • 净资产是资产负债表中的什么
  • 进口原料需要交哪些税
  • 资产减值准备的举例
  • 什么是商业汇票,其种类有哪些
  • 进项税额转出结转还是红冲
  • 债券溢价什么意思
  • 产品包装设计费属什么费用
  • 计提增值税如何做账
  • 税控抵税怎么做分录
  • 购货方享受现金折扣增值税
  • 增加个税信息怎么增加
  • 饭店现金账怎么记账
  • 银行账和实际金额对不上
  • 年度报告应包括哪些
  • mysql in如何优化
  • window 启动
  • solaris11.4安装教程
  • 苹果mac系统中英文切换
  • ubuntu 上不了网
  • Win10 Mobile首个RS2预览版14905快速版今日开始推送
  • msoobe.exe是什么
  • win10系统怎么关闭病毒防护
  • style=display:inline
  • Android游戏开发入门
  • fat32和fat16
  • Unsolved bug in fltk-1.1.10/src/filename_list.cxx for scandir --已解决!
  • 用wasfile.zip智能批量删除文件
  • python3 pygame
  • android在手机上运行
  • jquery移动版
  • express后端
  • android获取屏幕大小
  • 国家税务总局12366纳税服务平台
  • 党建服务中心属于哪个区
  • 总分机构在同一县城企业所得税需要预交吗
  • 工会经费范围税率是多少
  • 增值税税控系统专用设备及技术维护费
  • 查国税发票验证码怎么查
  • 免责声明:网站部分图片文字素材来源于网络,如有侵权,请及时告知,我们会第一时间删除,谢谢! 邮箱:opceo@qq.com

    鄂ICP备2023003026号

    网站地图: 企业信息 工商信息 财税知识 网络常识 编程技术

    友情链接: 武汉网站建设