美文网首页
Vue 项目中实现3D文字球的抽奖效果

Vue 项目中实现3D文字球的抽奖效果

作者: 酷酷的凯先生 | 来源:发表于2022-02-09 14:35 被阅读0次

今天突发奇想的想实现3D球和抽奖效果,一不做二不休把他俩融合了,效果如下:

1644388259(1).png
1644388297(1).png

话不多说,上代码

html

<template>
    <div class="sd-ball-box">
        <ul class="item-box" @mousemove="mouseMove" @mouseover="active = true" @mouseout="active = false" ref="item_box_ref">
            <li v-for="(item, key) in listTxt" :key="key">{{item}}</li>
        </ul>
        <div class="handle-box" >
            <div class="cnt-box" :style="!showRes ? 'background: transparent;' : 'background: rgba(118,5,5, .9);'">
                <p v-if="showRes">中奖名单</p>
                <p v-if="showRes">
                    <span v-for="name in sltTxtList" :key="name">{{name}}</span>
                </p>
            </div>
            <div class="btn-box">
                单次抽奖个数
                :<select v-model="sltNum">
                    <option value="1">1</option>
                    <option value="2">2</option>
                    <option value="3">3</option>
                    <option value="5">5</option>
                    <option value="10">10</option>
                </select>
                <button @click="start">开始</button>
                <button @click="end">结束</button>
            </div>
        </div>
    </div>
</template>

js

<script>
let sa, ca, sb, cb, sc, cc, per;

