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

cnblogs 2024-07-23 08:11:00 阅读 67

🧑‍💻 写在开头

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

引言

众所周知,进度条是程序员大大模拟的程序运行进度,一般会在某些数值卡住不动,引起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 :>

<div >

<div ></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

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



声明

本文内容仅代表作者观点,或转载于其他网站,本站不以此文作为商业用途
如有涉及侵权,请联系本站进行删除
转载本站原创文章,请注明来源及作者。