antd Progress 动态进度条实战:用 React 状态驱动可交互的进度展示
2026/9/19 5:00:03 网站建设 项目流程

antd Progress 动态进度条实战:用 React 状态驱动可交互的进度展示

【免费下载链接】ant-designAn enterprise-class UI design language and React UI library项目地址: https://gitcode.com/gh_mirrors/ant/ant-design

导读

本文以 Ant Design(antd)Progress组件的dynamic官方示例为核心,讲解如何用 ReactuseState状态驱动进度条实时变化,完整覆盖线型(line)与圆型(circle)两种形态的用法。文中不仅给出可直接运行的完整代码,还结合本仓库中 Progress 源码、Line 实现、Circle 实现 及 demo 快照测试 剖析底层渲染与边界钳制原理,帮助你从"会用"进阶到"懂原理",并能将其迁移到上传进度、任务执行、表单提交等真实业务场景。

示例背景:官方 "Dynamic" Demo 是什么

在 antd 官方文档中,Progress 组件提供了约 16 个示例(见 Progress 组件文档),其中 "Dynamic"(动态展示)示例对应的正是本文要讲解的 dynamic.tsx 及其 dynamic.md 说明。官方对该示例的中文描述只有一句话:

会动的进度条才是好进度条。

英文为 "A dynamic progress bar is better."——这句话点明了示例的核心意图:Progress 并非只能静态展示一个固定百分比,而是可以随着用户交互或数据变化实时刷新。该示例通过"加号 / 减号"两个按钮控制进度值,让线型进度条与圆型进度圈同步变化,是理解 antd 受控组件用法的经典入门案例。

从组件目录结构看,该示例被index.en-US.mdindex.zh-CN.md通过<code src="./demo/dynamic.tsx">的方式挂载到文档示例区,并会被 demo 测试快照(renders components/progress/demo/dynamic.tsx correctly)自动渲染校验,保证示例代码始终可用。

完整代码与逐行拆解

示例的完整代码位于 components/progress/demo/dynamic.tsx,核心实现如下:

import React, { useState } from 'react'; import { MinusOutlined, PlusOutlined } from '@ant-design/icons'; import { Button, Flex, Progress } from 'antd'; const App: React.FC = () => { const [percent, setPercent] = useState<number>(0); const increase = () => { setPercent((prevPercent) => { const newPercent = prevPercent + 10; if (newPercent > 100) { return 100; } return newPercent; }); }; const decline = () => { setPercent((prevPercent) => { const newPercent = prevPercent - 10; if (newPercent < 0) { return 0; } return newPercent; }); }; return ( <Flex vertical gap="small"> <Flex vertical gap="small"> <Progress percent={percent} type="line" /> <Progress percent={percent} type="circle" /> </Flex> <Button.Group> <Button onClick={decline} icon={<MinusOutlined />} /> <Button onClick={increase} icon={<PlusOutlined />} /> </Button.Group> </Flex> ); }; export default App;

1. 状态管理:唯一数据源

const [percent, setPercent] = useState<number>(0);

进度值percent是页面上唯一的"数据源",两个 Progress 组件共享同一个状态,因此点击按钮后线型与圆型进度条会保持数值一致、同步动画。初始值为0,对应 Progress 组件percent属性的默认值(见 progress.tsx 源码 中percent = 0的默认解构)。

2. 步进函数:基于函数式更新的加减逻辑

const increase = () => { setPercent((prevPercent) => { const newPercent = prevPercent + 10; if (newPercent > 100) { return 100; } return newPercent; }); }; const decline = () => { setPercent((prevPercent) => { const newPercent = prevPercent - 10; if (newPercent < 0) { return 0; } return newPercent; }); };

