MProjectsNDays之3:Progress Steps

415 阅读4分钟

这是MProjectsNDays的第2个项目,可点击体验,名字我不知道怎么翻译为中文更好,但是大家看到效果后一定能意会:

横向

纵向

其中第二个纵向的效果是我在原版的基础上拓展而来的,以此为基础可以实现常见的物流跟踪效果

老规矩,首先看html布局:

<body>
    <div class="container">
        <div class="progress-container">
            <div class="progress" id="progress"></div>
            <div class="circle active">1</div>
            <div class="circle">2</div>
            <div class="circle">3</div>
            <div class="circle">4</div>
        </div>

        <button class="btn" id="prev" disabled>上一步</button>
        <button class="btn" id="next">下一步</button>
    </div>
    <script src="script.js"></script>
</body>

html非常简单,不再赘述,等下结合css做讲解。

核心css如下:

:root {
    --line-border-fill: #3498db;
    --line-border-empty: #e0e0e0;
}

:root表示整个html的根元素,其他元素都是后代。那么在此处的用处就是定义了两个全局变量,方便后续使用。使用的方法有两种,第二种可以指定默认值。

var(--line-border-empty)
var(--line-border-empty,#000)
.progress-container {
    display: flex;
    justify-content: space-between;
    position: relative;
    margin-bottom: 30px;
    width: 350px;
}

progress-container使用了flex布局,space-between表示均匀排列每个元素,首个元素放置于起点,末尾元素放置于终点

.progress-container::before {
    content: '';
    background-color: var(--line-border-empty);
    position: absolute;
    height: 4px;
    top: 50%;
    width: 100%;
    transform:translateY(-50%);
    z-index: -1;
}

background-color: var(--line-border-empty);去掉,进度条底色就不见了,如下图所示:

image.png

可见,上述css核心功能就是生成进度条底色

content: ''——与 :before及 :after伪元素配合使用,插入生成内容。此处没有内容,如果去掉的话会有问题,如图:

image.png position: absolute——与progress-container中的position: relative前后呼应,表示相对于progress-container进行定位。

这里补充下这两个属性的知识:

relative:生成相对定位的元素,相对于其正常位置进行定位
absolute:生成绝对定位的元素,相对于static定位以外的第一个父元素进行定位

top: 50%——向上间隔50%的高度。由于自身的高度,单纯使用此属性并不会居中。 transform:translateY(-50%)——向上移动自身高度的50%,与上个属性结合便可以实现垂直居中。 为了便于直观理解,我这里放两张图,分别去掉其中一个属性:

image.png

image.png

第二个图需要仔细一点才能看出进度条底色没有垂直居中,因为其自身高度很小,所以没有偏移一半也不明显。

z-index: -1——设置元素的堆叠顺序。拥有更高堆叠顺序的元素处于较低元素的前面。 这里去掉该属性来感受一下:

image.png

.progress {
    background-color: var(--line-border-fill);
    position: absolute;
    height: 4px;
    top: 50%;
    width: 0%;
    transform:translateY(-50%);
    z-index: -1;
    transition: 0.4s ease;
}

这是进度条样式,有了progress-container::before的讲解,这里便无需赘述了,只在最后加入了动画:transition: 0.4s ease

.circle {
    background-color: #fff;
    color: #999;
    border-radius: 50%;
    height: 30px;
    width: 30px;
    display: flex;
    align-items: center;
    justify-content: center;
    border: 3px solid var(--line-border-empty);
    transition: 0.4s ease;
}

.circle.active {
    border-color: var(--line-border-fill);
}

circle定义了圆的样式,使用flex布局的三条属性是为了使圆中的数字居中,不是核心。.circle.active则定义了进度为当前位置时圆的样式,即将边框颜色改变了。

.btn {
    background-color: var(--line-border-fill);
    color: #fff;
    border: 0;
    border-radius: 6px;
    margin: 5px;
    padding: 8px 30px;
}

.btn:active {
    transform: scale(1.05);
}

.btn:disabled {
    background-color: var(--line-border-empty);
    cursor: not-allowed;
}

以上是按钮相关的样式,transform: scale(1.05)使得按钮被点击时放大。

js代码如下,其实也非常简单,只是对按钮、圆圈和进度条做了一些控制。

const progress = document.getElementById('progress')
const prev = document.getElementById('prev')
const next = document.getElementById('next')
const circles = document.querySelectorAll('.circle')
const verticel = document.querySelector('.vertical')

let currentActive = 1

next.addEventListener('click', () => {
    currentActive++;
    if (currentActive > circles.length) {
        currentActive = circles.length;
    }
    update();
})

prev.addEventListener('click', () => {
    currentActive--;
    if (currentActive < 1) {
        currentActive = 1;
    }
    update();
})

function update() {
    // circle状态控制
    circles.forEach((circle, index) => {
        if (currentActive < index + 1) {
            circle.classList.remove('active');
        } else {
            circle.classList.add('active');
        }
    })

    // 按钮状态控制
    if(currentActive <= 1) {
        prev.disabled = true;
    }else if(currentActive >= circles.length) {
        next.disabled = true;
    }else {
        next.disabled = false;
        prev.disabled = false;
    }

    // 设置进度条长度
    if(verticel) {
        progress.style.height = (currentActive - 1) * 100 / (circles.length - 1) + '%';
    }else {
        progress.style.width = (currentActive - 1) * 100 / (circles.length - 1) + '%';
    }
}

注意最后设置了进度条长度部分进行了判断,如果是垂直方向的进度条,改变的就是高度;如果是水平方向的,改变的则是宽度。 除了js代码之外,要实现垂直模式的效果,css部分也有所增加:

.progress-container.vertical {
    flex-direction: column;
    align-items: center;
    height: 350px;
}

.vertical::before {
    width: 4px;
    right: 50%;
    height: 100%;
    top: 0%;
    transform:translateX(50%);
}

.progress.vertical {
    top: 0%;
    width: 4px;
    right: 50%;
    height: 0%;
    transform:translateX(50%);
}

其思路与水平方向是相对的,此处不再赘述。

至此,整个项目就完成了! 完整源码可在此查看