当前位置:   article > 正文

css实现可控进度条动效_css实现进度条动画

css实现进度条动画

css实现可控进度条动效

实现效果

在这里插入图片描述

实现思路

整体思路就是通过监听宽度的过渡效果,因为需要一步一步去增加,进度条变化的同时控制数字和节点的变化,所以我们监听了过渡结束事件,并增加执行队列,保证了进度条动画结束后在执行下一次的动画执行

(1)增加旗帜 addFlag方法,传入一个需要增加的数量,然后需要分条件去判断剩余的位置是否足够放下增加的数量?如果够,直接调用改变进度条宽度的方法
如果不够,需要判断剩余的位置是否大于0,如果是,那么就先占满,把剩下的放进队列里,等占满后在继续执行;如果剩余位置为0,需要先改变宽度为0,然后在执行宽度的变化函数

(2)监听的回调函数中,需要判断在达到目的节点后队列是否有值,有的话,执行队列的尾值

实现重点:递归,队列、过渡结束监听

实现代码

import React,{FC, useEffect, useRef, useState} from "react";
import styles from './index.less'

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


  //初始化最开始的旗帜数
  const initFlag = (num:number)=>{
    const myflagNum = num
    if(myflagNum==0) return
    const mychangeflagNum = (myflagNum%5)===0?(Math.floor(myflagNum/5)-1)*5:Math.floor(myflagNum/5)*5
    setchangeflagNum(mychangeflagNum);
    addFlag(myflagNum)
  }


  useEffect(()=>{
    document.documentElement.style.fontSize=(document.documentElement.clientWidth/750)*100+'px';
    barRef.current.addEventListener("transitionend",widthchangend)
    initFlag(4);
  },[])

  const [barWidth,setbarWidth] =useState<number>(0)
    // 当前到达的节点
    const [nownode,setnowNode] =useState<number>(0)

    // 当前旗帜数
    const [flagNum,setflagNum] =useState<number>(0)

    //变化的旗帜数
    const [changeflagNum,setchangeflagNum] =useState<number>(0)

    const changeflagNumRef = useRef<any>(0);
    const flagNumRef = useRef<any>(0);
    const nownodeRef = useRef<any>(0);

    changeflagNumRef.current = changeflagNum
    flagNumRef.current = flagNum
    nownodeRef.current = nownode
    
    const barRef = useRef<any>(null);

    const queueRef = useRef<any>([]);//执行队列
    const addingRef = useRef<any>(false);//监听状态

    const widthObj:any = {
      "node1":20,
      "node2":40,
      "node3":60,
      "node4":80,
      "node5":100,
  }

  // 宽度变化结束 数字改变增加 节点增加
  const widthchangend  =()=>{
      setchangeflagNum( changeflagNumRef.current+1)
      setnowNode((  nownodeRef.current+1))
       // 增加到指定的数字了
      if(changeflagNumRef.current>= flagNumRef.current ){
        addingRef.current = false
        const num = queueRef.current.pop()
        if(num&&num>0){
          addFlag(num)
        }
        return
      }
      const nowFlag = changeflagNumRef.current%5+1;
      setbarWidth(widthObj["node"+nowFlag])
  }

  /**
   * 
   * @param flagNum // 需要更新的旗帜总数
   * @returns 
   */
    const changeWidth = (flagNum:number)=>{
        addingRef.current = true
        flagNumRef.current = flagNum
        setflagNum(flagNum)
    
        if(flagNum===0) return;

        //先增加一步 在这一步结束后 继续增加到指定数字
        const nowFlag = changeflagNumRef.current%5+1;
        setbarWidth(widthObj["node"+nowFlag])
    }


    // 增加旗帜的方法
    /**
     * 
     * @param num //需要增加的旗帜数
     * @returns 
     */
    const addFlag = (num:number)=>{
      if(num<=0){
        return 
      }

      // 如果目前正在执行动画中 那么把下次添加的加到队列里执行
      if(addingRef.current){
        console.log("加入队列",num)
        queueRef.current.push(num)
        return
      }
      
      // 变化的旗帜数===获得的旗帜总数
      // 不等于的时候就说明是进度条未加载完毕
      if(changeflagNum!==flagNumRef.current){
        setchangeflagNum(flagNumRef.current);
      }
     
  
      let k = 5; //k表示进度条上剩余的位置
      if(flagNumRef.current%5==0&&flagNumRef.current!==0){
          k=0
      }
      else{
         k  = 5-(flagNumRef.current%5)
      }
      console.log("剩余的位置",k,"需要增加的数量",num)
  
      // 剩余位置够放置需要增加的数量
      if(num<=k){
        const newflagNum = flagNumRef.current+num;
        changeWidth(newflagNum)
        return 
      }
      // 剩余的位置不够放置需要增加的数量
      else{
        // 没有剩余位置了
        if(k===0){
          // 需要换一页
          setbarWidth(0)
          setnowNode(0)
            // 加延时是为了移除过渡属性之后再去改变宽度,避免宽度过渡变为0的过渡被监听到
            setTimeout(()=>{
              // 需要判断增加的num是否大于5
              if(num>=5){
                changeWidth( flagNumRef.current +5)
                // 剩下的放进执行队列里 下次添加
                queueRef.current.push(num-5)
              }else{
                changeWidth( flagNumRef.current +num)
                
              }
            },500)
          return 
        }
        // 剩余位置k 把剩余的位置k占满 再次递归的时候就会回到上面的情况
        else{
          changeWidth( flagNumRef.current+k)
          queueRef.current.push(num-k)
        }
      }
    }


  return(
    <div className={styles.progress}>
       <div className={styles.num}>
          <span className={styles.text}>我的旗帜数量:{changeflagNum>flagNum?flagNum:changeflagNum}/332<span className={styles.tipIcon} /></span>
        </div>

      {/* 底 */}
      <div className={styles.bar}>
        {/* 需要宽度变化的那层 */}
        {/* 进度条 */}
        <div className={styles.bar2} 
          ref = {barRef}
          style={{width:`${barWidth}%`,
          transition:barWidth===0?"":`width 1s linear`
        }}
          >

            {/* 蒙层 主要是为了颜色的渐变 */}
            <div className={styles.masker} />
        </div>

         {/* 节点 旗帜 数字 */}
        <div className={styles.warpper}>
          {
              [0,1,2,3,4,5].map((item)=>{
              return(<div className={styles.nodeWrapper} key={item}>
                    
                  <div style={{position:"relative", left:"-0.3rem",width:"0.7rem",display:"flex",justifyContent:"flex-end",alignItems:"flex-start"}}>
                    <span className={styles.number}>
                      {
                        flagNum%5==0&&flagNum!=0?item+(Math.floor(flagNum/5)-1)*5:
                        item+Math.floor(flagNum/5)*5
                      }
                    </span>
                    <span className={styles.flag}/>
                  </div>
                  {/* 节点得判断 */}
                  {
                    item==5?<div className={styles.nodeEnd} >
                      <span className={styles.giftIcon}
                      style={{ animation:barWidth==100?`${styles.twinkle} 0.8s infinite 1s`:""}}
                      />
                    </div>:
                    <div className={styles.node1}>
                      <span className={styles.node2} style={{
                        opacity:item%5<=nownode?1:0,
                        transition:nownode===0?"":`opacity 1.5s`
                      }}/>
                    </div>
                  }
                  
                </div>)
              })
          }
        </div>

        <div onClick={()=>{addFlag(3)}} className={styles.btn}>增加旗帜</div>
      </div>
      
    </div>
  )
}

