步骤进度条(Stepper 分步表单进度组件动画源码)
步骤进度条源码:四步 Stepper 状态机,连线按百分比填充、圆点弹性放大并打勾、当前步呼吸高亮,上一步/下一步按钮联动禁用,原生 JS 零依赖,适合结算流程。
效果演示(真实可交互)
多步骤流程(注册、结算、问卷)最怕用户迷失,步骤进度条就是导航锚点。演示区模拟了一个四步流程:填写信息 → 确认订单 → 在线支付 → 完成。点击「下一步」,底部连接线以蓝绿色渐变从左向右填充对应百分比,当前步骤的圆点放大并泛起呼吸光环,已完成的圆点变为实心渐变并弹出 ✓;当前步名称同步更新,「上一步 / 下一步」按钮在首尾两端自动禁用,最后一步按钮变为「已完成 ✓」。整个流程是一个完整的状态机,接入真实业务时只需替换步骤名称数组与每步的表单内容。状态机的核心是单一数据源:当前步骤只存在 cur 变量里,渲染函数根据它一次性刷新圆点、连线、文案与按钮状态,不会出现多处状态不同步的问题。步数名称数组独立维护,换业务只需改 NAMES。分步流程中「回到上一步」同样重要:回退时已完成步骤的 ✓ 会保留,只有当前步状态变化,用户始终清楚自己走到哪、修改过什么,二次确认环节也不会被跳过。
当前第 1 步:填写信息
完整代码
HTML:步骤列表 + 状态文字 + 控制按钮,当前步骤存为 data-cur:
<div class="ps-steps" id="psSteps" data-cur="1">
<div class="ps-step ps-active" data-step="1">
<span class="ps-dot">1</span><span class="ps-name">填写信息</span>
</div>
<div class="ps-step" data-step="2">
<span class="ps-dot">2</span><span class="ps-name">确认订单</span>
</div>
<div class="ps-step" data-step="3">
<span class="ps-dot">3</span><span class="ps-name">在线支付</span>
</div>
<div class="ps-step" data-step="4">
<span class="ps-dot">4</span><span class="ps-name">完成</span>
</div>
</div>
<p class="ps-status" id="psStatus">当前第 1 步:填写信息</p>
<button id="psPrev">← 上一步</button>
<button id="psNext">下一步 →</button>
CSS:连接线用容器的 ::before(底槽)与 ::after(进度)双伪元素,宽度按步骤百分比切换:
.ps-steps { position: relative; display: flex; justify-content: space-between; }
.ps-steps::before { /* 底槽 */
content: ""; position: absolute; top: 19px; left: 10%; right: 10%; height: 3px;
background: #e4e8f5; border-radius: 3px;
}
.ps-steps::after { /* 进度填充 */
content: ""; position: absolute; top: 19px; left: 10%; height: 3px; width: 0;
background: linear-gradient(90deg, #6c8cff, #34c7a1);
transition: width .5s cubic-bezier(.4, 0, .2, 1);
}
.ps-steps[data-cur="2"]::after { width: 26.6%; } /* 四步均匀分布:80% / 3 */
.ps-steps[data-cur="3"]::after { width: 53.3%; }
.ps-steps[data-cur="4"]::after { width: 80%; }
圆点三态:当前步放大呼吸、完成步打勾弹入:
.ps-dot { width: 38px; height: 38px; border-radius: 50%; display: grid; place-items: center;
background: #fff; border: 2px solid #d4daea; color: #8892ab; font-weight: 700;
transition: background .4s ease, border-color .4s ease, color .4s ease, transform .4s cubic-bezier(.34,1.4,.64,1); }
.ps-step.ps-active .ps-dot { border-color: #6c8cff; color: #6c8cff; transform: scale(1.15);
box-shadow: 0 0 0 7px rgba(108,140,255,.15); animation: ps-pulse 1.6s ease-in-out infinite; }
@keyframes ps-pulse { 50% { box-shadow: 0 0 0 12px rgba(108,140,255,.06); } }
.ps-step.ps-done .ps-dot { background: linear-gradient(135deg, #6c8cff, #34c7a1);
border-color: transparent; color: #fff; font-size: 0; }
.ps-step.ps-done .ps-dot::after { content: "✓"; font-size: 16px; animation: ps-check .45s cubic-bezier(.34,1.6,.64,1); }
@keyframes ps-check { from { transform: scale(.3) rotate(-20deg); opacity: 0; } }
原生 JS:状态机 + 按钮联动:
const stepsBox = document.getElementById('psSteps');
const status = document.getElementById('psStatus');
const prevBtn = document.getElementById('psPrev');
const nextBtn = document.getElementById('psNext');
const NAMES = ['填写信息', '确认订单', '在线支付', '完成'];
let cur = 1;
function render() {
stepsBox.dataset.cur = cur;
stepsBox.querySelectorAll('.ps-step').forEach((step) => {
const n = Number(step.dataset.step);
step.classList.toggle('ps-done', n < cur);
step.classList.toggle('ps-active', n === cur);
});
status.textContent = `当前第 ${cur} 步:${NAMES[cur - 1]}`;
prevBtn.disabled = cur === 1;
nextBtn.disabled = cur === 4;
nextBtn.textContent = cur === 4 ? '已完成 ✓' : '下一步 →';
}
nextBtn.addEventListener('click', () => { if (cur < 4) { cur++; render(); } });
prevBtn.addEventListener('click', () => { if (cur > 1) { cur--; render(); } });
render();
参数说明
| 参数 | 默认值 | 说明 |
|---|---|---|
| 步骤数 | 4 | 增减步骤时同步调整 ::after 的宽度档位 |
| 进度档位 | 26.6% / 53.3% / 80% | 公式:80% × (cur-1) / (n-1),左右各留 10% |
| 填充时长 | transition: width .5s | 300-600ms,过长会显得拖沓 |
| 呼吸动画 | ps-pulse 1.6s | 当前步持续高亮,明确「你在这里」 |
| 完成打勾 | ps-check .45s | 过冲系数 1.6,✓ 弹出更俏皮 |
兼容性与性能
- 原生 JS + CSS 状态机,零依赖;宽度动画走
width属性,单次触发开销极小,如需极致性能可改用scaleX - 双伪元素画连接线无需额外 DOM,
data-cur单一数据源,状态可随时被外部 JS 读取或改写 - 按钮
disabled语义化:首步禁「上一步」、末步禁「下一步」,读屏可正确朗读禁用状态 - 圆点动画均为合成器属性 +
box-shadow呼吸,Chrome / Firefox / Safari / Edge 全兼容 - 连线进度使用
::after的width过渡,配合cubic-bezier(.4,0,.2,1)的标准 Material 缓动,观感匀速不突兀;若步骤很多,宽度档位按公式自动计算即可,不需要逐条手写
常见问题
Q: 进度条宽度和步骤对不齐? 宽度档位必须按公式 80% × (cur-1) / (n-1) 重算:四步时为 0 / 26.6 / 53.3 / 80%,改步骤数就要改所有档位,否则线头对不上圆点。
Q: 想支持点击步骤跳转? 给每个 .ps-step 加 click 监听并校验「只能回到已完成的步骤」,在 render() 里把可跳转步骤的圆点设为 cursor: pointer。
Q: 步骤很多(6 步以上)放不下? 缩小圆点与字号、允许 .ps-name 省略号截断,或改成横向滚动容器;连接线百分比公式不变。
Q: 可以商用吗? 可以。本组件为粤喵自研原创代码,免费商用无需署名。
许可与来源
- 许可证:自研原创 —— 粤喵自研原创,可免费用于个人与商业项目,无需署名。查看来源公示与许可清单
- 来源:粤喵团队原创自研(从效果思路到代码实现均为独立完成,未直接复制第三方代码)
- 首次发布:2026-09-01 · 最近更新:2026-09-01
- 修改日志:2026-09-01 首次发布;2026-09-13 补充许可证与来源标注、复制按钮说明与法务页入口
- 纠错:联系页面 / min@cactus.asia,2 个工作日内核实处理
常见问题
这个组件可以商用吗?
可以。本组件为粤喵自研原创,可免费用于个人和商业项目,无需署名。
浏览器兼容性如何?
支持所有现代浏览器(Chrome / Firefox / Safari / Edge 最新版本)。旧版 IE 不支持 CSS 动画相关特性。
怎么修改动画参数?
直接调整 CSS 中的 animation-duration(时长)、animation-timing-function(缓动曲线)等属性即可,代码中有注释说明。