HarmonyOS ArkUI Canvas 实战:从零绘制金融级价格走势图 ## 前言 随着 HarmonyOS 生态的成熟越来越多的开发者开始在 ArkUI 中构建数据可视化页面。虽然官方提供了 Canvas 组件但关于如何在 ArkUI 中高效绘制金融图表的资料还比较少。 本文从一个真实的黄金分析工具出发逐步拆解 - Canvas 在 ArkUI 中的正确用法 - 数据坐标系映射 - 网格线、折线、渐变填充的绘制 - 防坑点宽高获取、font 格式、性能 --- ## 一、基础准备 ### 1.1 Canvas 组件声明 在 ArkUI 中Canvas 需要一个 CanvasRenderingContext2D 上下文 ets private settings: RenderingContextSettings new RenderingContextSettings(true); private context: CanvasRenderingContext2D new CanvasRenderingContext2D(this.settings); RenderingContextSettings(true) 启用抗锯齿对图表渲染质量影响很大。 布局中使用 ets Canvas(this.context) .width(100%) .height(260) .borderRadius(8) .onReady(() { this.drawChart(); }) 这里有两件事值得说 1. **Canvas 的宽高**width(100%) 是逻辑像素vpCanvas 内部 context.width / context.height 拿到的是物理像素已自动乘以 vp 转换onReady 回调中一定能正确获取。 2. **为什么不用 onAreaChange**onAreaChange 会频繁触发而 Canvas 尺寸在首次布局后就固定了。onReady 在 Canvas 准备好后只触发一次最适合画图。 ### 1.2 上下文的生命周期 上下文只需创建一次之后的绘制都在 onReady 或按钮回调中通过 this.context 操作。每次绘制前先 clearRect 清空再重绘即可。 --- ## 二、核心数据坐标系映射 金融走势图本质上是在二维平面上将**数据点 (index, price)** 映射到**像素坐标 (x, y)**。 ### 2.1 计算绘制区域 ets const p { left: 44, right: 12, top: 20, bottom: 28 }; const cw w - p.left - p.right; // 图表实际宽度 const ch h - p.top - p.bottom; // 图表实际高度 左右预留空间给价格标签和日期标签上下预留空间给价格文字和 padding。 ### 2.2 数据值 - 像素坐标 ets const toX (i: number): number p.left (i / (n - 1)) * cw; const toY (v: number): number p.top (1 - (v - yMin) / yRng) * ch; 核心公式(value - min) / range 得到 0~1 归一化再用 1 - 归一化值 翻转 Y 轴屏幕原点在左上最后乘以图表高度 padding。 **关键细节**yMin 和 yMax 不要直接从数据取 min/max否则折线会紧贴图表边缘。加一个 10%~15% 的 padding ets const pad (mx - mn) * 0.12; const yMin mn - pad; const yMax mx pad; --- ## 三、分步绘制 ### 3.1 背景和网格 ets ctx.fillStyle #0d1526; ctx.fillRect(0, 0, w, h); ctx.strokeStyle #1e2d4a; ctx.lineWidth 0.5; const gridLines 4; for (let i 0; i gridLines; i) { const y p.top (i / gridLines) * ch; ctx.beginPath(); ctx.moveTo(p.left, y); ctx.lineTo(w - p.right, y); ctx.stroke(); } 画 4 条水平网格线并在每条线左侧标注对应价格fillText 时注意 textBaseline middle 让文字垂直居中。 ### 3.2 价格折线 ets ctx.beginPath(); ctx.moveTo(toX(0), toY(data[0])); for (let i 1; i n; i) { ctx.lineTo(toX(i), toY(data[i])); } ctx.strokeStyle #FFD700; ctx.lineWidth 2.5; ctx.lineJoin round; ctx.stroke(); **注意**lineJoin round 让折线的拐角圆滑视觉效果更好。 ### 3.3 渐变填充 ets const gradient ctx.createLinearGradient(0, p.top, 0, p.top ch); gradient.addColorStop(0, rgba(255, 215, 0, 0.25)); gradient.addColorStop(1, rgba(255, 215, 0, 0.01)); ctx.lineTo(toX(n - 1), p.top ch); ctx.lineTo(toX(0), p.top ch); ctx.closePath(); ctx.fillStyle gradient; ctx.fill(); 先画折线到底部形成闭合路径再用渐变填充做出从金价线到底部渐变过渡的效果也是金融图表常见的视觉风格。 ### 3.4 最后一个数据点高亮 在最新的数据点画一个小圆点 价格标签让用户一眼看到当前值 ets ctx.beginPath(); ctx.arc(lx, ly, 5, 0, Math.PI * 2); ctx.fillStyle #FFD700; ctx.fill(); ctx.strokeStyle #fff; ctx.lineWidth 1.5; ctx.stroke(); 白色描边让金色圆点在背景下更醒目。 --- ## 四、常见问题 ### 4.1 获取 Canvas 宽高 很多新手在 onReady 之前尝试 this.context.width拿到的是 0。**必须在 onReady 回调里获取或触发绘制。** 如果需要在其他地方使用 Canvas 尺寸可以在 onReady 中存储 ets .onReady(() { this.canvasWidth this.context.width; this.canvasHeight this.context.height; this.drawChart(); }) ### 4.2 font 设置 ArkUI 的 Canvas font 属性格式与 Web 一致bold 12px sans-serif。但**不支持** fontSize 这样的属性名。常见错误 ets // ❌ 错误 ctx.fontSize 12; // ✅ 正确 ctx.font 12px sans-serif; ctx.font bold 14px sans-serif; ### 4.3 性能 单次 drawChart 包含约 30 个绘图指令5 条网格线 label 折线 填充 高亮点在 HarmonyOS 设备上渲染耗时 5ms不会导致掉帧。 如果数据量极大数千个点可以用 Path2D 优化但一般金融日线/时线图完全不需要担心。 --- ## 五、完整集成效果 本文代码来自一个完整的黄金分析工具GoldAnalysis包含 - ✅ 金价卡片当前价、涨跌幅、开盘/最高/最低 - ✅ Canvas 走势图20 日价格 渐变填充 - ✅ 技术指标RSI、MA5、MA20、MA60 - ✅ AI 生成的文案分析 - ✅ 买卖信号 阻力位/支撑位 首页导航用户从 Index 页点击黄金分析按钮进入。 --- ## 结语 ArkUI 的 Canvas API 与 Web 标准高度一致有 Web 经验的开发者几乎零成本上手。金融图表绘制的核心在于数据坐标系映射理解这一点后无论折线图、K 线图还是分时图都是类似的原理。 希望这篇文章对你在 HarmonyOS 上做图表可视化有帮助。