Vue实现前端动画抓抓乐抽奖

2022-01-14  本文已影响0人  王果果

CSS3 动画卡顿性能优化
那么为什么会造成动画卡顿呢?

原因就是主线程和合成线程的调度不合理。

  1. 假设我们要一个元素的 height 从 100 px 变成 200 px,就像这样:
div {
  height: 100px;
  transition: height 1s linear;
}
 
div:hover {
  height: 200px;
}

可能会比较耗时,容易出现卡顿现象

  1. 而使用 transform:scale 实现,减少主线程的计算次数,提高动画性能
div {
  transform: scale(0.5);
  transition: transform 1s linear;
}
 
div:hover {
  transform: scale(1.0);
}

其次还可以通过开启硬件加速的方式优化动画,开启3d加速
webkit-transform: translate3d(0,0,0);
-moz-transform: translate3d(0,0,0);
-ms-transform: translate3d(0,0,0);
-o-transform: translate3d(0,0,0);
transform: translate3d(0,0,0);

总结解决 CSS3 动画卡顿方案
  1. 尽量使用 transform 当成动画熟悉,避免使用 height,width,margin,padding 等;
  2. 要求较高时,可以开启浏览器开启 GPU 硬件加速
    源链接:https://www.jb51.net/article/147736.htm

抓抓乐抽奖代码实现(移动端)

  1. 前端动画展示的一种抽奖形式
  2. 有上下两排奖品左右移动
  3. 点击抽奖按钮上方夹子下落,奖品停止移动,随机抓取移动的奖品上升
  4. 夹子到达初始位置,奖品返回初始位置并继续移动
<template>
    <div class="shopping-grab" :style="{'background-image': `url(${styleConfig.grab_bg})`}" >
        <!-- 动画部分 -->
        <div class="grab-box">
            <!-- 钩子部分 -->
            <div class="hook-box">
                <!-- 绳子 -->
                <div class="rope" :class="{ropetop:isDown && layers == 1,ropeBtm:isDown && layers == 2}" :style="{'background-image': `url(${styleConfig.grab_rope})`}"></div>
                <!-- 钩子主体 -->
                <div class="hook" :style="{'background-image': `url(${styleConfig.grab_body})`}" ref="hookRef">
                    <!-- 钩手左 -->
                    <div class="hookLeft" :class="{downLeft:isDown}"  :style="{'background-image': `url(${styleConfig.grab_hand_left})`}"></div>
                    <!-- 钩手右 -->
                    <div class="hookRight" :class="{downRight:isDown}" :style="{'background-image': `url(${styleConfig.grab_head_right})`}"></div>
                </div>
            </div>
            <!-- 盲盒部分 -->
            <div class="blind-box">
                <!-- 上排盲盒 -->
                <div class="blind-top" :class="{'blind-top-stop': isStop}" ref="blindTop">
                    <div class="blind-item"  v-for="(item,index) in blindBoxTop" :key="index" :style="{'left': item.left + 'px'}">
                        <img :src="item.url" class="blind-img" alt="">
                    </div>
                </div>
                <!-- 下排盲盒 -->
                <div class="blind-btm" id="blind-btm" :class="{'blind-top-stop': isStop}" ref="blindBtm">
                    <div class="blind-item" v-for="(item,index) in blindBoxBtm" :key="index" :style="{'left': item.left + 'px'}">
                        <img :src="item.url" class="blind-img" alt="">
                    </div>
                </div>
            </div>
        </div>
        <!-- 抓抓乐按钮 -->
        <div class="grab-btn" :style="{'background-image': `url(${styleConfig.grab_btn})`}" @click="grabFn" v-log="'商店-抓抓乐抽奖按钮'">
            <!-- 抓抓乐按钮文案  -->
            <div class="grab-btn-text" :style="{'color': styleConfig.btn_text_color}">{{ styleConfig.btn_text }}</div>
        </div>
    </div>
</template>

