css实现红包雨动效

提示:该实现方法灵敏度似乎不高 并且如果一直不点击红包,或者触发点击过慢,会出现屏幕上红包过多的问题(就是说不能让屏幕上的红包一直保持在一个均匀的量),另外一个缺点就是红包会重叠

css实现红包雨动效

效果

在这里插入图片描述

React代码

// 红包雨游戏
import React, { useEffect, useRef, useState } from "react"
import styles from './index.less'

const Game = ()=>{
    useEffect(()=>{
      document.documentElement.style.fontSize=(document.documentElement.clientWidth/750)*100+'px';
      // 执行红包雨创建函数
      createRedPacket()
      setTimeout(()=>{
        countDown()
      },500)
    },[])

    const [nums,setNums] = useState(0)
    const [countdown,setTime] = useState(15)
  
    const ref = useRef<any>()
  
    const timeRef = useRef<any>()
    const timerRef = useRef<any>()
    timeRef.current = countdown
  
    const numsref = useRef<any>()
    numsref.current = nums

     // 红包雨元素创建
  const createRedPacket = ()=>{
    // 红包个数
    const rainCount = 38
    const rainFragment = document.createDocumentFragment()

    let delay = 0;

  for (let i = 0; i < rainCount; i++) {
    let container =  document.createElement('div');

    // 光晕
    let halo =  document.createElement('div');
    let rainEle = document.createElement('div');
    let plusOne = document.createElement('div');
    // 设置一下点击区域 红包图片背景点击区域太大了
    let click = document.createElement('div');

    container.className = `${styles.container}`;
    click.className =  `${styles.rainclick}`;
    rainEle.className = `${styles.rain}`;
    plusOne.className = `${styles.plusOne}`;
    halo.className = `${styles.halo}`;

    const offset = 10;

    click.onclick = ()=>{
      if( timeRef.current<=0) return; //倒计时结束 点击失效
      plusOne.style.animation=`${styles.plusOne} 1s ease-in-out`
      setNums(numsref.current+1)
    
 
      rainEle.style.animation=`${styles.rainClick} 0.1s ease-in-out forwards`
      halo.style.animation=`${styles.halo} 1s ease-in-out forwards`
      container.style.animationPlayState="paused"
    }

    container.appendChild(plusOne)
    container.appendChild(rainEle)
    container.appendChild(halo)
    rainEle.appendChild(click)
  

    // 随机生成5-75的整数
    const leftPosition = Math.round(Math.random() * (75-5))+5;

    container.style.left  = `${leftPosition}%`

    container.style.animation  = `${styles.raindrop} 8s infinite ease-in-out` // infinite  无效循环 所以红包雨的动画是循环播放的


    // container.style.animationDelay = (Math.random()*14).toFixed(2) + 's';
    container.style.animationDelay =(delay)+'s';
    delay+=0.4;
 

    plusOne.addEventListener("animationend",()=>{
        // console.log("+1动画结束 移除动画 下一次点击时重新设置动画")
        plusOne.style.animation=""
    })
    halo.addEventListener("animationend",()=>{
        // console.log("光晕动画结束 移除动画 下一次点击时重新设置动画")
        halo.style.animation=""
        
        // 这样处理看看红包点击不了的问题能否解决 之前可能是因为层级
        // container.style.display="none" //这样ios可能会有兼容性问题出现?
        // 所以直接更改宽度让这个div消失
        container.style.width="0"
    })



    // 监听动画一次循环结束 一次循环结束之后需要重新设置位置
    container.addEventListener("animationiteration",()=>{
      rainEle.style.animation=""
      // 倒计时结束 不在播放红包雨动画
      if( timeRef.current<=0){
       //  移除动画
       container.style.animation=""
      //  setBegin(false)
        return;
      }

      // 重新设置位置
      let leftPosition
      if(i%2==0){
       
        leftPosition  = offset+Math.round(Math.random() * (75-5))+5;
        
      }else{
        leftPosition  = -offset+Math.round(Math.random() * (75-5))+5;
      }
       
        container.style.left  = `${leftPosition}%`
    })

    rainFragment.appendChild(container)
  }
  ref.current.appendChild(rainFragment)

  }

   // 倒计时
   const countDown = ()=>{
    timerRef.current  = setInterval(()=>{
      const countdown  = timeRef.current 
      setTime(countdown-1)
      if(countdown===1) {
        clearInterval(timerRef.current )
      }
    },1000)
  }

  return(
    <div className={styles.Game}>
    <div className={styles.top}>
      <div className={styles.countdown}>{countdown}S</div>
      <div  className={styles.time}>点击的红包数:{nums}</div>
   </div>
   <div className={styles.main}  ref={ref}>

   </div>
   </div>
  )
}

