

实例:股票 K 线图(K-Line Chart)|技术:全量代码解读、操作剧本、金融图表模式抽象
一、本篇定位
前两篇分别讲了绘制层与交互层,本篇完成三件事:
- 全量源码逐段解读------KLineChartPage 从头到尾过一遍;
- 运行效果演示------从触摸查看 OHLC 到换数据的完整操作;
- 模式抽象------"金融图表"通用模板,附两个改写场景。
二、完整源码:KLineChartPage.ets
typescript
/**
* 股票 K 线图 ------ 自定义 Canvas(CanvasRenderingContext2D)应用 8
* 对应博客:Canvas_2d/8_股票K线图/8-1 ~ 8-3
*
* 功能:
* 1. 蜡烛图:K 线(开/高/低/收)绘制,涨红跌绿
* 2. 均线:MA5 / MA10 / MA20 三条折线
* 3. 十字光标:触摸显示对应 K 线的 OHLC 数据
* 4. 数据生成:模拟随机游走生成 K 线数据
*/
import { hilog } from '@kit.PerformanceAnalysisKit';
const TAG = 'KLineChart';
/** K 线数据 */
interface KLine {
open: number;
high: number;
low: number;
close: number;
}
@Entry
@Component
struct KLineChartPage {
private settings: RenderingContextSettings = new RenderingContextSettings(true);
private ctx: CanvasRenderingContext2D = new CanvasRenderingContext2D(this.settings);
private canvasW: number = 0;
private canvasH: number = 0;
private data: KLine[] = [];
private maxPrice: number = 0;
private minPrice: number = 0;
private candleW: number = 8;
private gap: number = 4;
private padL: number = 44;
private padR: number = 12;
private padT: number = 16;
private padB: number = 8;
@State touchIndex: number = -1;
@State infoText: string = '触摸图表查看 OHLC 数据';
aboutToAppear(): void {
this.generateData();
}
// generateData / priceToY / indexToX / redraw / drawGrid
// drawCandles / drawMAs / drawMA / drawCrosshair / onTouchChart / build
// (见上两篇逐段解读)
}
逐段解读总表
| 代码段 | 职责 | 关键决策 |
|---|---|---|
| KLine 接口 | OHLC 四价 | 蜡烛数据模型 |
| 布局常量 | candleW/gap/pad* | 图表几何参数 |
| generateData | 随机游走 60 根 | 模拟行情 |
| priceToY | 价格→像素 Y | 归一化+翻转+平移 |
| indexToX | 索引→像素 X | 线性排布 |
| redraw | 分层重绘 | 层级顺序 |
| drawGrid | 网格 + 价格标签 | 刻度可读 |
| drawCandles | 蜡烛绘制 | 影线+实体 |
| drawMA | 均线滑动平均 | 三线复用 |
| drawCrosshair | 光标 + 浮层 | 交互反馈 |
| onTouchChart | 命中检测 | 坐标反算 |
三、接入工程三步
Step 1:放文件
KLineChartPage.ets→entry/src/main/ets/pages/canvas_2d/
Step 2:注册路由(main_pages.json)
json
"pages/canvas_2d/KLineChartPage",
Step 3:首页入口(Index.ets)
typescript
Button('📈 36 股票 K 线图')
.fontSize(14)
.width('80%')
.onClick(() => {
this.getUIContext().getRouter().pushUrl({ url: 'pages/canvas_2d/KLineChartPage' });
})
四、运行效果演示(操作剧本)
场景一:首次打开
| 步骤 | 界面表现 | 数据来源 |
|---|---|---|
| 1 | 标题栏:"📈 股票 K 线图",提示"触摸图表查看 OHLC 数据" | --- |
| 2 | 画布:白底,浅灰网格 5 等分,左侧价格标签(约 88~112 元),60 根红绿蜡烛 | generateData + drawCandles |
| 3 | 蜡烛上方三条均线:蓝 MA5 / 橙 MA10 / 紫 MA20 平滑蜿蜒 | drawMA |
| 4 | 图例:"━MA5 ━MA10 ━MA20 红涨 绿跌" | --- |
场景二:触摸查看 OHLC
- 触摸画布中部 → 十字光标虚线出现在最近的 K 线位置;
- 左上角深色浮层显示:开/收/高/低/涨跌 + 右侧红绿涨跌色条;
- 标题栏同步显示"第 32 根 · 开 98.5 收 101.2 高 102.0 低 97.8";
- 触摸另一处 → 光标移动、数据更新。
场景三:涨跌观察
触摸红色蜡烛(涨)→ 浮层涨跌色条红、涨跌 +2.7;触摸绿色蜡烛(跌)→ 色条绿、涨跌 -1.3。颜色与数值双重确认涨跌。
场景四:换数据
点"换数据"→ 生成新随机行情,光标消失、提示重置。连点几次看不同"行情"(有的上涨趋势、有的震荡)。
五、模式抽象:金融图表通用模板
5.1 模板骨架
┌─────────────────────────────────────────────┐
│ 数据层 │
│ KLine 数组(OHLC) / 价格范围 min~max │
└──────────────────┬──────────────────────────┘
│
┌──────────────────▼──────────────────────────┐
│ 坐标变换层 │
│ valueToY:归一化 → 翻转 → 平移 │
│ indexToX:索引 × 占宽 + 偏移 │
│ (逆运算做命中检测) │
└──────────────────┬──────────────────────────┘
│
┌──────────────────▼──────────────────────────┐
│ 绘制层 │
│ 网格 → 蜡烛/柱 → 指标线 → 光标 │
│ (分层顺序 = 视觉层级) │
└──────────────────┬──────────────────────────┘
│
┌──────────────────▼──────────────────────────┐
│ 交互层 │
│ 触摸 → 坐标反算 → 命中 → 光标 + 浮层 │
└─────────────────────────────────────────────┘
5.2 六个约定
- 数据与绘制解耦:KLine 数组只管数据,绘制只读它;
- 坐标变换成对:valueToY 与 yToValue、indexToX 与 xToIndex 互为逆运算;
- 归一化是核心:任何数据域 → 0,1 → 像素域;
- 绘制分层:网格垫底、数据中、交互浮层最上;
- 状态复位:setLineDash 用完复位、换数据清交互状态;
- 参数配置化:candleW/gap/pad 是常量,调参改观感。
5.3 两个改写场景
场景 A:分时图
| K 线实例 | 分时改写 |
|---|---|
| KLine 数组 | 时间点数组(time, price) |
| 蜡烛绘制 | 折线绘制(当日价格 + 均价线) |
| 均线 | 均价线(成交量加权) |
| Y 轴 | 昨收价为中心 ± 百分比 |
场景 B:MACD 指标图
| K 线实例 | MACD 改写 |
|---|---|
| 主图蜡烛 | 保留(K 线 + 均线) |
| 副图 | DIF/DEA 折线 + MACD 柱(红正绿负) |
| 布局 | 双绘图区(价格区 + 指标区) |
场景 C:柱状成交量图------K 线下方成交量柱(红涨绿跌同色),模板复用。
三个场景的共同点:"数据 → 坐标变换 → 分层绘制 → 交互命中"四层结构。掌握 36,任何金融图表(K 线、分时、指标)都能搭建。
六、三篇内容总对照
| 篇章 | 核心知识点 | 一句话总结 |
|---|---|---|
| 36-1 坐标变换与蜡烛图绘制 | 归一化映射、蜡烛、均线 | 数据域↔像素域映射 |
| 36-2 十字光标与触摸交互 | 命中检测、虚线、浮层 | 坐标往返变换 |
| 36-3 完整代码与运行效果 | 全量源码、操作剧本、金融模板 | 四层结构,改数据即新图 |
七、FAQ(三篇汇总补遗)
Q1:36 和 23 数据图表有什么区别?
A:23 是基础图表(柱/折线/饼,静态 + 简单动画);36 是金融图表(蜡烛 + 均线 + 十字光标交互)。36 把坐标变换和触摸命中提升为第一公民------这是专业图表的门槛。
Q2:价格范围为什么用 high 最大 / low 最小?
A:Y 轴要覆盖全部价格波动(影线顶端到低端)。若用开收范围,影线会超出绘图区。范围 = 数据的极值域。
Q3:均线计算 O(n²) 会卡吗?
A:60 根 × MA20 = 1200 次加法,毫秒级。数据上万时可滑动增量(sum += new - old)降为 O(n)。教学直算,大数据优化。
Q4:命中检测为什么用 round 而不用 floor?
A:round 就近取整------触摸在两个 K 线中间时选"较近"的一根,更符合直觉。floor 会永远选左侧,边缘处有偏差。
Q5:真机触摸的 t.x 是相对什么?
A:TouchEvent.touches0.x/y 是相对组件 的坐标(组件内左上角为原点),所以直接用于 indexToX 逆运算(减去 padL)。组件相对坐标正好匹配画布坐标系。
Q6:能加多周期切换(日K/周K)吗?
A:能------数据源按周期聚合(日线数据 → 周线聚合),绘制层不变。周期是数据预处理,不是绘制问题。
八、结语
三篇文章完整走完了 K 线图的"绘制 → 交互 → 复用"闭环:
- 1 教你坐标变换:归一化映射、蜡烛绘制、均线计算;
- 2 教你触摸交互:命中检测、十字光标、数据浮层;
- 3 教你模式复用:四层结构模板,改数据即覆盖分时、MACD、成交量。
K 线图是 Canvas 系列"数据可视化"的最高峰------它把数据图表的基础能力升级为专业金融图表,坐标变换 + 交互命中的组合是数据可视化的通用钥匙。