记录–进度条真的是匀速的,不信你看

  • 记录–进度条真的是匀速的,不信你看已关闭评论
  • 44 次浏览
  • A+
所属分类:Web前端
摘要

点赞 + 收藏 === 学会🤣🤣🤣众所周知,进度条是程序员大大模拟的程序运行进度,一般会在某些数值卡住不动,引起99%悬案。但是背后的原理你真的清楚吗,其实进度条真的是匀速运动的!


🧑‍💻 写在开头

点赞 + 收藏 === 学会🤣🤣🤣

引言

众所周知,进度条是程序员大大模拟的程序运行进度,一般会在某些数值卡住不动,引起99%悬案。但是背后的原理你真的清楚吗,其实进度条真的是匀速运动的!

先来看看效果

记录--进度条真的是匀速的,不信你看

接下来开始实现

创建一个矩形,然后折叠起来,完成!

创建一个容器,用于宽度限制

折叠形状实际大小肯定是大于需求大小的,在这得用绝对定位限制,防止影响到页面布局。将每一段用block展示,后续使用3d变换折叠起来。

// stylus .outsidebox     position relative     height 20px     .loading-bar          display flex         position absolute         transform-style preserve-3d         height 20px         .block             height 100%             background #000f2e              <div class="outsidebox" :style="{width: `${showWidth}px`}">     <div class="loading-bar">         <div class="block"></div>     </div> </div>

建立主函数,接收参数为最终可视宽度

因折叠块起始是水平块,所以限制块个数为奇数,以横竖横方式生成。 艺术就是派大星! 随机数!块的宽度采用随机比值的方式生成,通过横向的比值和逆推实际总宽度。

const createRandomRatio = (num: number) => {     // 创建随机比值,合为1     let sum = 0;     const numbers = [];     for (let i = 0; i < num; i++) {         let randomNumber = Math.random();          numbers.push(randomNumber);         sum += randomNumber;     }     return numbers.map(num => num / sum); } const calcTotalWidht = (ratio: number[], width: number) => {     // 根据用户输入宽度,反向求出折叠前宽度     let r = 0;     for(let i = 0; i < ratio.length; i+=2) {         r += ratio[i];     }     let w = width / r;     return w } const createRadomRect = (width: number) => {     // 主函数入口,创建折叠矩形块     let num = 11;     let widthRatio = createRandomRatio(num); // 创建随机比值     let calcW = calcTotalWidht(widthRatio, width); // 逆向推导实际宽度     showWidth.value = width; // 用户看得到的宽度     totalWidth.value = calcW; // 实际的宽度 }

实现创建横块与纵块

上一段获取到总宽度,接下来根据比值生成具体的块。

1.生成横块,返回横块的水平,垂直偏移位置

const createHorizontal = (id: number, horizontal: number, vertial: number, width: number) => {     data.value.push({         id,         width,         transform: `translateX(${horizontal}px) translateZ(${vertial}px)`     })     return {         transX: horizontal,         transZ: vertial     } }

  1. 接收横块位置,生成纵块位置,返回纵块水平位置及垂直位置
const createVertical = (id: number, horizontal: number, vertial: number, width: number) => {     let direct = prevDirect.value == 1 ? -1 : 1; // 逆时针旋转则x右移z上移,顺时针则x右移z下移     let transX = prevDirect.value * vertial + width / 2; // 如前一个旋转块方向相反则需更新垂直移动方向,前逆后顺更改为左移     let transZ = horizontal * direct + width / 2 * -direct     data.value.push({         id,         width,         transform: `rotateY(${90 * direct}deg) translateZ(${transZ}px) translateX(${transX}px)`     })     prevDirect.value = direct;     return {         transX: horizontal + width * -1,         transZ: vertial + width * -direct     } }

  1. 调整主函数,增加循环生成横纵块逻辑
const totalWidth = ref(0); const showWidth = ref(0) const data = ref([] as any) const prevDirect = ref(1); const createRadomRect = (width: number) => {     // 主函数入口,创建折叠矩形块     let num = 11;     let widthRatio = createRandomRatio(num); // 创建随机比值     let calcW = calcTotalWidht(widthRatio, width); // 逆向推导实际宽度     showWidth.value = width; // 用户看得到的宽度     totalWidth.value = calcW; // 实际的宽度     let blockWidth = 0;     let transX = 0;     let transZ = 0;     for(let i = 0; i < num; i++) {         let rectWidth = Math.floor(calcW * widthRatio[i]);         if(i == num - 1) {             // 最后一个横块,修正floor带来的宽度缺失             rectWidth = width - blockWidth         }          if(i % 2 == 0) {             blockWidth += rectWidth;             let obj = createHorizontal(i, transX, transZ, rectWidth)             transX = obj.transX;             transZ = obj.transZ;         } else {             let obj = createVertical(i,transX, transZ, rectWidth)             transX = obj.transX;             transZ = obj.transZ;         }     } }

初具规模了,嘿嘿

记录--进度条真的是匀速的,不信你看

 

实现进度动画

将进度抽象为0-1,对应的UI展示效果就是背景色的填充进度。因为是分块,所以将总体的宽度*进度,再分摊到每个块上进行显示。动画采用requestAnimationFrame API实现,懂得都懂。每步长度设置为0.001,这样看起来比较美观。

const progress = ref(0) const calcChangeRect = (progress: number) => {     // 计算每个矩形的进度     let current = progress * totalWidth.value;     let list = data.value;     let add = 0;     for(let i = 0; i < list.length; i++) {         if(list[i].width + add > current) {             list[i].progress = (current - add) / list[i].width;             break         } else {             list[i].progress = 1;             add += list[i].width;         }     } } const createAnimation = () => {     let animationId = 0;     let start = () => {         progress.value += 0.001;         if(progress.value < 1) {             animationId = window.requestAnimationFrame(start)         } else {             progress.value = 1;             window.cancelAnimationFrame(animationId);         }         calcChangeRect(progress.value)     }     animationId = window.requestAnimationFrame(start) }

最后加点debugger工具

1.设置鼠标旋转事件

const rotateStyle = ref("") const useMouseMove = ref(false) const toggleMouseMove = () => {     useMouseMove.value = !useMouseMove.value } const handleMouseMove =  (event: MouseEvent) => {     if(!useMouseMove.value) return 0     let pageX = event.pageX,         pageY = event.pageY;     const winW = window.innerWidth / 2,           winH = window.innerHeight / 2;     let X = 0;     let Y = 0;     if(pageX < winW) {         X = -((winW - pageX) / winW * 90);     } else {         X = (pageX - winW) / winW * 90     }      if(pageY < winH) {         Y = -((winH - pageY) / winH * 90);     } else {         Y = (pageY - winH) / winH * 90     }     rotateStyle.value = `transform: rotateY(${X}deg) rotateX(${Y}deg)` } document.addEventListener("mousemove", handleMouseMove);

2.设置主视图和45度视图

const setDisplay45 = () => {     useMouseMove.value = false;     rotateStyle.value = `transform: rotate3d(1, 1, 0, 45deg)` } const setDisplay0 = () => {     useMouseMove.value = false;     rotateStyle.value = `` }

结语

这个项目是为了熟悉3d变换,在使用translateZ、translateX想了很久,脑子不够用了。还有很多地方可以调整为配置项,设置块数,块颜色等,甚至封装成api,下次一定。

本文转载于:https://juejin.cn/post/7370682158103347238

如果对您有所帮助,欢迎您点个关注,我会定时更新技术文档,大家一起讨论学习,一起进步。

 记录--进度条真的是匀速的,不信你看