export default Progressbar
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
  • 188
  • 189
  • 190
  • 191
  • 192
  • 193
  • 194
  • 195
  • 196
  • 197
  • 198
  • 199
  • 200
  • 201
  • 202
  • 203
  • 204
  • 205
  • 206
  • 207
  • 208
  • 209
  • 210
  • 211
  • 212
  • 213
  • 214
  • 215
  • 216
  • 217
  • 218
  • 219
  • 220
  • 221
  • 222
.progress{
  position: relative;
  width: 100%;
  .num{
    background:linear-gradient(#FFF2E8,#FFE2BE);
    border-radius: 0.15rem;
    width: 90%;
    height: 1rem;
    margin: auto;
    margin-top: 0.3rem;
    display: flex;
    justify-content: space-between;
    align-items: center;
    .text{
      color:#7A1C0E;
      font-size: 0.29rem;
      margin-left: 0.15rem;
    }
  }
  .bar{
    background-image: url(../../img/progress/flagProgressBar.png);
    background-repeat: no-repeat;
    background-size: 100% 100%;
    width: 6rem;
    height: 0.2rem;
    margin: 1rem auto;
    position: relative;
    .bar2{
      height: 0.2rem;
      border-radius: 0.2rem;
      background:linear-gradient(to right, #FF9445,#FFE29D); //外层的边框
      display: flex;
      align-items: center;
      transform: translateY(-0.04rem);
      position: absolute;
    
      .masker {
        position: absolute;
        width: calc(100% - 0.05rem);
        height: 0.16rem;
        opacity: 0.6;
        border-radius: 0.2rem;
        background:linear-gradient(to right, #FF8751,#FFE7D0);
        margin-left: 0.025rem;
       
      }
    }
    .bar2::after{
          width: calc(100% - 0.05rem);
          height: 0.16rem;
          border-radius: 0.2rem;

          // 制造倾斜条纹
          background: repeating-linear-gradient(-45deg, #FF8F5B  25%, #FE5404 0, #FE5404  46%, #FF8F5B  0, #FF8F5B  75%, #FE5404 0);
          background-size: 0.2rem 0.2rem;
          content: '';
          border-radius: 0.2rem;
          margin-left: 0.025rem;

          // 条纹移动的动画
          animation: panoramic 30s linear infinite;
          
    }
  }

  .warpper{
      position: absolute;
      width: 100%;
      display: flex;
      top: -0.4rem;
      left: 3%;
      justify-content: space-between;
      align-items: flex-start;
      z-index: 2;
      .num0{
        position: absolute;
        top: -0.1rem;
        left: -18%;
        font-size: 0.24rem;
        color: #E5571D;
        vertical-align: super;
      }
      .nodeWrapper{
        display: flex;
        flex-direction:column;
        align-items: flex-start;
        .number{
          font-size: 0.24rem;
          color: #E5571D;
          vertical-align: super;
          margin:-0.1rem 0.1rem 0 0;
        }
        .flag{
          display: inline-block;
          background-image: url(../../img/progress/flag.png);
          background-repeat: no-repeat;
          background-size: cover;
          width: 0.23rem;
          height: 0.33rem;
          vertical-align: text-top;
        }

        .node1{
          background:url(../../img/progress/node1.png);
          background-repeat: no-repeat;
          background-size: 100% 100%;
          display: block;
          width: 0.23rem;
          height: 0.23rem;
          position: relative;
          display: flex;
          justify-content: center;
          align-items: center;
          .node2{
            background:linear-gradient(45deg, #FE4107, #FF925E);
            border-radius: 50%;
            width: 0.15rem;
            height: 0.15rem;
            display: inline-block;
          }
        }
        .nodeEnd{
          background:url(../../img/progress/node1.png);
          background-repeat: no-repeat;
          background-size: 100% 100%;
          width: 0.23rem;
          height: 0.23rem;
          position: relative;
          z-index: 1;
          border-radius: 50%;
          transform: scale(1.65);
          .giftIcon{
            width: 0.25rem;
            height: 0.29rem;
            background-image: url(../../img/progress/giftIcon.png);
            background-size: cover;
            display: inline-block;
            position: absolute;
            top: 0;
            left: -0.015rem;
          }
          .giftIcon2{
            width: 0.25rem;
            height: 0.29rem;
            background-image: url(../../img/progress/openGift.png);
            background-size: cover;
            display: inline-block;
            position: absolute;
            top: 0;
            left: -0.015rem;
            transform: scale(1.2);
          }
        }
      }
     
  }

  .btn{
    background-color: #E5571D;
    border-radius: 0.5rem;
    width: 2rem;
    margin: auto;
    position: relative;
    top: 0.5rem;
    text-align: center;
    line-height: 0.5rem;
    height: 0.5rem;
    color: wheat;
    font-weight: bold;
    cursor: pointer;
  }
}   

/*进度条动画*/
@keyframes panoramic{
  to {
    background-position: 200% 0;
  }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/itdef/article/detail/60374
推荐阅读
相关标签
  

闽ICP备14008679号