这里有两个值得注意的编码细节:

  • 函数式更新(setPercent(prev => ...):回调接收上一次的状态值,而非直接读取闭包中的percent。这在连续多次调用、或进度由定时器/异步任务驱动时能避免闭包过期问题,是 React 官方推荐的写法。
  • 边界钳制(clamp):每次加 10,超过 100 就固定为 100;每次减 10,小于 0 就固定为 0。这保证了传入percent的值永远在合法区间内。实际上即使你忘了做这个判断,组件内部也有兜底——见下文"源码级原理"中对validProgress的分析。

3. 布局与按钮组

<Flex vertical gap="small"> ... <Button.Group> <Button onClick={decline} icon={<MinusOutlined />} /> <Button onClick={increase} icon={<PlusOutlined />} /> </Button.Group> </Flex>
  • Flex是 antd 的弹性布局组件,vertical让子元素纵向排列,gap="small"控制间距;
  • Button.Group将两个图标按钮组合成连体按钮组;
  • 按钮通过icon属性引入@ant-design/iconsMinusOutlined(减号)与PlusOutlined(加号),点击分别触发declineincrease

两种形态:line 与 circle 的渲染差异

示例同时展示了 Progress 最常用的两种形态:

<Progress percent={percent} type="line" /> <Progress percent={percent} type="circle" />
形态type取值渲染方式默认尺寸
线型进度条line(默认值)div+ CSS 宽度百分比(见 Line.tsx)高度 8px(small为 6px)
圆型进度圈circle基于rc-progress的 SVG<circle>(见 Circle.tsx)120 × 120(small为 60)

从 utils.ts 的getSize实现 可以确认:

  • 线型默认高度:size === 'small' ? 6 : 8,且宽度为-1时外层自动撑满100%
  • 圆型默认尺寸:size === 'small' ? 60 : 120,内部文字字号由width * 0.15 + 6计算得出(见 Circle.tsx),所以圆越大,中央百分比数字越大。

源码级原理:percent 是如何变成进度条的

理解动态进度的底层逻辑,需要看 Progress 的渲染链路:progress.tsx(入口分发)→Line.tsx/Circle.tsx(具体形态)→utils.ts(数值校验)。

1. 数值钳制:validProgress

在 utils.ts 中:

export function validProgress(progress?: number) { if (!progress || progress < 0) { return 0; } if (progress > 100) { return 100; } return progress; }

无论你传入的percent是负数、NaN还是大于 100 的值,最终渲染前都会被钳制到 0~100 区间。线型进度条的宽度正是${validProgress(percent)}%(见 Line.tsx),圆型进度圈则通过getPercentage计算出实际角度数组(见 utils.ts)。这与示例中手动做边界判断的思路一致,属于"双保险"。

2. 状态自动判定:percent ≥ 100 即 success

在 progress.tsx 中:

const progressStatus = React.useMemo(() => { if (!ProgressStatuses.includes(status!) && percentNumber >= 100) { return 'success'; } return status || 'normal'; }, [status, percentNumber]);

当未显式指定status且进度达到 100 时,组件会自动进入success状态:线型进度条变为绿色,圆型进度圈中央的数字会替换为对勾图标(CheckCircleFilled/CheckOutlined,见 progress.tsx)。也就是说,在 dynamic 示例中把进度加到 100 时,你会看到状态自动变化——无需额外编码。

3. 无障碍与可访问性:role="progressbar"

从 demo 快照 可以看到,Progress 渲染出的 DOM 自带完整 ARIA 语义:

<div aria-valuemax="100" aria-valuemin="0" aria-valuenow="0" class="ant-progress ant-progress-status-normal ant-progress-line ..." role="progressbar" >

这来自 progress.tsx 中对容器元素role="progressbar"aria-valuenow={percentNumber}aria-valuemin={0}aria-valuemax={100}的设置。percent每次变化,aria-valuenow都会随之更新,屏幕阅读器用户也能感知进度变化,这是动态进度在无障碍层面的关键保障。

4. 动画过渡

圆型进度圈的 SVG 路径带有transition: stroke-dashoffset .3s ease ...(见快照中style属性),线型进度条同样依赖 CSS 过渡。因此当percent状态被按钮快速连续修改时,进度条是平滑"流动"的,而不是生硬跳变——这正是"会动的进度条"体验的来源。

扩展实战:把动态进度迁移到真实场景

掌握了状态驱动的思路后,你可以轻松扩展出多种真实场景的进度方案。

场景一:定时器驱动的进度(如倒计时、任务轮询)

import React, { useEffect, useState } from 'react'; import { Progress } from 'antd'; const AutoProgress: React.FC = () => { const [percent, setPercent] = useState(0); useEffect(() => { const timer = setInterval(() => { setPercent((prev) => (prev >= 100 ? 0 : prev + 1)); }, 100); return () => clearInterval(timer); }, []); return <Progress percent={percent} />; };

注意这里依然使用函数式更新setPercent((prev) => ...),避免定时器闭包读到过期状态。

场景二:自定义文字格式(format)

进度文案并不一定是百分比。参考 format 示例,可以这样改造:

<Progress percent={percent} format={(p) => (p >= 100 ? 'Done' : `${p} Days`)} />

format的函数签名是(percent?: number, successPercent?: number) => ReactNode,默认值为(percent) => percent + '%'(见 index.en-US.md API 表格)。

场景三:更多参数组合

结合 Progress API 文档 与 ProgressProps 类型定义,常用的动态场景参数如下:

参数说明类型默认值
percent完成百分比,动态进度的核心驱动值number0
type形态:line/circle/dashboardstringline
showInfo是否显示进度数值与状态图标booleantrue
status状态:success/exception/normal/active(仅 line)string-
strokeColor进度条颜色,传对象可渲染渐变string | string[] | object-
trailColor未填充部分的颜色string-
strokeLinecap端点样式:round/butt/squarestringround
size尺寸:数字 / 数组 / 对象 /small/defaultnumber | [number, number] | object | stringdefault
success成功段配置:{ percent, strokeColor }object-
steps分步显示总数(line 为 number,circle 可为对象)number | { count, gap }-

例如模拟一个带失败状态的上传进度:

<Progress percent={percent} status={percent === 100 ? 'success' : 'active'} strokeColor={{ from: '#108ee9', to: '#87d068' }} />

status="active"会为线型进度条附加流动动画条纹;strokeColor传入{ from, to }对象时,底层 handleGradient 会生成linear-gradient(to right, from, to)渐变背景。注意status是受控的:一旦显式传入,组件就不再依据percent >= 100自动切换状态。

小结

通过官方dynamic示例,我们掌握了 antd Progress 动态进度的完整套路:一个状态值 + 函数式更新 + 边界钳制 + 受控渲染。其底层由 progress.tsx 统一分发、Line.tsx 与 Circle.tsx 分别渲染、utils.ts 保证数值合法,同时内置了 ARIA 无障碍语义与平滑过渡动画。把这套模式套用到上传、任务、轮询等任何"过程可量化"的业务中,即可快速产出体验良好的进度反馈。

【免费下载链接】ant-designAn enterprise-class UI design language and React UI library项目地址: https://gitcode.com/gh_mirrors/ant/ant-design

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询