Vue3 实现转盘抽奖

Vue3 实现转盘抽奖

组件封装版:

数据结构:

[{
name: '名称',
pic: '',
id: 0,
}]

使用方式:

<TurntableLottery :listData='listData' :pointerImg='pointerImg' :index="intKey" @onChange="onChange" />

// <TurntableLottery :listData='数据组' :pointerImg='指针图' :boxImg='背景图' :index="命中的id" @onChange="回调结果" />

组件:

<!-- s转盘 抽奖 -->
<template>
	<div class="body">
		<div class="for_box" :style="boxStyle">
			<div class="for_list" :style="styleFor(index)" v-for="(item,index) in listData" :key="index">
				<img class="for_img" :src="item.pic" />
				<div class="for_name">{{item.name}}</div>
			</div>
			<div class="for_lin" :style="styleFor(index)" v-for="(item,index) in listData" :key="index"></div>
		</div>
		<div class="for_tap" :style="`background-image:url(${pointerImg});`" @click="tapGo"> </div>
	</div>

</template>
<script setup>
	import {
		ref,
		defineProps,
		defineEmits
	} from 'vue';

	/* 子组件接收值 */
	const Props = defineProps({
		listData: {
			type: Array, //列表
			value: [{
				name: '名称',
				pic: '',
				id: 0,
			}]
		},
		boxImg: {
			type: String //转盘背景图
		},
		pointerImg: {
			type: String //指针背景图
		},
		index: {
			type: Number //命中列表的id值
		}
	})
	const emits = defineEmits(['onChange']);

	//转盘
	let boxStyle = ref(`background-image:url(${Props.boxImg});`);

	//转盘中间 按钮
	const tapGo = () => {
		let deg = 360 / Props.listData.length; //平均每个度
		let toDeg = 0; //命中坐标
		let index = 0;

		boxStyle.value = `
		transform: rotate(0deg);
		background-image:url(${Props.boxImg});
		`

		//搜索要命中的位置
		for (var i = 0; i < Props.listData.length; i++) {
			index = i;
			if (Props.listData[i].id == Props.index) {
				toDeg = i * deg; //命中位置
				break;
			}
		}
		toDeg = toDeg + 1080

		//定时恢复
		setTimeout(() => {
			console.log(toDeg, Props.index);

			boxStyle.value = `
			transform: rotate(${toDeg}deg);
			transition: transform 5s ease-in-out;
			background-image:url(${Props.boxImg});
			`
		}, 50)

		//给父组件传值
		emits("onChange", {
			index: index,
			data: Props.listData[index],
		});

	}

	/* 循环均分模块  */
	function styleFor(index) {
		let angle = (360 / Props.listData.length) * index; //平均每个度
		return ` transform: rotate(${angle}deg); `;
	}
</script>

<style scoped>
	/* 中间按钮 */
	.for_tap {
		width: 15vw;
		height: 22vw;
		float: left;
		border-radius: 100%;
		background-size: 100% 100%;
		z-index: 3;
		position: absolute;
		left: 41vw;
		top: 31vw;
	}

	.for_tap:active {
		opacity: 0.7;
	}

	.for_lin {
		width: 100%;
		height: 2px;
		float: left;
		background-color: #fd800888;
		position: absolute;
		left: 0vw;
		top: 40vw;
	}

	.for_img {
		width: 10vw;
		height: 10vw;
		float: left;
		margin-top: 5vw;
		background-size: cover;
		position: absolute;
	}

	.for_name {
		width: 100%;
		float: left;
		font-size: 2.5vw;
		position: absolute;
	}

	.for_list {
		width: 100%;
		height: 100%;
		float: left;
		position: absolute;
		padding: 10px;
		box-sizing: border-box;

		display: flex;
		justify-content: center;
	}

	/* 转盘 */
	.for_box {
		width: 80vw;
		height: 80vw;
		float: left;
		border-radius: 50%;
		position: relative;
		background-color: #fd800899;
		background-size: cover;
		border: solid 20px #fd800888;
	}

	.body {
		width: 100%;
		float: left;
		display: flex;
		align-items: center;
		justify-content: center;
		overflow: hidden;
		position: relative;
	}
</style>

原始代码:

<template>
	<div class="body">
		<div class="for_box" :style="boxStyle">
			<div class="for_list" :style="styleFor(index)" v-for="(item,index) in listData" :key="index">
				<img class="for_img" :src="item.pic" />
				<div class="for_name">{{item.name}}</div>
			</div>
			<div class="for_lin" :style="styleFor(index)" v-for="(item,index) in listData" :key="index"></div>
		</div>
		<div class="for_tap" :style="`background-image:url(${pointerImg});`" @click="tapGo"> </div>
	</div>


	<!-- 演示数据 -->
	<div style="float:left;margin-top:50px;z-index:99;">
		<span style="float:left;margin-top:10px;">演示数据,想中哪个?</span>
		<button v-for="(item,index) in listData" :key="index" @click="tapOk(index)"
			:style="index == intKey ? 'background-color:aquamarine;float:left;margin:10px;':'float:left;margin:10px;'">
			{{index+1}}
		</button>
	</div>