export default {
    data () {
        return {
            listTxt: [], // 内容
            itemBox: null, // 元素盒子
            itemList: null, // 元素集合
            temSize: [], // 每个元素宽高集合
            radius: 250, // 旋转半径
            active: false, // 鼠标是否悬浮
            tspeed: 5, // 旋转速度
            mouseX: 0, // 坐标点x
            mouseY: 0, // 坐标点y
            mouseX_move: 1,
            mouseY_move: 1,
            distr: true, //
            timer: null,
            isStart: false, // 是否开始抽奖
            isInit: true, // 是否是初始化
            defaultTimer: null, // 默认自传
            showRes: false,
            sltNum: 1,
            sltTxtList: []
        }
    },
    mounted () {
        this.init();
    },
    methods: {
        start () {
            this.showRes = false;
            if(this.isStart) return;
            // 开始抽奖时不启动默认旋转
            if(this.defaultTimer){
                clearTimeout(this.defaultTimer);
                this.defaultTimer = null;
            }
            // 一般是第一次初始化完成或自动旋转时
            if ( this.timer ) {
                this.isInit = false;
                this.isStart = true;
            } else {
                // 计算正/余弦
                this.sineCosine( 0, 0, 0 );
                // 所有元素随机排序,并重新计算位置
                this.positionAll();
                this.timer = setInterval( this.rolling, 50 );
                this.isInit = false;
                this.isStart = true;
                console.log(333)
            }
        },
        end () {
            if(!this.isStart) return;
            let tmpList = [], isStop = false, randomIndex = Math.floor((Math.random()*this.listTxt.length));
            while(!isStop){
                randomIndex = Math.floor((Math.random()*this.listTxt.length));
                if(!tmpList.includes(this.listTxt[randomIndex])){
                    tmpList.push(this.listTxt[randomIndex])
                }
                isStop = tmpList.length == this.sltNum;
            }
            this.isStart = false;
            this.showRes = true;
            console.log( this.sltNum )
            // debugger
            this.sltTxtList = tmpList;
        },
        // 计算正/余弦
        sineCosine ( a, b, c ) {
            let dtr = Math.PI / 180; // 弧度
            sa = Math.sin( a * dtr );
            ca = Math.cos( a * dtr );
            sb = Math.sin( b * dtr );
            cb = Math.cos( b * dtr );
            sc = Math.sin( c * dtr );
            cc = Math.cos( c * dtr );
        },
        // 所有元素随机排序,并重新计算位置
        positionAll () {
            var phi = 0, theta = 0, itemTmpSize = null;
            // 元素个数
            var max = this.temSize.length;
            // 元素临时存放,用于随机排序
            var itemTmpList = Array.from( this.itemList );
            // 文档片段
            var oFragment = document.createDocumentFragment();
            //随机排序
            itemTmpList.sort( () => {
                return Math.random() < 0.5 ? 1 : -1;
            } );
            // 遍历元素,逐个插入文档片段集合
            for ( let i = 0; i < itemTmpList.length; i++ ) {
                oFragment.appendChild( itemTmpList[i] );
            }
            // 把文档片段集合插入元素盒子尾部
            this.itemBox.appendChild( oFragment );
            // 循环遍历出每个元素的坐标 和 left top
            for ( let i = 1; i < max + 1; i++ ) {
                // if ( this.distr ) {
                phi = Math.acos( -1 + ( 2 * i - 1 ) / max );
                theta = Math.sqrt( max * Math.PI ) * phi;
                // }
                // else {
                //     console.log(4444)
                //     phi = Math.random() * ( Math.PI );
                //     theta = Math.random() * ( 2 * Math.PI );
                // }

                // 坐标变换
                itemTmpSize = this.temSize[i - 1];

                itemTmpSize.cx = this.radius * Math.cos( theta ) * Math.sin( phi );
                itemTmpSize.cy = this.radius * Math.sin( theta ) * Math.sin( phi );
                itemTmpSize.cz = this.radius * Math.cos( phi );

                this.itemList[i - 1].style.left = itemTmpSize.cx + this.itemBox.offsetWidth / 2 - itemTmpSize.offsetWidth / 2 + 'px';
                this.itemList[i - 1].style.top = itemTmpSize.cy + this.itemBox.offsetHeight / 2 - itemTmpSize.offsetHeight / 2 + 'px';
                this.itemList[i - 1].style.color = '#f5b16d';
                if(Math.random() > .5){
                    this.itemList[i - 1].style.color = '#ff6060';
                }
            }
        },
        // 鼠标在元素移动事件, 计算出鼠标最后消失的位置
        // 解开注释可实现跟随商标方向滚动
        mouseMove ( ev ) {
            let oEvent = window.event || ev;

            this.mouseX = oEvent.clientX - ( this.itemBox.offsetLeft + this.itemBox.offsetWidth / 2 );
            this.mouseY = oEvent.clientY - ( this.itemBox.offsetTop + this.itemBox.offsetHeight / 2 );

            this.mouseX /= 5;
            this.mouseY /= 5;
        },
        // 滚动
        // 解开注释可实现跟随商标方向滚动
        rolling () {
            let a, b, c = 0, size = 200, howElliptical = 1;
            // debugger
            // 只有初始化时才可以使用鼠标控制,即抽奖时不可以
            if ( this.active && !this.isStart ) {
                a = ( -Math.min( Math.max( -this.mouseY, -size ), size ) / this.radius ) * this.tspeed;
                b = ( Math.min( Math.max( -this.mouseX, -size ), size ) / this.radius ) * this.tspeed;
            }
            // 首次加载时默认滚动
            else if ( this.isInit ) {
                a = this.mouseX_move * ( 1 ); // 小于 1 会慢慢停止
                b = this.mouseY_move * ( 1 ); // 小于 1 会慢慢停止
            }
            // 开始抽奖
            else if ( !this.isInit && this.isStart ) {
                a = this.mouseX_move * ( 1.10 );
                b = this.mouseY_move * ( 1.25 );
            }
            // 结束抽奖
            else if ( !this.isStart ) {
                clearInterval( this.timer );
                this.timer = null;
                // 10s 后自动恢复至初始化状态即默认自动旋转
                this.defaultTimer = setTimeout( () => {
                    // 计算正/余弦
                    this.sineCosine( 1, 0, 0 );
                    // 所有元素随机排序,并重新计算位置
                    this.positionAll();
                    this.isInit = true;
                    this.isStart = false;
                    this.timer = setInterval( this.rolling, 50 );
                    clearTimeout(this.defaultTimer);
                    this.defaultTimer = null;
                }, 5000 );
            }
            this.mouseX_move = a || 1;
            this.mouseY_move = b || 1;
            if ( Math.abs( a ) <= 0.01 && Math.abs( b ) <= 0.01 ) {
                clearInterval( this.timer );
                this.timer = null;
                return;
            }
            // 重新计算正/余弦
            this.sineCosine( a, b, c );
            for ( let j = 0; j < this.temSize.length; j++ ) {
                let rx1 = this.temSize[j].cx,
                    ry1 = this.temSize[j].cy * ca + this.temSize[j].cz * ( -sa ),
                    rz1 = this.temSize[j].cy * sa + this.temSize[j].cz * ca;

                let rx2 = rx1 * cb + rz1 * sb,
                    ry2 = ry1,
                    rz2 = rx1 * ( -sb ) + rz1 * cb;

                let rx3 = rx2 * cc + ry2 * ( -sc ),
                    ry3 = rx2 * sc + ry2 * cc,
                    rz3 = rz2;

                this.temSize[j].cx = rx3;
                this.temSize[j].cy = ry3;
                this.temSize[j].cz = rz3;

                // 计算透视距离
                per = 330 / ( 330 + rz3 );

                this.temSize[j].x = ( howElliptical * rx3 * per ) - ( howElliptical * 2 );
                this.temSize[j].y = ry3 * per;
                this.temSize[j].scale = per;
                this.temSize[j].alpha = per;
                this.temSize[j].alpha = ( this.temSize[j].alpha - .6 ) * ( 10 / 6 );
            }

            this.doPosition();
            this.depthSort();
        },
        // 转动时重新计算定位
        doPosition () {
            let lf = this.itemBox.offsetWidth / 2, tp = this.itemBox.offsetHeight / 2, itemListTmp = null;
            for ( let i = 0; i < this.temSize.length; i++ ) {
                itemListTmp = this.itemList[i];

                itemListTmp.style.left = this.temSize[i].cx + lf - this.temSize[i].offsetWidth / 2 + 'px';
                itemListTmp.style.top = this.temSize[i].cy + tp - this.temSize[i].offsetHeight / 2 + 'px';
                itemListTmp.style.fontSize = Math.ceil( 12 * this.temSize[i].scale / 2 ) + 8 + 'px';
                itemListTmp.style.filter = "alpha(opacity=" + 100 * this.temSize[i].alpha + ")";
                itemListTmp.style.opacity = this.temSize[i].alpha;
            }
        },
        // 转动时重新计算 zIndex
        depthSort () {
            let aTmp = [];
            for ( let i = 0; i < this.itemList.length; i++ ) {
                aTmp.push( this.itemList[i] );
            }

            aTmp.sort( ( vItem1, vItem2 ) => {
                if ( vItem1.cz > vItem2.cz ) {
                    return -1;
                }
                else if ( vItem1.cz < vItem2.cz ) {
                    return 1;
                }
                else {
                    return 0;
                }
            } );

            for ( let i = 0; i < aTmp.length; i++ ) {
                aTmp[i].style.zIndex = i;
            }
        },
        init () {
            const baiJiaXing = '赵钱孙李周吴郑王冯陈褚卫蒋沈韩杨朱秦尤许何吕施张孔曹严华金魏陶姜戚谢邹喻柏水窦章云苏潘葛奚范彭郎鲁韦昌马苗凤花方俞任袁柳酆鲍史唐费廉岑薛雷贺倪汤滕殷罗毕郝邬安常乐于时傅皮卞齐康伍余元卜顾孟平黄和穆萧尹姚邵湛汪祁毛禹狄米贝明臧计伏成戴谈宋茅庞熊纪舒屈项祝董梁杜阮蓝闵席季麻强贾路娄危江童颜郭梅盛林刁钟徐邱骆高夏蔡田樊胡凌霍虞万支柯昝管卢莫经房裘缪干解应宗丁宣贲邓郁单杭洪包诸左石崔吉钮龚程嵇邢滑裴陆荣翁荀羊於惠甄曲家封芮羿储靳汲邴糜松井段富巫乌焦巴弓牧隗山谷车侯宓蓬全郗班仰秋仲伊宫宁仇栾暴甘钭厉戎祖武符刘景詹束龙叶幸司韶郜黎蓟薄印宿白怀蒲邰从鄂索咸籍赖卓蔺屠蒙池乔阴鬱胥能苍双闻莘党翟谭贡劳逄姬申扶堵冉宰郦雍郤璩桑桂濮牛寿通边扈燕冀郏浦尚农温别庄晏柴瞿阎充慕连茹习宦艾鱼容向古易慎戈廖庾终暨居衡步都耿满弘匡国文寇广禄阙东欧殳沃利蔚越夔隆师巩厍聂晁勾敖融冷訾辛阚那简饶空曾毋沙乜养鞠须丰巢关蒯相查后荆红游竺权逯盖益桓公';
            let isTrue = false, strTmp = '';
            this.listTxt = []
            // 填充内容
            for ( let i of baiJiaXing ) {
                if ( ( isTrue && strTmp.length == 3 ) || ( !isTrue && strTmp.length == 2 ) ) {
                    this.listTxt.push( strTmp );
                    strTmp = '';
                    isTrue = Math.random() > .5;
                }
                else {
                    strTmp += i;
                }
            }
            this.$nextTick( _ => {
                // 遍历时获得当前项
                let oTag = null;
                // 获取 包裹所有元素的 盒子
                this.itemBox = this.$refs.item_box_ref;
                // 获取 盒子里所有 元素
                this.itemList = this.itemBox.getElementsByTagName( 'li' );
                for ( let i = 0; i < this.itemList.length; i++ ) {
                    oTag = {};
                    oTag.offsetWidth = this.itemList[i].offsetWidth;
                    oTag.offsetHeight = this.itemList[i].offsetHeight;
                    this.temSize.push( oTag );
                }
                // 计算正/余弦
                this.sineCosine( 0, 0, 0 );
                // 所有元素随机排序,并重新计算位置
                this.positionAll();
                // 初始化时,默认转动
                this.timer = setInterval( this.rolling, 50 );
            } )
        }
    }
}
</script>