<script>
export default {
   // 导出了btnClick抽奖按钮点击 and prizePopupFn 中奖弹窗
   props: {
       // 盲盒数组
       blindArray: {
           type: Array,
           default: ()=>{
               return []
           }
       },
       // 是否调用抽奖动画
       isAnimation: {
           type: Boolean,
           default: false
       },
       // 抓抓乐样式
       styleConfig: {
           type: Object,
           default: ()=>{
               return {
                   grab_bg: '', // 背景图
                   grab_rope: '', // 绳子图
                   grab_body: '', // 钩子主体
                   grab_hand_left: '', // 钩子左
                   grab_head_right: '', // 钩子右
                   grab_btn: '', // 按钮图片
                   btn_text_color: '', // 按钮文案颜色
                   btn_text: '', // 按钮文案
               }
           }
       }      
   },
   watch: {
       isAnimation(newVal) {
           if (newVal) {
               this.grabLottrey()
           }
       }, 
   },
   created () {
       // 适配
       const iphone6ItemWidth = 85;
       const curScreenWidth = window.screen.width;
       const curItemWidth = curScreenWidth / 375 * iphone6ItemWidth;
       let blind1 = {
           url: this.blindArray[0],
           left: 0,
       }
       let blind2 = {
           url: this.blindArray[1],
           left: curItemWidth,
       }
       let blind3 = {
           url: this.blindArray[2],
           left: curItemWidth * 2,
       }
       let blind4 = {
           url: this.blindArray[3],
           left: curItemWidth * 3,
       }
       let blind5 = {
           url: this.blindArray[4],
           left: curItemWidth * 4,
       }
       let blind6 = {
           url: this.blindArray[0],
           left: curItemWidth * 5,
       }
       let blind7 = {
           url: this.blindArray[1],
           left: curItemWidth * 6,
       }
       let blind8 = {
           url: this.blindArray[2],
           left: curItemWidth * 7,
       }
       let blind9 = {
           url: this.blindArray[3],
           left: curItemWidth * 8,
       }
       let blind10 = {
           url: this.blindArray[4],
           left: curItemWidth * 9,
       }
       this.blindBoxTop.push(blind1,blind2,blind3,blind4,blind5,blind6,blind7,blind8,blind9,blind10) // 上层盲盒
       this.blindBoxBtm = JSON.parse(JSON.stringify(this.blindBoxTop)) // 下排盲盒
   },
   data () {
       return {
           blindBoxTop: [], // 上排盲盒
           blindBoxBtm: [], // 下排盲盒
           isDown: 0, // 绳子拉长
           layers: 1, // 落下层数
           isStop: 0, // 是否暂停盲盒移动
           timeid: null,
           isAim: null
       }
   },
   methods: {
       // 点击抽盲盒
       grabFn() {
           this.$emit('btnClick') // 导出按钮点击事件 
       },
       // 钩子动画
       hookFn() {
           this.isDown = 1  // 钩子是否下落
           let cdnTop = this.$refs.blindTop.childNodes
           let cdnBtm = this.$refs.blindBtm.childNodes
           if (this.layers == 1) {
               this.timeid = setTimeout(()=>{
                   for (let i = 0; i < cdnTop.length; i++) {
                       console.log(cdnBtm[i].getBoundingClientRect().left)
                       if (cdnTop[i].getBoundingClientRect().left >= 100 && cdnTop[i].getBoundingClientRect().left < 220) {
                           let sltbox = this.$refs.hookRef.appendChild(cdnTop[i])
                           setTimeout(()=>{
                               this.initial() // 初始化盲盒移动
                               this.$refs.blindTop.appendChild(sltbox) // 盲盒复位
                           },2500)
                           clearTimeout(this.timeid)
                           return
                       } 
                   }
               },2000)
           } else if (this.layers == 2) {
               this.timeid = setTimeout(()=>{
                   for (let i = 0; i < cdnBtm.length; i++) {
                       console.log(cdnBtm[i].getBoundingClientRect().left)
                       if (cdnBtm[i].getBoundingClientRect().left >= 100 && cdnBtm[i].getBoundingClientRect().left < 220) {
                           let sltbox = this.$refs.hookRef.appendChild(cdnBtm[i])
                           setTimeout(()=>{
                               this.initial() // 初始化动画
                               this.$refs.blindBtm.appendChild(sltbox) // 盲盒复位
                           },3500)
                           clearTimeout(this.timeid)
                           return
                       }
                   }
               },3000)
           }
       },
       // 盲盒动画
       blindFn() {
           this.layers =  Math.ceil(Math.random()*2);
           this.hookFn() // 调用钩子动画
           if (this.layers == 1) {
               setTimeout(()=>{  // 如果是第一层点击后延时停止盲盒移动
                   this.isStop = 1
               },2000)
           } else if (this.layers == 2) {
               setTimeout(()=>{  // 如果是第二层点击后延时停止盲盒移动
                   this.isStop = 1
               },3000)
           }
       },
       // 盲盒抽奖
       grabLottrey() {
           this.blindFn() // 调用盲盒动画
           // 判断下落的层数控制弹中奖弹窗的延时时间
           if (this.layers == 1) {
               setTimeout(()=>{
                   this.$emit('prizePopupFn')
               },3000)
           } else if (this.layers == 2) {
               setTimeout(()=>{
                   this.$emit('prizePopupFn')
               },5000)
           }
       },
       // 初始化动画
       initial() {
           this.isStop = 0  // 盲盒移动
           this.isDown = 0 // 初始化下落类名
       },
   }
}
</script>


