最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • 手撸简易大转盘抽奖组件

    正文概述 掘金(首席切图仔)   2021-03-05   442

    一、画圆盘

    实现思路:

    • 根据奖品数量将圆盘等分,计算出一份奖品所占的角度和大致宽度
    • 将每一格奖品底边的中点设置在圆盘的原点处
    • 根据顺序计算每一格奖品所需要旋转的角度,并旋转至各自的位置,铺满360度圆盘
    • 将指针定位至圆盘中心处

    奖品数据

    const prizeList = [
      {
          name: "奖品1",
          color: "red"
      },
      {
          name: "奖品2",
          color: "orange"
      },
      {
          name: "奖品3",
          color: "yellow"
      },
      {
          name: "奖品4",
          color: "green"
      },
      {
          name: "奖品5",
          color: "hotpink"
      },
      {
          name: "奖品6",
          color: "blue"
      },
      {
          name: "奖品7",
          color: "purple"
      },
      {
          name: "奖品8",
          color: "gray"
      }
    ]
    data() {
       return {
          prizeLists: [],
          angleList: []
        }
    }
    

    计算旋转角度

    initPrizeList() {
    	const length = this.prizeList.length // 奖品数量
        const width = parseInt(3.14 * 300 / length) // 每份奖品的大致宽度,由于抽奖指针最终只会定格在奖品的最中间位置,所以这里只需计算大致宽度即可,有重叠部分对页面视觉和结果无影响
        const halfWidth = -parseInt(width / 2) // 奖品初始位置需要偏离的值
        const average = 360 / length // 均分圆盘,每份奖品所占的角度
        const half = average / 2 // 每份奖品的中间位置,也是指针最终停留的角度
        list.forEach((item, index) => {
        	const angle = - (index * average + half) // 依次计算每份奖品的旋转角度,初始位置都是朝向12点方向
            item.style = `transform: rotate(${angle}deg); width: ${width}px; marginLeft: ${halfWidth}px` // 首先将奖品全部移动到"下边的中点至圆盘原点"的状态,再旋转到上一步计算出来的角度
            this.angleList.push(index * average + half) // 收集每个奖品中间位置的角度
        })
        return [...list]
    }
    
    

    页面结构

    <div class="lottery-wrap">
        <img class="lottery-pointer" :src="pointerImg" @click="beginRotate" >
        <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>
    
    .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;
            }
        }
    }
    

    手撸简易大转盘抽奖组件

    至此圆盘的结构就完成了

    二、转起来

    实现思路:

    • 用户点击抽奖后,只需要转动圆盘,指针保持不动即可
    • 真实场景中获取后端返回的奖项后,前端只需计算出角度,再执行旋转动画即可,
    • 每次计算处新角度,可以通过计算属性来让页面更新
    • 需要注意的是每次旋转完要保存旋转角度,并且防止用户重复点击

    初始数据

    data() {
    	isRotating: false, // 防止重复点击旋转
        rotateAngle: 0 // 当前位置
    }
    

    旋转方法

    methods: {
    	beginRotate() {
        	const { isRotating, rotateAngle, angleList } = this
            if (isRotating) reurn // 防止重复点击
            this.isRotating = true
            const angle = rotateAngle + 5 * 360 + angleList[4] - (rotateAngle % 360) // 为了视觉好看,这里默认旋转5圈以上
            this.rotateAngle = angle // 保存此次旋转角度,下次旋转需要用到
            
            setTimeout(() => {
            	this.isRotating = false // 本次抽奖结束,可以进行下一次
                console.log("抽中了!)
            }, 5000)
        }
    }
    

    计算属性更新页面

    computed: {
    	rotateStyle() {
        	return `
                transition: transform 5000ms ease-in-out;
                transform: rotate(${this.rotateAngle}deg)
            `
        }
    }
    

    至此完成了转盘的抽奖功能

    接下来改造成可配置性更高的组件

    三、改造成可配置的组件

    实现思路:尽可能将可配置的数据改造成参数传入,包含背景图、指针图片、奖品列表、旋转时间、动画效果、旋转圈数等

    接收参数

    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"
        }
    }
    

    旋转方法改造

    methods: {
    	rotating(index) {
        	const { isRotating, duration, circle, count, rotateAngle, angleList } = this
            if (isRotating) reurn // 防止重复点击
            this.isRotating = true
            const angle = rotateAngle + circle * 360 + angleList[index] - (rotateAngle % 360) // 为了视觉好看,这里默认旋转5圈以上
            this.rotateAngle = angle // 保存此次旋转角度,下次旋转需要用到
            
            setTimeout(() => {
            	this.isRotating = false // 本次抽奖结束,可以进行下一次
                console.log("抽中了!)
            }, duration)
        }
    }
    

    计算属性改造

    computed: {
    	rotateStyle() {
        	return `
                transition: transform ${this.duration}ms ${this.mode};
                transform: rotate(${this.rotateAngle}deg)
            `
        }
    }
    

    实际场景中,最终获奖奖品一般由后端控制比较安全,因此这里需要改造成由父组件来控制, 而点击事件是在自组件中,所以做如下改造

    <div class="lottery-wrap">
        <img class="lottery-pointer" :src="pointerImg" @click="beginRotate" >
        <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>
    
    methods: {
    	beginRotate() {
        	this.$emit("beginRotate") // 让父组件来控制点击,并传入获奖结果
        },
        rotating(index) {
        	const { isRotating, duration, circle, count, rotateAngle, angleList } = this
            if (isRotating) reurn // 防止重复点击
            this.isRotating = true
            const angle = rotateAngle + circle * 360 + angleList[index] - (rotateAngle % 360) // 为了视觉好看,这里默认旋转5圈以上
            this.rotateAngle = angle // 保存此次旋转角度,下次旋转需要用到
            
            setTimeout(() => {
            	this.isRotating = false // 本次抽奖结束,可以进行下一次
                this.rotateOver()
            }, duration)
        },
        rotateOver() {
        	this.$emit("rotateOver") // 抽奖结束,通知父组件
        }
    }
    
    // 父组件
    <turntable ref="lottery" :prizeList="prizeList" @beginRotate="beginRotate" @rotateOver="rotateOver"></turntable>
    
    methods: {
    	beginRotate() {
        	// 这里调用后端接口拿到最终获奖结果
            const res = .......
            this.$refs.lottery.rotating(res)
        },
        rotateOver() {
        	// 完成抽奖结束的剩余步骤
        }
    }
    

    至此,就完成简易功能的抽奖组件


    起源地下载网 » 手撸简易大转盘抽奖组件

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元