First, draw a disk
Implementation idea:
- Divide the disc equally according to the number of prizes and calculate the Angle and approximate width of a prize
- Set the midpoint of the bottom edge of each grid prize at the origin of the disk
- According to the order of the calculation of the need to rotate the Angle of each grid prize, and rotate to their respective positions, spread full 360 degree disc
- Position the pointer to the center of the disk
The prize data
const prizeList = [
{
name: "The prize 1".color: "red"
},
{
name: "The prize 2".color: "orange"
},
{
name: "The prize 3".color: "yellow"
},
{
name: "The prize four.".color: "green"
},
{
name: "Award 5".color: "hotpink"
},
{
name: "The prize 6".color: "blue"
},
{
name: "The prize 7".color: "purple"
},
{
name: "The prize 8".color: "gray"}]data() {
return {
prizeLists: [].angleList: []}}Copy the code
Calculate the rotation Angle
initPrizeList() {
const length = this.prizeList.length // Number of prizes
const width = parseInt(3.14 * 300 / length) // The approximate width of each prize, since the lucky draw pointer will only be fixed in the middle of the prize, so it only needs to calculate the approximate width, there is overlap will not affect the page vision and results
const halfWidth = -parseInt(width / 2) // The value that the initial position of the prize needs to deviate from
const average = 360 / length // Divide the disk evenly, the Angle occupied by each prize
const half = average / 2 // The middle position of each prize is also the Angle at which the pointer ends
list.forEach((item, index) = > {
const angle = - (index * average + half) // Calculate the rotation Angle of each prize in turn, starting at 12 o 'clock
item.style = `transform: rotate(${angle}deg); width: ${width}px; marginLeft: ${halfWidth}px` // First move all the prizes to the state of "lower midpoint to the origin of the disk", then rotate to the Angle calculated in the previous step
this.angleList.push(index * average + half) // Collect the Angle in the middle of each prize
})
return [...list]
}
Copy the code
Page structure
<div class="lottery-wrap">
<img class="lottery-pointer" :src="pointerImg" @click="beginRotate" alt="">
<div class="prize-wrap" :style="rotateStyle">
<div class="prize-list">
<div class="prize-item" :style="item.style" v-for="(item, index) in prizeLists" :key="index">
<span class="prize-icon" :style="{backgroundColor: item.color}"></span>
<span class="prize-name">{{item.name}}</span>
</div>
</div>
</div>
</div>
Copy the code
.lottery-wrap {
width: 300px;
height: 300px;
border-radius: 50%;
background-color: rgba(0.0.0.0.4);
position: relative;
}
.lottery-pointer {
width: 100px;
height: 100px;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
z-index: 2;
}
.prize-wrap {
width: 100%;
height: 100%;
}
.prize-list {
position: absolute;
width: 100%;
height: 100%;
top: 0;
left: 0;
.prize-item {
position: absolute;
// width: 120px;
height: 150px;
box-sizing: border-box;
padding-top: 30px;
top: 0;
left: 50%;
// margin-left: -60px;
transform-origin: 50% 100%;
display: flex;
flex-direction: column;
// justify-content: center;
align-items: center;
.prize-icon {
width: 30px;
height: 30px; }}}Copy the code
Now the structure of the disk is complete
Two, turn
Implementation idea:
- When users click on the draw, they just need to turn the disk and keep the pointer still
- After obtaining the prize returned by the back end in the real scene, the front end only needs to calculate the Angle and perform the rotation animation.
- Each time a new Angle is evaluated, the page can be updated by evaluating properties
- It should be noted that the rotation Angle should be saved after each rotation, and users should not click repeatedly
The initial data
data() {
isRotating: false.// Prevent repeated click rotation
rotateAngle: 0 // The current position
}
Copy the code
Spinning method
methods: {
beginRotate() {
const { isRotating, rotateAngle, angleList } = this
if (isRotating) reurn // Prevent repeated clicks
this.isRotating = true
const angle = rotateAngle + 5 * 360 + angleList[4] - (rotateAngle % 360) // For visual purposes, the default rotation is more than 5 turns
this.rotateAngle = angle // Save the rotation Angle for the next rotation
setTimeout(() = > {
this.isRotating = false // After this drawing is over, the next drawing can be carried out
console.log("A winner! }}}, 5000)Copy the code
Evaluate the property update page
computed: {
rotateStyle() {
return `
transition: transform 5000ms ease-in-out;
transform: rotate(The ${this.rotateAngle}deg)
`}}Copy the code
So far completed the turntable lottery function
Next, transform to a more configurable component
Three, transformed into configurable components
Implementation idea: as far as possible to configure the data into parameters, including background, pointer pictures, prize list, rotation time, animation effect, rotation number, etc
Receive parameters
props: {
prizeList: {
type: Array.default: () = >[]},pointerImg: {
type: String.default: () = > require("./img/xxx.png")},lotteryBgImg: {
type: String.default: () = > require("./img/xxx.png")},duration: {
type: Number.default: () = > 5000
},
circle: {
type: Number.default: () = > 5
},
mode: {
type: String.default: () = > "ease-in-out"}}Copy the code
Transformation of rotation method
methods: {
rotating(index) {
const { isRotating, duration, circle, count, rotateAngle, angleList } = this
if (isRotating) reurn // Prevent repeated clicks
this.isRotating = true
const angle = rotateAngle + circle * 360 + angleList[index] - (rotateAngle % 360) // For visual purposes, the default rotation is more than 5 turns
this.rotateAngle = angle // Save the rotation Angle for the next rotation
setTimeout(() = > {
this.isRotating = false // After this drawing is over, the next drawing can be carried out
console.log("A winner! }, duration) } }Copy the code
Modification of computing attributes
computed: {
rotateStyle() {
return `
transition: transform The ${this.duration}ms The ${this.mode};
transform: rotate(The ${this.rotateAngle}deg)
`}}Copy the code
In the actual scenario, it is safer to control the final prize by the back end, so it needs to be changed to be controlled by the parent component, while the click event is in the self component, so the following changes are made
<div class="lottery-wrap">
<img class="lottery-pointer" :src="pointerImg" @click="beginRotate" alt="">
<div class="prize-wrap" :style="rotateStyle">
<div class="prize-list">
<div class="prize-item" :style="item.style" v-for="(item, index) in prizeLists" :key="index">
<span class="prize-icon" :style="{backgroundColor: item.color}"></span>
<span class="prize-name">{{item.name}}</span>
</div>
</div>
</div>
</div>
Copy the code
methods: {
beginRotate() {
this.$emit("beginRotate") // Let the parent control the click and pass in the winning result
},
rotating(index) {
const { isRotating, duration, circle, count, rotateAngle, angleList } = this
if (isRotating) reurn // Prevent repeated clicks
this.isRotating = true
const angle = rotateAngle + circle * 360 + angleList[index] - (rotateAngle % 360) // For visual purposes, the default rotation is more than 5 turns
this.rotateAngle = angle // Save the rotation Angle for the next rotation
setTimeout(() = > {
this.isRotating = false // After this drawing is over, the next drawing can be carried out
this.rotateOver()
}, duration)
},
rotateOver() {
this.$emit("rotateOver") // Notify the parent when the lottery ends}}Copy the code
/ / the parent component<turntable ref="lottery" :prizeList="prizeList" @beginRotate="beginRotate" @rotateOver="rotateOver"></turntable>
Copy the code
methods: {
beginRotate() {
// Call the backend interface here to get the final award result
const res = .......
this.$refs.lottery.rotating(res)
},
rotateOver() {
// Complete the remaining steps at the end of the draw}}Copy the code
At this point, the completion of the simple function of the lottery component