export default Game

css代码

.Game{
  height: 92vh;
  width: 100vw;
  background: #808080c2;
  overflow-y: hidden;
  .top{
    display: flex;
    justify-content: center;
    align-items: center;
    color: #FFEDD3;
    .time{
      font-size: 0.3rem;
      font-weight: 500;
      margin: 0 0.5rem;
    }
    .countdown{
      width: 1.7rem;
      height: 1.7rem;
      border: 0.01rem white solid;
      border-radius: 50%;
      font-size: 0.6rem;
      text-align: center;
      font-weight: bold;
      line-height: 1.7rem;
    }
    position: relative;
  }

  // 红包雨
  .main{
    position: absolute;
    top: -2.5rem;
    width: 100%;
    height: 100%;

    // 被点击的光晕
    .halo{
      position: absolute;
      top: 0;
      left: 0;
      opacity: 0;
      width: 1.5rem;
      height: 1.5rem;
      border-radius: 50%;
      background-image: radial-gradient(circle at 50% 50%,rgba(0, 0, 0, 0),rgba(177, 74, 30, 0.81),rgba(244, 226, 89, 0.76) );
      left: 50%;
      top: 50%;
      transform: translate(-50%,-50%);
    }

    .container{
      position: absolute;
      background-clip: content-box;
      box-sizing: border-box;
      width: 1.88rem;
      height: 2.01rem;
      transform: scale(0.8);

    }
    .rain{
      position: absolute;
      top: 0;
      left: 0;
      width: 1.88rem;
      height: 2.01rem;
      background-image: url(../../img/rain/rain.png);
      background-size: cover;
      background-repeat: no-repeat;
    }
    .rainclick{
      position: absolute;
      width: 1.1rem;
      height: 1.45rem;
      left: 0.4rem;
      top: 0.32rem;
      transform: skew(-33deg,32deg);
      z-index: 9;
    }
    .plusOne{
      width: 1rem;
      height: 1rem;
      transform: scale(0.8);
      background-image: url(../../img/rain/plus1.png);
      background-size: cover;
      background-repeat: no-repeat;
      position: absolute;
      top: -0.3rem;
      left: 0;
      opacity: 0;
      z-index: 3;
    }
  }
}


// 红包雨的动画
@keyframes raindrop {
  0% {
    opacity: 1;
  }
  100% {
    /* 1vh表示窗口高度的1% */
    transform: scale(0.8) translate(0, calc(150vh)) ;
    opacity: 1;
  }
}

// 红包被点击的动画

@keyframes rainClick {
  0% {
    opacity: 1;
  }
  100% {
    opacity: 0;
  }
}

// +1的动画

@keyframes plusOne{
    20%{
      opacity: 1;
    }
    100%{
        transform: translateY(-1.5rem);
        opacity: 0;
    }
}


// 被点击的光晕的动画

@keyframes halo{
    from{
        opacity: 0.3;
      
    }
    to{
      transform: translate(-50%,-50%) scale(1.5);
      opacity: 0;
    }
}

总结

1、实现方法就是css的循环动画,然后监听每次动画循环的结束,重新设置红包的横向位置
2、怎样控制红包不会一起下落呢?使用动画延时,让后面的红包下落动画延迟执行:
container.style.animationDelay =(delay)+‘s’;
delay+=0.4;

3、倒计时结束的时候,清除css动画,并且让点击事件失效
4、代码中用了很多ref是为了读取到state里面更新的最新的值,因为闭包的问题,如果不使用ref会一直读取到初始值的

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值