一个不会让你空手而归的大转盘组件

611 阅读2分钟

前言

本篇文章封装了一个类似掘金抽奖大转盘组件,先来看看效果(😂匀速的速度 太快了,导致录屏捕捉不到。。。各位看官如果有想体验的可以更换配置项内的速度看看) 1.gif

功能

本组件功能特色:

  • 刷新即可随机生成奖品,不限量
  • 全新玩法、全面升级!100%中奖率,200钻石买不了吃亏买不了上当,却能大奖带回家!😀
  • 随机加速度、减速度
  • 当然奖品也都是可以自定,使用时传指定的奖品图片即可😂
  • 基于vue,其它依赖只包含一个element的信息提示框,可自行替换

开搞

使用案例

/**
 * 九宫格游戏参数
 * @param {Object} pics - 礼品图片对象集合,自行使用import导入进来
 *      @example {gift1:  () => import('')}
 * @param {Object} options - 大转盘配置项
 * @param {Object} giftName - 礼品名称对应的上面pics中图片的名称,因为图片上只有礼品的英文名称相信,所以这里弄了个英文名称相信对应的礼品名称信息
 *      @example {gift1: "Yoyo抱枕"} 做了一层图片文件名与礼品名称之间的映射,方便$message弹出信息
 * @param {number} targetIndex - 中奖的位置
 * @param {Object} positionIndex - 转动顺序规则的映射,默认顺时针,详情可看组件代码配置注释
 * @param {func} clickLottery - 点击抽奖按钮的回调
 * @param {func} complete - 大转盘运行结束的回调
 * @returns {}
 */
<LuckGame 
    :pics="pics"
    :options="options" 
    :giftName="giftName" 
    :targetIndex="targetIndex" 
    @clickLottery="fetchGiftInfo" 
    @complete="showGift"
    >
</LuckGame>

关于组件源码

单文件组件.vue

先讲下大致的实现思路:声明一个定时器,在运行过程中一直存在,当加速、匀速、减速时仅仅改变的是这个定时器的时间(在这里也就是速度this.optionData.speed,这个值越小,触发定时器的频率越快,速度越快;这个定时器实现其实就是反复切换激活元素的位置,达到大转盘“滚动”的效果),其它的一切操作都是围绕这个来的。

<!-- 九宫格 -->
<template>
    <div class="clearfix flex luck-game">
        <div :class="[`luck-unit luck-unit-${i - 1}`]" v-for="i of 9" :key="i" ref="gifts">
            <template v-if="i === 5">
                <div @click="lottery" class="lottery">
                    <slot name="btn">
                        <p class="num">200钻石/次</p>
                        <p class="tit">抽奖</p>
                        <div class="img"></div>
                    </slot>
                </div>
            </template>
        </div>
    </div>
