赞
踩
整体思路就是通过监听宽度的过渡效果,因为需要一步一步去增加,进度条变化的同时控制数字和节点的变化,所以我们监听了过渡结束事件,并增加执行队列,保证了进度条动画结束后在执行下一次的动画执行
(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
.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; } }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。