css

<style lang="scss" scoped>
.sd-ball-box {
     width: 650px;
    height: 600px;
    display: flex;
    justify-content: center;
    align-items: center;
    position: relative;
    margin: 0 auto;
}
.item-box {
    width: 650px;
    height: 600px;
    background: #000;
    list-style: none;
    position: relative;
    li {
        position: absolute;
        top: 50%;
        left: 50%;
        padding: 3px 6px;
        // transition: all .1s;
        transform: translate(-10%, -10%);
        font-family: Microsoft YaHei;
        font-weight: bold;
        // color: #f5b16d; // #84a4fd #f7bd82
    }
}
.handle-box {
    position: absolute;
    left: 50px;
    top: 38px;
    width: 550px;
    display: flex;
    flex-direction: column;
    justify-content: space-between;
    z-index: 99999;
    // 中奖名单
    .cnt-box {
        height: 450px;
        text-align: center;
        padding: 50px 125px 20px;
        border-radius: 8px;
        p:first-child{
            font-size: 55px;
            font-weight: bold;
            color: #ffd300;
            text-shadow:#000 10px 5px 5px;
            letter-spacing: 10px;
        }
        p:last-child {
            padding-top: 50px;
            letter-spacing: 2px;
            span {
                display: inline-block;
                width: 100px;
                padding-top: 18px;
                font-size: 26px;
                // font-weight: bold;
                color: yellow;
                text-shadow:#000 5px 3px 5px;
                // text-shadow: #84a4fd 1px 0 0, #84a4fd 0 1px 0, #84a4fd -1px 0 0, #84a4fd 0 -1px 0;
            }
        }
    }
    .btn-box {
        padding: 20px;
        display: flex;
        justify-content: center;
        align-items: center;
        position: relative;
        bottom: -56px;
        select {
            width: 66px;
            height: 26px;
            margin-left: 20px;
        }
        button {
            outline: none;
            border: none;
            padding: 5px 15px;
            margin-left: 30px;
            cursor: pointer;
        }
    }
}
</style>

