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>
668 Views