css实现摇奖机动效

摇奖机动效实现

效果

在这里插入图片描述

实现代码

import React,{FC, useEffect, useRef, useState} from "react"
import styles from './index.less'
import btn from '@/img/ernie/btn.png'
import icon1 from '@/img/ernie/icon1.png'
import icon2 from '@/img/ernie/icon2.png'
import icon3 from '@/img/ernie/icon3.png'


class LuckGame{
  setting: {
    //奖品个数
    len: number;
    //滚动时间
    speed: number;
    //循环几圈
    circle: number;
  };
  $ul: any;
  $height: any;
  $gameItem: any;
  canbegin:boolean;
  constructor(obj: { len?: number; speed?: number; circle?: number; ul: any; item: any }) {
          this.setting = {
              //奖品个数
              len : 3,
              //滚动时间
              speed : 5000, 
               //循环几圈
              circle : 5
          };
          this.extend( this.setting, obj );
          this.$ul = obj.ul
          this.$gameItem = obj.item
          this.$height = this.$gameItem[0]?.offsetHeight;//item的高度
          this.canbegin=true
      }

      setList(imgArr: any[]){
        //填充li      
        const List = imgArr.map((img,index)=>{
          return <li key={index}><img src={img.img} style={{width:"70%"}}/></li>
        })
        const arr = new Array();
        for(let n = 0;n <= this.setting.circle;n++){
           arr.push(List)
        };
        return arr.map(item=>{
           return item;
        });
      }
     
      // 初始时设置图片随机位置
      randomPosition(){
        // 随机生成10-110的一个随机数
        const num  = Math.floor(Math.random()*110+10);
        const arr = num.toString().split("");
        if(arr.length===2){
          
          const number = +arr[0]-1
          arr.push(number+"");
        }
        [].forEach.call(this.$ul,(ulItem,index)=>{      
          //@ts-ignore
        ulItem.style['transform']=ulItem.style['transform'] = `translate(0px, -${+arr[index]% this.setting.len * this.$height}px)`;
       });
      }

      start(arr:[],fn?:any){
        this.canbegin =false
        let that = this;
        let count = 0;
        //开始抽奖
        [].forEach.call(that.$ul,(o,i)=>{
        setTimeout(()=>{
          const y=(arr[i]+that.setting.len *(that.setting.circle-1))*that.$height;
          //@ts-ignore
          o.style['transition'] = `${that.setting.speed}ms ease`;
          //@ts-ignore
          o.style['transform'] = `translate(0px, -${y}px)`
        },i * 300);

    //  @ts-ignore
      o.addEventListener('transitionend',function(){
        this.style['transition'] = '0ms ease';
        this.style['transform'] = `translate(0px, -${arr[i]*that.$height}px)`;
        count++
        if(count===that.$ul.length){
          fn&&fn()
        }
      },false);
    })

    }

    // 中奖动画
    getPrice(callback?:any){
     
        let that = this; 
        [].forEach.call(that.$gameItem,(o,i)=>{
          setTimeout(()=>{
            switch(i){
              case 0:
                //@ts-ignore
                o.style['animation'] = `${styles.iocnleft}  3s  linear forwards`
                break;
              case 1:
                //@ts-ignore
                o.style['animation'] = `${styles.iocncenter}  2s  linear forwards`
                break;
              case 2:
                //@ts-ignore
                o.style['animation'] = `${styles.iocnright}  3s  linear forwards`
                break;
              default:break;
            }
          },300);


          if(i===2){
             //@ts-ignore
            o.addEventListener("animationend", callback);
          }
         
        })
    }

    // 修改setting配置(外部传入的优先)
      extend (n: { [x: string]: any; len?: number; speed?: number; circle?: number },n1: { [x: string]: any; len?: number | undefined; speed?: number | undefined; circle?: number | undefined; ul?: any; item?: any }){ 
          for(let i in n1){n[i] = n1[i]};
      }
}