</template>
​
<script>
export default {
    data() {
        return {
            optionData: this.options
        };
    },
​
    props: {
        pics: {
            type: Object,
            require: true
        },
        options: {
            type: Object,
            require: true
        },
        targetIndex: {
            type: [Number, null]
        },
        giftName: {
            type: Object
        },
        /*
        dom顺序            转动顺序
            0, 1, 2            0, 1, 2
            3, 4, 5            8, 4, 3
            6, 7, 8            7, 6, 5
        */
        // 默认顺时针转动规则
        positionIndex: {
            type: Object,
            default() {
                return {
                    3: 5,
                    5: 8,
                    6: 7,
                    7: 6,
                    8: 3
                };
            }
        }
    },
    computed: {
        count() {
            return this.$refs.gifts.length;
        },
        gifts() {
            return this.$refs.gifts;
        },
        index() {
            return this.optionData.index % this.count;
        }
    },
​
    mounted() {
        this.optionData.speed = this.optionData.initSpeed;
        this.initGiftPic();
        this.setActiveDom(this.getIndex(this.optionData.index));
    },
​
    methods: {
        lottery() {
            if (this.optionData.isRunning) {
                this.$message.warning("正在拼命给您领取礼物,请稍后😀");
                return;
            }
            this.$emit("clickLottery");
            this.roll();
        },
        roll() {
            let activeDom = this.getActiveDom();
            activeDom.classList.remove("active");
            ++this.optionData.index;
            // 跳过中间的按钮
            if (this.index === 4) {
                ++this.optionData.index;
            }
            // 对于运行的顺序可以自定义关联规则去修改
            activeDom = 
                this.gifts.find((gift) => gift.classList.contains(`luckunit-${this.getIndex(this.index)}`));
            activeDom.classList.add("active");
​
            this.timer = setTimeout(() => {
                this.roll();
            }, this.optionData.speed);  // 速度值越小,触发定时器的时间越短,故速度越快,控制speed的值即可控制转动的速度
            
            if (!this.optionData.isRunning) {
                let timing = this.getRandomTime(this.optionData.upTimer);
                this.speedUp(timing);
                this.optionData.isRunning = true;
            }
        },
        speedUp(timing) {
            let upTimer;
​
            if (this.optionData.speed >= this.optionData.upMax) {
                upTimer = setTimeout(() => {
                    this.optionData.speed -= this.optionData.upStep;
                    this.speedUp(timing);
                }, timing);
            } else {
                this.speedConstant();
                clearTimeout(upTimer);
            }
        },
        speedConstant() {
            let constantTimer = setTimeout(() => {
                let timing = this.getRandomTime(this.optionData.downTimer);
                this.speedDown(timing);
                clearTimeout(constantTimer);
            }, this.optionData.constantTiming);
        },
        speedDown(timing) {
            let downTimer;
            if (this.optionData.speed >= this.optionData.downMax && this.targetIndex === this.index) {
                this.stop();
                clearTimeout(downTimer);
            } else {
                downTimer = setTimeout(() => {
                    this.optionData.speed += this.optionData.downStep;
                    this.speedDown(timing);
                }, timing);
            }
        },
        stop() {
            this.$message.success(`恭喜您取得${this.getActiveDom().title}`);
            clearTimeout(this.timer);
            this.$emit("complete", this.getActiveDom());
            this.optionData.isRunning = false;
        },
        getRandomTime(timing) {
            return (Math.random() * 4 + 0.4) * timing;
        },
        initGiftPic() {
            this.gifts.forEach((giftBox) => {
                // 设定随机礼物
                const random = Math.ceil(Math.random() * Object.keys(this.pics).length);
                // 排除抽奖按钮
                if (giftBox.children.length === 0) {
                    let giftIndex = `gift${random}`;
                    let url = this.pics[giftIndex];
                    giftBox.style.backgroundImage = `url('${url}')`;
                    giftBox.title = this.giftName[giftIndex];
                }
            });
        },
        getActiveDom() {
            return this.gifts.find((gift) => gift.classList.contains("active"));
        },
        setActiveDom(index) {
            this.gifts[index] && this.gifts[index].classList.add("active");
        },
        getIndex(index) {
            return this.positionIndex[index] || index;
        }
    }
};
</script>
<style lang="less" scoped>
@import url("./index.less");
</style>

less样式文件

这里唯一引入的图片文件就是中间btn的,可以自行引入

.luck-game {
    width: 486px;
    height: 478px;
    margin: auto;
    background: skyblue;
    flex-wrap: wrap;
    .luck-unit {
        float: left;
        flex-basis: 33.33%;
        height: 33.33%;
        border-radius: 16px;
        background-size: 50%;
        background-repeat: no-repeat;
        background-position: center;
        animation: all 0.3s;
        &.active {
            background-color: rgba(255, 204, 149, 0.5);
            &.luck-unit {
                background-size: 58%;
            }
        }
        &.luck-unit-4 {
            background-image: url("~@src/img/common/juejin.svg");
            background-size: contain;
            background-position: center center;
            background-repeat: no-repeat;
            background-size: 92%;
            color: #3e0b08;
            font-weight: 700;
            position: relative;
            cursor: pointer;
            display: flex;
            flex-direction: column;
            justify-content: center;
            .lottery {
                display: flex;
                flex-direction: column;
                justify-content: center;
            }
            .num,
            .tit {
                z-index: 3;
                text-align: center;
                // color: #fff;
            }
            .tit {
                text-shadow: 1px 1px 10px 0px rgba(49, 27, 17, 0.72);
                font-size: 37px;
            }
            .img {
                position: absolute;
                z-index: 2;
                background-image: url("~@src/img/common/juejin.svg");
                background-size: contain;
                background-position: center center;
                background-repeat: no-repeat;
                // top\与 background-position-y互为相反数
                top: 0px;
                background-position-y: -0px;
                height: 100%;
                width: 100%;
                transition: all 0.3s;
            }
            &:hover .img {
                background-image: url("~@src/img/common/juejin.svg");
                background-size: contain;
                background-position: center center;
                background-repeat: no-repeat;
            }
        }
    }
}

结语

简单的大转盘小游戏就封装好了,这里提供的仅仅是思路噢,肯定还有更好的实现方式,当然大家也可以自行二次开发适应自己的业务😄

求赞.png