HarmonyOS APP开发---“步道“运动数据可视化App,需要用到这个库
HarmonyOS APP开发—"步道"运动数据可视化App,需要用到这个库
做一个运动数据 App,首页要画圆环进度(今日步数完成度)、折线图(一周心率趋势)、SVG 路径(跑步轨迹)。用 PNG 画图表放大模糊,用三方图表库太重——
@react-native-ohos/react-native-svg直接用 Path/Circle/渐变画矢量图形,任意缩不失真。
📦 仓库地址:https://gitcode.com/CPF-RN/rntpc_react-native-svg | 安装:
npm install @react-native-ohos/react-native-svg
写在前面
"步道"是一个运动数据可视化 App,首页 Dashboard 要展示:
- 步数进度环:今日 8234/10000 步,圆环填充 82%
- 心率折线图:最近 7 天静息心率趋势
- 跑步轨迹:GPS 路径用 SVG Path 绘制在地图叠加层上
- 卡路里仪表盘:半圆仪表盘 + 渐变填充
这些图形的共同特点:随数据动态变化、需要矢量精度、要用渐变和动画。PNG 图方案直接出局——放大模糊、不能动态变化。用 react-native-chart 库又太重,定制困难。
react-native-svg 让你像写 HTML SVG 一样在 RN 里画矢量图形——Path/Circle/Rect/Line/LinearGradient 全套元素,原生层渲染,任意缩放不失真,还能和 Reanimated 联动做动画。
这篇文章聊什么
- 步数进度环——Circle + strokeDashoffset 动画
- 心率折线图——动态生成 Path d 属性
- 卡路里仪表盘——渐变半圆 + 弧线
第一步:安装
npm install @react-native-ohos/react-native-svg
第二步:步数进度环
用 Circle 的 strokeDasharray + strokeDashoffset 实现圆环进度:
import Svg, { Circle, Defs, LinearGradient, Stop } from 'react-native-svg'
import { useEffect } from 'react'
const RADIUS = 70
const CIRCUMFERENCE = 2 * Math.PI * RADIUS // 周长
const TARGET = 10000
const CURRENT = 8234
const PERCENT = CURRENT / TARGET
function StepProgressRing() {
const offset = CIRCUMFERENCE * (1 - PERCENT) // 未完成部分的长度
return (
<Svg width={180} height={180}>
<Defs>
<LinearGradient id="ringGrad" x1="0%" y1="0%" x2="100%" y2="100%">
<Stop offset="0%" stopColor="#4ECDC4" />
<Stop offset="100%" stopColor="#45B7D1" />
</LinearGradient>
</Defs>
{/* 背景环 */}
<Circle
cx={90} cy={90} r={RADIUS}
stroke="#F0F0F0" strokeWidth={12}
fill="none"
/>
{/* 进度环(渐变描边) */}
<Circle
cx={90} cy={90} r={RADIUS}
stroke="url(#ringGrad)" strokeWidth={12}
fill="none"
strokeLinecap="round"
// 旋转 -90 度让进度从顶部开始
transform="rotate(-90, 90, 90)"
strokeDasharray={CIRCUMFERENCE} // 虚线长度=周长
strokeDashoffset={offset} // 偏移=未完成部分
/>
{/* 中心文字 */}
<SvgText x={90} y={85} textAnchor="middle" fontSize={32} fontWeight="bold" fill="#333">
{CURRENT.toLocaleString()}
</SvgText>
<SvgText x={90} y={105} textAnchor="middle" fontSize={14} fill="#999">
/ {TARGET.toLocaleString()} 步
</SvgText>
</Svg>
)
}
strokeDasharray={CIRCUMFERENCE} + strokeDashoffset={offset} 是 SVG 进度环的经典技巧——虚线长度等于圆周长,偏移量等于未完成部分,调整 offset 就能控制进度。
第三步:心率折线图
根据心率数据动态生成 Path 的 d 属性:
import Svg, { Path, Line, Text as SvgText } from 'react-native-svg'
const HEART_RATES = [62, 65, 61, 58, 60, 63, 59] // 最近 7 天静息心率
const WIDTH = 300
const HEIGHT = 120
const PADDING = 20
function HeartRateChart() {
const min = Math.min(...HEART_RATES) - 5
const max = Math.max(...HEART_RATES) + 5
const range = max - min
// 生成 Path d 属性:M x0 y0 L x1 y1 L x2 y2 ...
const points = HEART_RATES.map((hr, i) => {
const x = PADDING + (i / (HEART_RATES.length - 1)) * (WIDTH - 2 * PADDING)
const y = HEIGHT - PADDING - ((hr - min) / range) * (HEIGHT - 2 * PADDING)
return `${i === 0 ? 'M' : 'L'} ${x} ${y}`
})
const linePath = points.join(' ')
// 填充区域路径(折线下方填充)
const fillPath = `${linePath} L ${WIDTH - PADDING} ${HEIGHT - PADDING} L ${PADDING} ${HEIGHT - PADDING} Z`
return (
<Svg width={WIDTH} height={HEIGHT}>
{/* 填充区域 */}
<Path d={fillPath} fill="#4ECDC4" opacity={0.1} />
{/* 折线 */}
<Path d={linePath} stroke="#4ECDC4" strokeWidth={2} fill="none" />
{/* 数据点 */}
{HEART_RATES.map((hr, i) => {
const x = PADDING + (i / (HEART_RATES.length - 1)) * (WIDTH - 2 * PADDING)
const y = HEIGHT - PADDING - ((hr - min) / range) * (HEIGHT - 2 * PADDING)
return <Circle key={i} cx={x} cy={y} r={3} fill="#4ECDC4" />
})}
</Svg>
)
}
全程用 Path/Circle 原生元素——矢量渲染,在高 DPI 鸿蒙屏幕上线条清晰锐利,不存在 PNG 放大模糊的问题。
第四步:卡路里仪表盘(渐变弧线)
半圆仪表盘,用渐变弧线填充:
function CalorieGauge({ value, max }: { value: number; max: number }) {
const percent = Math.min(value / max, 1)
const startAngle = 180 // 左侧
const endAngle = 180 + percent * 180 // 右侧
// 计算弧线路径
const cx = 100, cy = 100, r = 70
const startX = cx + r * Math.cos((startAngle * Math.PI) / 180)
const startY = cy + r * Math.sin((startAngle * Math.PI) / 180)
const endX = cx + r * Math.cos((endAngle * Math.PI) / 180)
const endY = cy + r * Math.sin((endAngle * Math.PI) / 180)
const arcPath = `M ${startX} ${startY} A ${r} ${r} 0 0 1 ${endX} ${endY}`
return (
<Svg width={200} height={120}>
<Defs>
<LinearGradient id="gaugeGrad" x1="0%" y1="0%" x2="100%" y2="0%">
<Stop offset="0%" stopColor="#FFB344" />
<Stop offset="100%" stopColor="#FF6B6B" />
</LinearGradient>
</Defs>
{/* 背景弧 */}
<Path d={`M ${cx - r} ${cy} A ${r} ${r} 0 0 1 ${cx + r} ${cy}`} stroke="#F0F0F0" strokeWidth={14} fill="none" strokeLinecap="round" />
{/* 进度弧(渐变) */}
<Path d={arcPath} stroke="url(#gaugeGrad)" strokeWidth={14} fill="none" strokeLinecap="round" />
<SvgText x={cx} y={cy - 10} textAnchor="middle" fontSize={28} fontWeight="bold" fill="#333">
{value}
</SvgText>
<SvgText x={cx} y={cy + 10} textAnchor="middle" fontSize={12} fill="#999">kcal</SvgText>
</Svg>
)
}
为什么"步道"选了 SVG?
| 需求 | PNG 图片 | 三方图表库 | react-native-svg |
|---|---|---|---|
| 动态进度环 | ❌ 不能变 | ✅ | ✅ |
| 折线图定制 | ❌ | ⚠️ 样式有限 | ✅ 全自定义 |
| 渐变填充 | ❌ | ⚠️ | ✅ |
| 矢量不失真 | ❌ 放大模糊 | ✅ | ✅ |
| 轻量 | ✅ | ❌ 重 | ✅ |
| 动画联动 | ❌ | ⚠️ | ✅ Reanimated |
总结
"步道"这个场景里,@react-native-ohos/react-native-svg 解决了三件事:
- 进度环——Circle + strokeDashoffset,渐变描边 + 动画驱动
- 折线图——动态生成 Path d 属性,矢量渲染不模糊
- 仪表盘——Arc Path + LinearGradient,渐变弧线精确到角度
如果你也需要在 RN 鸿蒙应用里画矢量图形——进度环、自定义图表、GPS 轨迹、仪表盘——react-native-svg 是最轻量又最灵活的选择。
更多推荐

所有评论(0)