【JavaScript】制作一个抽奖转盘页面

这篇具有很好参考价值的文章主要介绍了【JavaScript】制作一个抽奖转盘页面。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

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

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,//抽奖概率自己写,传入预定奖品的index
		success:(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. 实现方法

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

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,//转动耗时3s
			success(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浏览看看,正常的话,运行效果图如下
【JavaScript】制作一个抽奖转盘页面

💡小提示

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

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

可根据其它的需求改

到此结束,如阅读中有遇到什么问题,请在文章结尾评论处留言,ヾ( ̄▽ ̄)ByeBye文章来源地址https://www.toymoban.com/news/detail-470678.html

到了这里,关于【JavaScript】制作一个抽奖转盘页面的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • HarmonyOS 自定义抽奖转盘开发(ArkTS)

    本篇 Codelab 是基于画布组件、显式动画,实现的一个自定义抽奖圆形转盘。包含如下功能: 1.  通过画布组件 Canvas,画出抽奖圆形转盘。 2.  通过显式动画启动抽奖功能。 3.  通过自定义弹窗弹出抽中的奖品。 ● Stack组件:堆叠容器,子组件按照顺序依次入栈,后一个子组

    2024年02月07日
    浏览(44)
  • html+css+js实现转盘抽奖

     

    2024年01月25日
    浏览(36)
  • 基于uniapp ts 实现微信小程序动态抽奖幸运大转盘

     这是view视图层布局,内容中有注释,这里就不过多标注 以下是数据层实现方法  注:本人技术比较菜,体谅体谅,有好的建议欢迎提出来 上述是个人理解。描述不恰当的地方欢迎指正。一起进步~

    2024年02月03日
    浏览(53)
  • HTML旅游景点网页作业制作——旅游中国11个页面(HTML+CSS+JavaScript)

    👨‍🎓学生HTML静态网页基础水平制作👩‍🎓,页面排版干净简洁。使用HTML+CSS页面布局设计,web大学生网页设计作业源码,这是一个不错的旅游网页制作,画面精明,排版整洁,内容丰富,主题鲜明,非常适合初学者学习使用, 这个实例比较全面,有助于同学的学习,本文将

    2024年02月05日
    浏览(60)
  • Unity制作一个简单的登入注册页面

    1.创建Canvas组件 首先我们创建一个Canvas画布,我们再在Canvas画布底下创建一个空物体,取名为Resgister。把空物体的锚点设置为全屏撑开。  2.我们在Resgister空物体底下创建一个Image组件,改名为bg。我们也把它 的锚点设置为全屏撑开状态。接下来我们把我们的图片UI素材导入进

    2024年02月12日
    浏览(58)
  • 【JavaScript】3.1 项目实践:制作一个简单的网页应用

    在此章节中,我们将学习如何使用JavaScript创建一个简单的网页应用。这将是一个待办事项列表应用,用户可以添加新的待办事项,标记已完成的事项,以及删除事项。通过这个项目,我们将学习如何使用JavaScript操作DOM,处理事件,以及使用localStorage进行数据存储。 我们的待

    2024年02月05日
    浏览(44)
  • 分享微信抽奖小程序制作步骤_微信抽奖小程序怎么开发

    各位商家在节日期间做活动的时候,都希望用更少的费用去或者更好的宣传和推广的效果。比较常见的就是抽奖活动小程序。无须玩家下载,通过微信扫码或者指定入口就可以参与。 方便,效果又好。 那么,性价比高的抽奖活动小程序怎么做? 来看看微信小游戏发布和制作的

    2024年02月10日
    浏览(37)
  • 【Unity】制作一个简单的菜单栏页面并实现其功能

    1.创建UGUI组件 (1)接下来我们制作一下整个菜单页面的UGUI,其大致制作效果如下图,有一下细节我们需要注意就是设置好Canvas的分辨率并且在创建UI组件的过程每一个物体我们对需要设置好对应的锚点让它固定在这个位置,不会随分辨率大小而改变。造成后面比较麻烦,所

    2024年02月11日
    浏览(42)
  • ❤️创意网页:制作一个绚丽的烟花效果(HTML、CSS和JavaScript实现)

    ✨ 博主: 命运之光 🌸 专栏: Python星辰秘典 🐳 专栏: web开发(简单好用又好看) ❤️ 专栏: Java经典程序设计 ☀️ 博主的其他文章: 点击进入博主的主页 前言: 欢迎踏入我的Web项目专栏,一段神奇而令人陶醉的数字世界! 🌌 在这里,我将带您穿越时空,揭开属于

    2024年02月17日
    浏览(82)
  • PyQt5利用Qt Designer制作一个可以拖动获取文件信息的页面

    前言 本篇在讲什么 用pyqt5制作一个简单的程序,拖动文件或脚本可以读取文件信息 本篇适合什么 适合 初学PyQt5 的小白 本篇需要什么 对 Python 语法有简单认知 对 Qt 有简单认知 依赖 Pycharm 编辑器 本篇的特色 具有全流程的 图文教学 重实践,轻理论,快速上手 提供全流程的

    2024年01月15日
    浏览(67)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包