<style lang="scss" scoped>
@import '../css/mixin.scss';
    // 抓抓乐
    .shopping-grab {
        height: 1068px;
        width: 100%;
        // background-color: pink;
        margin-bottom: 20px;
        background-size: 100% 100%;
        background-repeat: no-repeat;
        padding-top: 66px;
        position: relative;
        box-sizing: border-box;
        .grab-box {
            height: 750px;
            width: 606px;
            // background-color: pink;
            overflow: hidden;
            margin: 0 auto 10px;
            padding-top: 280px;
            position: relative;
            // 钩子
            .hook-box {
                z-index: 99;
                position: absolute;
                top: 0;
                left: 50%;
                transform: translateX(-50%);
                // 被抓起来的盒子样式
                .blind-item {
                    margin: 0 auto;
                    width: 150px;
                    height: 236px;
                    overflow: hidden;
                    .blind-img {
                        height: 136px;
                        width: 150px;
                        margin: 90px auto 0;
                    }
                }
                // 绳子 infinite
                .ropetop {
                    animation: animationTop 4s linear;
                }
                .ropeBtm {
                    animation: animationBtm 6s linear;
                }
                .rope {
                    width: 16px;
                    height: 50px;
                    background-size: 100% 100%;
                    background-repeat: repeat;
                    margin: 0 auto 0;
                    transform: translate3d(0, 0, 0); 
                    // animation: animationBtm 7s linear;                       
                }
                @keyframes animationTop {
                    0% {
                        height: 50px;
                    }
                    50% {
                        height: 210px;
                    }
                    100% {
                        height: 50px;
                    }
                }
                @keyframes animationBtm {
                    0% {
                        height: 50px;
                    }
                    50% {
                        height: 470px;
                    }
                    100% {
                        height: 50px;
                    }
                }
                // 钩子部分
                .hook {
                    width: 170px;
                    height: 124px;
                    background-size: 100%;
                    background-repeat: no-repeat;
                    margin: 0 auto 0;
                    position: relative;
                    .hookLeft {
                        position: absolute;
                        left: 0;
                        top: 36%;
                        height: 100px;
                        width: 50px;
                        background-size: 100%;
                        background-repeat: no-repeat;
                    }
                    .downLeft {
                        transform: rotate(22deg);
                        transform-origin: 10px 10px;
                        transition: 1s;
                    }
                    .downRight {
                        transform: rotate(-22deg);
                        transform-origin: 40px 10px;
                        transition: 1s;
                    }
                    .hookRight {
                        position: absolute;
                        right: 0;
                        top: 36%;
                        height: 100px;
                        width: 50px;
                        background-size: 100%;
                        background-repeat: no-repeat;
                    }
                }
            }
            // 盲盒  
            .blind-box {
                height: 412px;
                width: 100%;
                position: relative;
                .blind-top {
                    animation: rolling1 7s linear infinite;
                    position: absolute;
                    height: 140px;
                    // display: flex;
                    width: 1700px;
                    // transform: translate3d(0, 0, 0);
                    /* Other transform properties here */
                    @keyframes rolling1 {
                        from {
                            transform: translate3d(0,0,0);
                        }
                        to {
                            transform: translate3d(-50%,0,0);
                        }
                    }
                    .blind-item {
                        position: absolute;
                        top: 0;
                        // margin-right: 20px;
                        width: 150px;
                        height: 136px;
                        .blind-img {
                            height: 136px;
                            width: 150px;
                            margin: 0 auto;
                        }
                    }
                    .blind-bmbox {
                        width: 1700px;
                        height: 140px;
                    }
                }
                .blind-btm {
                    animation: rolling2 7s linear infinite;
                    position: absolute;
                    top: 284px;
                    height: 140px;
                    // display: flex;
                    width: 1700px;
                    @keyframes rolling2 {
                        from {
                            transform: translate3d(-50%,0,0);
                        }
                        to {
                            transform: translate3d(0,0,0);
                        }
                    }
                    .blind-item {
                        // margin-right: 20px;
                        position: absolute;
                        top: 0;
                        width: 150px;
                        height: 136px;
                        .blind-img {
                            height: 136px;
                            width: 150px;
                            margin: 0 auto;
                        }
                    }
                    .blind-bmbox {
                        width: 1700px;
                        height: 140px;
                    }
                }
                .blind-top-stop {
                    animation-play-state:paused;
                }
            }
        }
        .grab-btn {
            position: absolute;
            bottom: 76px;
            left: 50%;
            transform: translateX(-50%);
            width: 420px;
            height: 160px;
            background-size: 100%;
            // background-color: #fff;
            background-repeat: no-repeat;
            margin: 0 auto 10px;
            .grab-btn-text {
                font-size: 22px;
                text-align: center;
                line-height: 170px;
            }
        }
    }
</style>
上一篇 下一篇

猜你喜欢

热点阅读