有些东西我也没搞太明白,欢迎大家留言 ~~~

相关文章

  • Vue 项目中实现3D文字球的抽奖效果

    今天突发奇想的想实现3D球和抽奖效果,一不做二不休把他俩融合了,效果如下: 话不多说,上代码 html js cs...

  • iOS 抽奖轮盘效果实现思路

    iOS 抽奖轮盘效果实现思路 iOS 抽奖轮盘效果实现思路

  • 随手收集

    one div实现icon one div 实现动态天气 css实现文字3D CSS实现文本干扰效果 CSS实现文...

  • Android超简单的3D旋转效果--图片轮播式

    Android超简单的3D旋转效果--图片轮播式 这段时间项目中要实现一个3D旋转的效果图,并要下载zip图包下载...

  • 通过VuePress管理项目文档(二)

    通过vue组件实现跟:Element相似的效果。需要在VuePress网站中将自己的项目中的Vue组件运行结果展示...

  • Vue动画之二: JS方式

    在vue 项目中, 通过JS添加动画效果。1、js 事件, 处理 2、Velocity.js 实现的

  • Android 自定义View 之九宫格抽奖View

    最近在项目中要实现一个九宫格抽奖view 。中间是抽奖按钮,八个格子是奖品。效果图如下: 接下来我就分析一下实现这...

  • vue中百度地图的使用

    写在前面 需求:vue项目中添加百度地图,实现具体业务。 一、效果: 二、下载安装vue-baidu-map 三、...

  • 3d翻转效果

    项目中对上面的轮播图的需求是要3d翻转效果,下面是小弟写的一个demo,没有实现手动滑动效果 #import"Vi...

  • css实现简单的3D效果

    css实现简单3D房间效果 纯3D效果实现上述效果,总体思路就是旋转容器,为容器贴纸。 这是一个简单的3D立方体空...

网友评论

      本文标题:Vue 项目中实现3D文字球的抽奖效果

      本文链接:https://www.haomeiwen.com/subject/jrvwkrtx.html