</template>
<script setup>
	import {
		ref
	} from 'vue';

	//奖项列表  《《《《《《《
	let listData = ref([{
			name: '玫瑰',
			pic: 'https://img1.baidu.com/it/u=1125656938,422247900&fm=253&fmt=auto&app=120&f=JPEG',
			id: 0,
		},
		{
			name: '手表',
			pic: 'https://img1.baidu.com/it/u=2631716577,1296460670&fm=253&fmt=auto&app=120&f=JPEG',
			id: 1,
		},
		{
			name: '苹果',
			pic: 'https://img2.baidu.com/it/u=2611478896,137965957&fm=253&fmt=auto&app=138&f=JPEG',
			id: 2,
		},
		{
			name: '棒棒糖',
			pic: 'https://img2.baidu.com/it/u=576980037,1655121105&fm=253&fmt=auto&app=138&f=PNG',
			id: 3,
		},
		{
			name: '娃娃',
			pic: 'https://img2.baidu.com/it/u=4075390137,3967712457&fm=253&fmt=auto&app=138&f=PNG',
			id: 4,
		},
		{
			name: '木马',
			pic: 'https://img1.baidu.com/it/u=2434318933,2727681086&fm=253&fmt=auto&app=120&f=JPEG',
			id: 5,
		},

	])

	//转盘背景图
	let boxStyle = ref(``); //转盘背景图 《《《《《《《
	let pointerImg = ref(`http://uii.linwute.com/upload/2023/9/zhizhen.png`); //指针图 《《《《《《《
	let intKey = ref(1); //命中位置 (列表哪个) 《《《《《《《

	//转盘中间 按钮
	const tapGo = () => {
		let deg = 360 / listData.value.length; //平均每个度
		let toDeg = 0; //命中坐标

		//搜索要命中的位置
		for (var i = 0; i < listData.value.length; i++) {
			if (listData.value[i].id == intKey.value) {
				toDeg = 360 - (i * deg); //命中位置
				break;
			}
		}

		boxStyle.value = `
		transform: rotate(${toDeg + 1080}deg);
		transition: transform 5s ease-in-out;
		`
	}

	/* 循环均分模块  */
	function styleFor(index) {
		let angle = (360 / listData.value.length) * index; //平均每个度
		return ` transform: rotate(${angle}deg); `;
	}

	/* 演示按钮 */
	function tapOk(index) {
		intKey.value = index;
		boxStyle.value = `transform: rotate(0deg);`
	}
</script>

<style scoped>
	/* 中间按钮 */
	.for_tap {
		width: 15vw;
		height: 22vw;
		float: left;
		border-radius: 100%;
		background-size: 100% 100%;
		z-index: 3;
		position: absolute;
		left: 41vw;
		top: 31vw;
	}

	.for_tap:active {
		opacity: 0.7;
	}

	.for_lin {
		width: 100%;
		height: 2px;
		float: left;
		background-color: #fd800888;
		position: absolute;
		left: 0vw;
		top: 40vw;
	}

	.for_img {
		width: 10vw;
		height: 10vw;
		float: left;
		margin-top: 5vw;
		background-size: cover;
		position: absolute;
	}

	.for_name {
		width: 100%;
		float: left;
		font-size: 2.5vw;
		position: absolute;
	}

	.for_list {
		width: 100%;
		height: 100%;
		float: left;
		position: absolute;
		padding: 10px;
		box-sizing: border-box;

		display: flex;
		justify-content: center;
	}

	/* 转盘 */

	.for_box {
		width: 80vw;
		height: 80vw;
		float: left;
		border-radius: 50%;
		position: relative;
		background-color: #fd800899;
		background-size: cover;
		border: solid 20px #fd800888;
	}

	.body {
		width: 100%;
		float: left;
		display: flex;
		align-items: center;
		justify-content: center;
		overflow: hidden;
		position: relative;
	}
</style>
507 Views
分享你的喜爱
linwute
linwute

我要像梦一样自由,像大地一样宽容;
在艰辛放逐的路上,点亮生命的光芒;
我要像梦一样自由,像天空一样坚强;
在曲折蜿蜒的路上,体验生命的意义;

留下评论

您的电子邮箱地址不会被公开。 必填项已用 * 标注