记录--进度条真的是匀速的,不信你看
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
}
}
- 接收横块位置,生成纵块位置,返回纵块水平位置及垂直位置
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
}
}
- 调整主函数,增加循环生成横纵块逻辑
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
如果对您有所帮助,欢迎您点个关注,我会定时更新技术文档,大家一起讨论学习,一起进步。
声明
本文内容仅代表作者观点,或转载于其他网站,本站不以此文作为商业用途
如有涉及侵权,请联系本站进行删除
转载本站原创文章,请注明来源及作者。