RN for OpenHarmony 实战 TodoList 项目:任务完成进度条

案例开源地址:https://atomgit.com/lqjmac/rn_openharmony_todolist

进度的力量

人类天生喜欢看到进度。游戏里的经验条、下载时的百分比、健身 App 的步数环,这些进度指示器让我们感觉"在前进"。

在 TodoList 应用中,进度条告诉用户"你完成了多少"。当进度条从 0% 慢慢涨到 100%,用户会有成就感。这种正向反馈能激励用户继续完成任务。

我们的应用有两个进度条:任务列表页的小进度条和统计页的大进度条。它们显示的是同一个数据,但视觉呈现不同。


进度数据的计算

进度条需要三个数据:总任务数、已完成数、完成百分比。

tsx 复制代码
const totalTasks = tasks.length;
const completedTasks = tasks.filter(t => t.completed).length;
const progress = totalTasks > 0 ? (completedTasks / totalTasks) * 100 : 0;

总任务数

tasks.length 直接获取数组长度,这是最简单的。

已完成数

tasks.filter(t => t.completed).length 先筛选出已完成的任务,再取长度。filter 方法返回一个新数组,包含所有 completedtrue 的任务。

完成百分比

tsx 复制代码
const progress = totalTasks > 0 ? (completedTasks / totalTasks) * 100 : 0;

这里有一个三元运算符处理边界情况。如果 totalTasks 是 0,直接除会得到 NaN(除以零)。所以先判断,如果没有任务就返回 0。

百分比的计算

(completedTasks / totalTasks) * 100 是标准的百分比计算。比如完成了 3 个任务,总共 5 个,那就是 (3 / 5) * 100 = 60


任务列表页的进度条

任务列表页顶部的统计区域有一个小进度条:

tsx 复制代码
<View style={styles.progressContainer}>
  <View style={[styles.progressBar, {backgroundColor: theme.border}]}>
    <View style={[styles.progressFill, {width: `${progress}%`, backgroundColor: theme.accent}]} />
  </View>
  <Text style={[styles.progressText, {color: theme.subText}]}>{Math.round(progress)}%</Text>
</View>

三层结构

进度条是三层嵌套的 View

  1. progressContainer 是最外层容器,负责整体布局
  2. progressBar 是进度条的背景轨道
  3. progressFill 是进度条的填充部分

宽度的动态设置

tsx 复制代码
{width: `${progress}%`}

填充部分的宽度用百分比设置。如果 progress 是 60,宽度就是 '60%'。这样填充部分会占据轨道的 60%。

百分比文字

tsx 复制代码
<Text style={[styles.progressText, {color: theme.subText}]}>{Math.round(progress)}%</Text>

Math.round(progress) 对百分比四舍五入。如果 progress 是 66.666...,显示的是 67%。小数点后的数字对用户没有意义,整数更清晰。


进度条的样式

tsx 复制代码
progressContainer: {flex: 1.5, alignItems: 'center'},
progressBar: {width: '100%', height: 8, borderRadius: 4, overflow: 'hidden'},
progressFill: {height: '100%', borderRadius: 4},
progressText: {fontSize: 12, marginTop: 4},

progressContainer 容器样式

  • flex: 1.5 让进度条区域比其他统计项宽一些,因为进度条需要更多空间
  • alignItems: 'center' 让内容水平居中

progressBar 轨道样式

  • width: '100%' 占满容器宽度
  • height: 8 轨道高度 8 像素,不太粗也不太细
  • borderRadius: 4 圆角是高度的一半,让轨道两端是半圆形
  • overflow: 'hidden' 隐藏超出部分,确保填充部分的圆角不会超出轨道

progressFill 填充样式

  • height: '100%' 高度填满轨道
  • borderRadius: 4 同样的圆角,让填充部分也是圆润的

progressText 文字样式

  • fontSize: 12 小字体,不抢眼
  • marginTop: 4 与进度条的间距

统计页的大进度条

统计页有一个更大、更醒目的进度条:

tsx 复制代码
<View style={[styles.statsCard, {backgroundColor: theme.card, borderColor: theme.border}]}>
  <Text style={[styles.statsCardTitle, {color: theme.text}]}>完成率</Text>
  <View style={styles.progressLarge}>
    <View style={[styles.progressBarLarge, {backgroundColor: theme.border}]}>
      <View style={[styles.progressFillLarge, {width: `${progress}%`, backgroundColor: theme.accent}]} />
    </View>
    <Text style={[styles.progressPercent, {color: theme.accent}]}>{Math.round(progress)}%</Text>
  </View>
</View>

卡片包装

大进度条放在一个卡片里,有标题"完成率"。这让它成为一个独立的信息模块。

更大的视觉呈现

大进度条的百分比文字更大、颜色更醒目,用主题强调色而不是次要文字颜色。


大进度条的样式

tsx 复制代码
progressLarge: {alignItems: 'center'},
progressBarLarge: {width: '100%', height: 12, borderRadius: 6, overflow: 'hidden'},
progressFillLarge: {height: '100%', borderRadius: 6},
progressPercent: {fontSize: 24, fontWeight: 'bold', marginTop: 12},

更粗的轨道

height: 12 比小进度条的 8 像素更粗,视觉上更有分量。

更大的百分比

fontSize: 24fontWeight: 'bold' 让百分比数字成为视觉焦点。用户一眼就能看到完成了多少。

更大的间距

marginTop: 12 让百分比和进度条之间有更多空间,不会显得拥挤。


进度条的颜色

tsx 复制代码
<View style={[styles.progressBar, {backgroundColor: theme.border}]}>
  <View style={[styles.progressFill, {width: `${progress}%`, backgroundColor: theme.accent}]} />
</View>

轨道颜色

轨道用 theme.border 颜色,是一个低调的灰色。它是背景,不应该太抢眼。

填充颜色

填充用 theme.accent 颜色,是主题的强调色紫色。它是前景,应该醒目。

颜色对比

灰色轨道配紫色填充,对比明显。用户能清楚地看到"完成了多少"和"还剩多少"。


进度为 0 和 100 的特殊情况

进度为 0

当没有完成任何任务时,progress 是 0,填充部分宽度是 '0%',也就是不显示。用户只能看到灰色的轨道。

进度为 100

当所有任务都完成时,progress 是 100,填充部分宽度是 '100%',完全覆盖轨道。用户看到的是一条完整的紫色进度条。

没有任务

当任务列表为空时,totalTasks 是 0,progress 被设为 0。显示一个空的进度条,而不是报错。


进度条的动画效果

当前的实现没有动画,进度变化是瞬间的。如果想要平滑的动画效果:

tsx 复制代码
const progressAnim = useRef(new Animated.Value(0)).current;

useEffect(() => {
  Animated.timing(progressAnim, {
    toValue: progress,
    duration: 500,
    useNativeDriver: false,
  }).start();
}, [progress]);

// 使用
<Animated.View style={[styles.progressFill, {
  width: progressAnim.interpolate({
    inputRange: [0, 100],
    outputRange: ['0%', '100%'],
  }),
  backgroundColor: theme.accent
}]} />

动画的原理

Animated.Value 存储动画值,当 progress 变化时,用 Animated.timing 让动画值平滑过渡到新值。interpolate 把 0-100 的数值映射到 '0%'-'100%' 的宽度。

useNativeDriver 的限制

useNativeDriver: false 是必须的,因为宽度动画不能用原生驱动。这意味着动画性能可能不如原生驱动的动画,但对于简单的进度条来说完全够用。


进度条与筛选的关系

进度条显示的是所有任务的完成情况,不受筛选影响:

tsx 复制代码
const totalTasks = tasks.length;
const completedTasks = tasks.filter(t => t.completed).length;

基于原始数据

计算用的是 tasks 而不是 filteredTasks。即使用户筛选了"待办"任务,进度条仍然显示整体的完成率。

为什么这样设计

如果进度条跟着筛选变化,用户筛选"已完成"后会看到 100% 的进度,这没有意义。进度条应该反映整体情况,给用户一个全局视角。


