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

🧑‍💻 写在开头

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

引言

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

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

 

热门相关:别吃那个鬼   剑道邪尊   大唐第一世家   信息全知者   梁医生又在偷偷套路我