const Ernie :FC<any> = ()=>{

  const [game,setGame] = useState<any>()
  useEffect(()=>{

    document.documentElement.style.fontSize=(document.documentElement.clientWidth/750)*100+'px';
    const newgame = new LuckGame({
      len : 3, 
      speed : 3000, 
      circle : 5, 
      ul:gameUlRefList.current,
      item:gameItemRefList.current
    }); 
    setGame(newgame)
  },[])

  useEffect(()=>{
    if(game){
      game.randomPosition()
    }
  },[game])


  const reset = ()=>{
    game.$gameItem.forEach((item:any)=>{
      item.style["animation"]  = "none"
    })
    // 设置随机图标显示
    game.randomPosition()
  }
  const onAction = ()=>{
      // 先请求接口判断是否中奖==>触发动效
      if(!game.canbegin) return
      console.log("开始触发动效")
      game.start([1,1,1],()=>{
        game.getPrice(()=>{
          alert("弹出弹窗展示中奖结果")
          reset()
          game.canbegin = true
        })
      })
  }


  const gameUlRefList = useRef<any>([])
  const getULRef = (dom: any) => {
    if(!dom) return;
    if(gameUlRefList.current.length>=3) return
    gameUlRefList.current.push(dom)
  }
  
  const gameItemRefList = useRef<any>([])
  const getItemRef = (dom: any) => {
    if(!dom) return;
    if(gameItemRefList.current.length>=3) return
    gameItemRefList.current.push(dom)
  }

  

    return(
         <div className={styles.mechine}>
          {/* 摇奖机图案 */}
          <div className={styles.game_wrap}>
            {
              [1,2,3].map((item)=>{
                 return <div className={styles.game_item} key={item} ref={getItemRef} >
                         <ul ref={getULRef}>
                           {game&&game.setList([{img:icon1},{img:icon2},{img:icon3}])}
                         </ul>
                       </div>
              })
            }
          </div>
          <div className={styles.btnContain}>
             <img src={btn}  className={styles.btn} onClick={onAction}/>
          </div>
       </div>
    )
}

export default Ernie
.mechine{
  width: 90%;
  background-image: url(../../img/ernie/mechine.png);
  background-repeat: no-repeat;
  background-size: contain;
  height: 90vh;
  margin: auto;

  .game_wrap{
    width: 4.7rem;
    height: 2.4rem;
    overflow: hidden;
    position: absolute;
    top: 1.65rem;
    left: 50%;
    transform: translateX(-50%);

    .game_item {
      width: 33.333%;
      height: 2.2rem;
      display: inline-block;
    }

    .game_item ul{
      padding:  0;
    }
    
    .game_item li {
      list-style: none;
      width: 100%;
      height: 2.2rem;
      line-height: 2.2rem;
      text-align: center;
      position: relative;
      font-size: 50px;
    }
  }

  .btn{
    position: absolute;
    width: 45%;
    top: 4.4rem;
    left: 50%;
    transform: translateX(-50%);
  }
}


// 抖动--->向中间靠拢并且透明度慢慢变为0
@keyframes iocnleft{
  3%,5%,25%,10%{
   transform: rotate(-3deg)
  }
  20%,15%,30%{
   transform: rotate(3deg)
  }
  0%, 35%,40%{
   transform:  rotate(0)
  }
  90%{
   opacity: 0.5;
   
  }
  100%{
    transform: translateX(1.6rem);
    opacity: 0;
  }
}

// 抖动--->慢慢消失弹出礼品
@keyframes iocncenter{
 3%,5%,25%,10%{
   transform: rotate(-3deg)
  }
  20%,15%,30%{
   transform: rotate(3deg)
  }
  0%, 35%,40%{
   transform:  rotate(0)
  }
 90%{
   opacity: 1;
 }
  100%{
   opacity: 0.3;
  }
}

// 抖动--->向中间靠拢并且透明度变为0
@keyframes iocnright{
 3%,5%,25%,10%{
   transform: rotate(3deg)
  }
  20%,15%,30%{
   transform: rotate(-3deg)
  }
  0%, 35%,40%{
   transform:  rotate(0)
  }

  90%{
   opacity: 0.5;
   
  }
  100%{
    transform: translateX(-1.6rem);
    opacity: 0;
  }
}

实现原理

第一部分:初始化(滚动圈数,速度,奖品个数:这里设置的是三个)
第二部分:填充奖品:根据滚动的圈数来判断需要填充多少元素在ul里面,方法看(setList)
第三部分:随机位置,因为默认都是从第一个奖品开始填充的,所以需要设置每个ul(理解成每个纵向)需要随机移动多少位置,具体实现方法看(randomPosition)
第四部分(重点):元素滚动:传入一个arr数组,表示第i个位置想要停留显示的是哪一个元素,例如arr = [1,1,1]表示1,2,3的位置都想要最后停留的是元素1,具体实现看start方法
滚动原理就是通过设置transition和transform
第五部分:中奖合成显示结果,看getPrice方法

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值