进度条的可访问性

为了让进度条对所有用户友好:

tsx 复制代码
<View 
  style={styles.progressContainer}
  accessibilityRole="progressbar"
  accessibilityValue={{min: 0, max: 100, now: Math.round(progress)}}
  accessibilityLabel={`任务完成进度 ${Math.round(progress)}%`}
>

accessibilityRole

accessibilityRole="progressbar" 告诉屏幕阅读器这是一个进度条。

accessibilityValue

accessibilityValue 提供进度条的数值信息:最小值、最大值、当前值。

accessibilityLabel

accessibilityLabel 是屏幕阅读器会朗读的文字,比如"任务完成进度 60%"。


进度条的变体

除了水平进度条,还有其他形式:

环形进度条

SVGreact-native-svg 画一个圆环,用 strokeDasharraystrokeDashoffset 控制进度。环形进度条更紧凑,适合在小空间里显示。

分段进度条

把进度条分成几段,每完成一个任务点亮一段。这种形式更有"打怪升级"的感觉。

数字进度

不用进度条,直接显示"3/5"或"60%"。最简单,但视觉冲击力不如进度条。


进度条的心理学

进度条不只是显示数据,它还影响用户的心理。

成就感

看到进度条增长,用户会有成就感。这种正向反馈能激励用户继续完成任务。

目标感

进度条暗示了一个目标:100%。用户会不自觉地想要填满它。

压力感

如果进度条长期停在很低的位置,用户可能会感到压力。设计时要注意,不要让进度条成为焦虑的来源。


进度条的颜色变化

可以根据进度改变颜色:

tsx 复制代码
const getProgressColor = () => {
  if (progress < 30) return '#ff6b6b';  // 红色,进度低
  if (progress < 70) return '#ffd93d';  // 黄色,进度中等
  return '#6bcb77';  // 绿色,进度高
};

颜色的语义

红色表示"还有很多要做",黄色表示"进行中",绿色表示"快完成了"。这种颜色变化能给用户更直观的反馈。

是否需要

这取决于产品定位。如果想强调"完成任务"的成就感,颜色变化是好的。如果想保持界面简洁,单一颜色也可以。


小结

进度条是一个简单但有效的 UI 元素。它用视觉的方式告诉用户"完成了多少",比单纯的数字更直观。

实现要点:

  • 进度计算要处理除以零的边界情况
  • 进度条是三层结构:容器、轨道、填充
  • 填充部分的宽度用百分比动态设置
  • 百分比数字用 Math.round 四舍五入
  • 进度条基于原始数据计算,不受筛选影响

好的进度条应该是"一眼就懂、看了想填满"。它不只是数据的可视化,更是一种激励机制,推动用户完成更多任务。


欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net

相关推荐
cn_mengbei16 小时前
从零到一:基于Qt on HarmonyOS的鸿蒙PC原生应用开发实战与性能优化指南
qt·性能优化·harmonyos
Van_Moonlight16 小时前
RN for OpenHarmony 实战 TodoList 项目:深色浅色主题切换
javascript·开源·harmonyos
俩毛豆16 小时前
华为的“天工计划”是什么
华为·harmonyos·鸿蒙·搜索·小艺
小贵子的博客16 小时前
Ant Design Vue <a-table>
前端·javascript·vue.js·anti-design-vue
天天进步201516 小时前
【Nanobrowser源码分析4】交互篇: 从指令到动作:模拟点击、滚动与输入的底层实现
开发语言·javascript·ecmascript
FIT2CLOUD飞致云16 小时前
应用升级为智能体,模板中心上线,MaxKB开源企业级智能体平台v2.5.0版本发布
人工智能·ai·开源·1panel·maxkb
console.log('npc')16 小时前
vue2中子组件父组件的修改参数
开发语言·前端·javascript
Van_captain17 小时前
rn_for_openharmony常用组件_Chip纸片
javascript·开源·harmonyos
奋斗吧程序媛17 小时前
vue3 Study(1)
前端·javascript·vue.js