@363045841yyt/klinechart 0.1.0 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +286 -48
- package/dist/App.vue.d.ts +1 -1
- package/dist/components/KLineChart.vue.d.ts +19 -6
- package/dist/components/KLineTooltip.vue.d.ts +15 -0
- package/dist/composables/useKLineInteraction.d.ts +50 -0
- package/dist/composables/useKLineRenderer.d.ts +49 -0
- package/dist/core/chart.d.ts +145 -0
- package/dist/core/controller/interaction.d.ts +50 -0
- package/dist/core/layout/pane.d.ts +72 -0
- package/dist/core/renderers/candle.d.ts +6 -0
- package/dist/core/renderers/crosshair.d.ts +13 -0
- package/dist/core/renderers/crosshairLabels.d.ts +17 -0
- package/dist/core/renderers/extremaMarkers.d.ts +8 -0
- package/dist/core/renderers/grid.d.ts +6 -0
- package/dist/core/renderers/gridLines.d.ts +7 -0
- package/dist/core/renderers/lastPrice.d.ts +5 -0
- package/dist/core/renderers/ma.d.ts +11 -0
- package/dist/core/renderers/maLegend.d.ts +9 -0
- package/dist/core/renderers/paneBorder.d.ts +14 -0
- package/dist/core/renderers/paneSeparator.d.ts +10 -0
- package/dist/core/renderers/paneTitle.d.ts +6 -0
- package/dist/core/renderers/timeAxis.d.ts +15 -0
- package/dist/core/renderers/yAxis.d.ts +11 -0
- package/dist/core/scale/price.d.ts +11 -0
- package/dist/core/scale/priceScale.d.ts +18 -0
- package/dist/core/viewport/viewport.d.ts +31 -0
- package/dist/index.cjs +1 -1
- package/dist/index.js +1036 -121
- package/dist/klinechart.css +1 -1
- package/dist/types/price.d.ts +14 -0
- package/dist/utils/dateFormat.d.ts +68 -0
- package/dist/utils/kLineDraw/MA.d.ts +8 -0
- package/dist/utils/kLineDraw/axis.d.ts +113 -0
- package/dist/utils/kLineDraw/grid.d.ts +30 -0
- package/dist/utils/{draw → kLineDraw}/kLine.d.ts +6 -8
- package/dist/utils/kLineDraw/pixelAlign.d.ts +48 -0
- package/dist/utils/kline/format.d.ts +14 -0
- package/dist/utils/kline/ma.d.ts +2 -0
- package/dist/utils/kline/viewport.d.ts +10 -0
- package/dist/utils/logger.d.ts +1 -1
- package/dist/utils/priceToY.d.ts +6 -0
- package/package.json +57 -55
- package/dist/utils/draw/MA.d.ts +0 -5
package/README.md
CHANGED
|
@@ -1,48 +1,286 @@
|
|
|
1
|
-
# kmap
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
##
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
##
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
1
|
+
# kmap - 金融图表绘制库
|
|
2
|
+
|
|
3
|
+
这是一个基于 Vue 3 和 Canvas 的金融图表绘制库,专注于提供高性能的 K 线图展示功能。该库支持横向滚动、移动平均线(MA)显示以及从多种数据源(包括 AKTools)获取金融数据。
|
|
4
|
+
|
|
5
|
+
## 功能特性
|
|
6
|
+
|
|
7
|
+
- 📊 **K 线图绘制**:使用 Canvas 实现高性能的 K 线图绘制
|
|
8
|
+
- 📈 **移动平均线**:支持 MA5、MA10、MA20 等多种移动平均线显示
|
|
9
|
+
- ↔️ **横向滚动**:支持大量历史数据的横向滚动浏览
|
|
10
|
+
- 🎨 **深色模式**:自动适配系统深色模式
|
|
11
|
+
- 📱 **响应式设计**:适配不同屏幕尺寸
|
|
12
|
+
- ⚡ **高性能**:使用 requestAnimationFrame 优化渲染性能
|
|
13
|
+
|
|
14
|
+
## 技术栈
|
|
15
|
+
|
|
16
|
+
- [Vue 3](https://vuejs.org/) - 渐进式 JavaScript 框架
|
|
17
|
+
- [Vite](https://vite.dev/) - 下一代前端构建工具
|
|
18
|
+
- [TypeScript](https://www.typescriptlang.org/) - JavaScript 类型检查
|
|
19
|
+
- [Canvas API](https://developer.mozilla.org/zh-CN/docs/Web/API/Canvas_API) - 图形绘制
|
|
20
|
+
- [AKTools](https://github.com/akfamily/aktools) - 开源金融数据接口库
|
|
21
|
+
|
|
22
|
+
## 项目结构
|
|
23
|
+
|
|
24
|
+
```
|
|
25
|
+
src/
|
|
26
|
+
├── api/ # API 接口定义
|
|
27
|
+
│ └── data/
|
|
28
|
+
│ └── kLine.ts # K 线数据接口
|
|
29
|
+
├── components/ # 组件
|
|
30
|
+
│ └── KLineChart.vue # K 线图主组件
|
|
31
|
+
├── types/ # 类型定义
|
|
32
|
+
│ ├── kLine.ts # K 线类型定义
|
|
33
|
+
│ └── price.ts # 价格类型定义
|
|
34
|
+
├── utils/ # 工具函数
|
|
35
|
+
│ ├── draw/ # 绘制工具
|
|
36
|
+
│ │ ├── kLine.ts # K 线绘制
|
|
37
|
+
│ │ └── MA.ts # 移动平均线绘制
|
|
38
|
+
│ ├── mock/ # 模拟数据生成
|
|
39
|
+
│ ├── logger.ts # 日志工具
|
|
40
|
+
│ └── priceToY.ts # 价格转 Y 坐标
|
|
41
|
+
├── stores/ # 状态管理 (Pinia)
|
|
42
|
+
└── assets/ # 静态资源
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## 数据接入
|
|
46
|
+
|
|
47
|
+
### AKTools 数据接入
|
|
48
|
+
|
|
49
|
+
AKTools 是一个开源的金融数据接口库,可以免费获取股票、期货、期权等金融产品的历史数据。
|
|
50
|
+
|
|
51
|
+
#### 安装 AKTools
|
|
52
|
+
|
|
53
|
+
使用 uv pip 安装:
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
# 安装 uv(如果尚未安装)
|
|
57
|
+
pip install uv
|
|
58
|
+
|
|
59
|
+
# 使用 uv 安装 AKTools
|
|
60
|
+
uv pip install aktools
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
或者直接使用 pip 安装:
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
pip install aktools
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
#### 启动 AKTools 数据服务
|
|
70
|
+
|
|
71
|
+
方法一:使用 uv 直接运行 AKTools
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
uv run python -m aktools
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
方法一(推荐):通过本项目脚本启动(会自动切换到上级目录的 `aktoolshttp/`)
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
pnpm aktools
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
#### 手机访问本机(开发模式)并调用 AKTools API(推荐)
|
|
84
|
+
|
|
85
|
+
如果你希望用手机浏览器访问本机正在运行的 `pnpm dev` 页面,同时前端还能调用本机的 AKTools API,推荐使用 **Vite 代理**(避免 CORS,且手机不需要直连 8080)。
|
|
86
|
+
|
|
87
|
+
本项目已在 `vite.config.ts` 配置:
|
|
88
|
+
|
|
89
|
+
- dev server 监听 `0.0.0.0`
|
|
90
|
+
- 代理转发:`/api` -> `http://127.0.0.1:8080`
|
|
91
|
+
|
|
92
|
+
启动步骤:
|
|
93
|
+
|
|
94
|
+
1. 启动 AKTools(本机 8080):
|
|
95
|
+
|
|
96
|
+
```bash
|
|
97
|
+
pnpm aktools
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
2. 启动前端 dev server(允许局域网访问):
|
|
101
|
+
|
|
102
|
+
```bash
|
|
103
|
+
pnpm dev:lan
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
3. 查找本机局域网 IP(例如 `192.168.1.23`),手机浏览器访问:
|
|
107
|
+
|
|
108
|
+
```
|
|
109
|
+
http://192.168.1.23:5173
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
> 说明:前端请求路径保持 `VITE_API_PATH=/api/public/stock_zh_a_hist`,浏览器请求会先到 5173,再由 Vite 代理到本机 8080,因此通常不会遇到跨域问题。
|
|
113
|
+
|
|
114
|
+
如果你之前在 `.env` 写死了 `VITE_API_BASE_URL=http://127.0.0.1:8080`,手机端会把 `127.0.0.1` 解析成“手机自己”,从而导致 API 连接失败。此时请把 `VITE_API_BASE_URL` 留空(或删除该行),让前端走相对路径并交给 Vite 代理。
|
|
115
|
+
|
|
116
|
+
方法二:创建自定义后端服务
|
|
117
|
+
除了使用 AKTools 自带的服务外,你也可以根据需要创建自定义的后端服务来处理数据。
|
|
118
|
+
|
|
119
|
+
#### 配置前端环境变量
|
|
120
|
+
|
|
121
|
+
在项目根目录创建 `.env` 文件:
|
|
122
|
+
|
|
123
|
+
```
|
|
124
|
+
VITE_API_BASE_URL=http://127.0.0.1:8080
|
|
125
|
+
VITE_API_PATH=/api/public/stock_zh_a_hist
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
然后在 [vite.config.ts](file:///d:/Code/kmap/kmap/vite.config.ts) 中确保环境变量被正确加载:
|
|
129
|
+
|
|
130
|
+
```ts
|
|
131
|
+
import { defineConfig } from 'vite'
|
|
132
|
+
import vue from '@vitejs/plugin-vue'
|
|
133
|
+
|
|
134
|
+
export default defineConfig({
|
|
135
|
+
plugins: [vue()],
|
|
136
|
+
define: {
|
|
137
|
+
'process.env': process.env,
|
|
138
|
+
},
|
|
139
|
+
})
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
### 数据格式
|
|
143
|
+
|
|
144
|
+
K 线数据需要包含以下字段:
|
|
145
|
+
|
|
146
|
+
```ts
|
|
147
|
+
interface KLineDailyDongCaiResponse {
|
|
148
|
+
日期: string // 日期
|
|
149
|
+
股票代码: string // 股票代码
|
|
150
|
+
开盘: number // 开盘价
|
|
151
|
+
收盘: number // 收盘价
|
|
152
|
+
最高: number // 最高价
|
|
153
|
+
最低: number // 最低价
|
|
154
|
+
成交量: number // 成交量
|
|
155
|
+
成交额: number // 成交额
|
|
156
|
+
振幅: number // 振幅
|
|
157
|
+
涨跌幅: number // 涨跌幅
|
|
158
|
+
涨跌额: number // 涨跌额
|
|
159
|
+
换手率: number // 换手率
|
|
160
|
+
}
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
## 使用方法
|
|
164
|
+
|
|
165
|
+
### 1. 安装依赖
|
|
166
|
+
|
|
167
|
+
```sh
|
|
168
|
+
pnpm install
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
### 2. 启动开发服务器
|
|
172
|
+
|
|
173
|
+
```sh
|
|
174
|
+
pnpm dev
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
### 3. 在组件中使用 K 线图
|
|
178
|
+
|
|
179
|
+
```vue
|
|
180
|
+
<template>
|
|
181
|
+
<KLineChart
|
|
182
|
+
:data="klineData"
|
|
183
|
+
:kWidth="10"
|
|
184
|
+
:kGap="2"
|
|
185
|
+
:yPaddingPx="60"
|
|
186
|
+
:showMA="{ ma5: true, ma10: true, ma20: true }"
|
|
187
|
+
:autoScrollToRight="true"
|
|
188
|
+
/>
|
|
189
|
+
</template>
|
|
190
|
+
|
|
191
|
+
<script setup lang="ts">
|
|
192
|
+
import { ref, onMounted } from 'vue'
|
|
193
|
+
import KLineChart from '@/components/KLineChart.vue'
|
|
194
|
+
import { getKlineDataDongCai } from '@/api/data/kLine'
|
|
195
|
+
import { toKLineData, type KLineData } from '@/types/price'
|
|
196
|
+
|
|
197
|
+
const klineData = ref<KLineData[]>([])
|
|
198
|
+
|
|
199
|
+
onMounted(async () => {
|
|
200
|
+
const raw = await getKlineDataDongCai({
|
|
201
|
+
symbol: '601360', // 三六零股票代码
|
|
202
|
+
period: 'daily',
|
|
203
|
+
start_date: '20250501',
|
|
204
|
+
end_date: '20251230',
|
|
205
|
+
adjust: 'qfq', // 前复权
|
|
206
|
+
})
|
|
207
|
+
klineData.value = toKLineData(raw) // 转换并排序数据
|
|
208
|
+
})
|
|
209
|
+
</script>
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
### 组件属性
|
|
213
|
+
|
|
214
|
+
| 属性 | 类型 | 默认值 | 说明 |
|
|
215
|
+
| ----------------- | ----------- | ------------------------------------- | ------------------------------ |
|
|
216
|
+
| data | KLineData[] | [] | K 线数据数组 |
|
|
217
|
+
| kWidth | number | 10 | K 线实体宽度 |
|
|
218
|
+
| kGap | number | 2 | K 线间距 |
|
|
219
|
+
| yPaddingPx | number | 60 | Y 轴上下留白像素 |
|
|
220
|
+
| showMA | MAFlags | { ma5: true, ma10: true, ma20: true } | 是否显示移动平均线 |
|
|
221
|
+
| autoScrollToRight | boolean | true | 数据更新后是否自动滚动到最右侧 |
|
|
222
|
+
|
|
223
|
+
## 性能优化
|
|
224
|
+
|
|
225
|
+
- 使用 `requestAnimationFrame` 优化渲染性能
|
|
226
|
+
- 对于滚动等高频事件,使用 passive 模式提升响应性能
|
|
227
|
+
- Canvas 绘制时使用设备像素比(devicePixelRatio)确保在高分屏上清晰显示
|
|
228
|
+
- 通过路径重置(beginPath)避免路径污染
|
|
229
|
+
|
|
230
|
+
## 环境要求
|
|
231
|
+
|
|
232
|
+
- Node.js: ^20.19.0 || >=22.12.0
|
|
233
|
+
- pnpm: 包管理器
|
|
234
|
+
- Python: 用于运行 AKTools 服务(可选)
|
|
235
|
+
- uv: Python 包与运行器(用于 `pnpm aktools`,可选但推荐)
|
|
236
|
+
|
|
237
|
+
## 构建与部署
|
|
238
|
+
|
|
239
|
+
### 生产环境构建
|
|
240
|
+
|
|
241
|
+
```sh
|
|
242
|
+
pnpm build
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
### 预览生产包
|
|
246
|
+
|
|
247
|
+
```sh
|
|
248
|
+
pnpm preview
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
## API 接口说明
|
|
252
|
+
|
|
253
|
+
### getKlineDataDongCai
|
|
254
|
+
|
|
255
|
+
获取 K 线数据的异步函数。
|
|
256
|
+
|
|
257
|
+
**参数:**
|
|
258
|
+
|
|
259
|
+
```ts
|
|
260
|
+
interface KLineDailyDongCaiRequest {
|
|
261
|
+
symbol: string // 股票代码
|
|
262
|
+
period: 'daily' | 'weekly' | 'monthly' // 周期
|
|
263
|
+
start_date: string // 开始日期,格式:YYYYMMDD
|
|
264
|
+
end_date: string // 结束日期,格式:YYYYMMDD
|
|
265
|
+
adjust?: 'qfq' | 'hfq' // 复权方式,qfq: 前复权, hfq: 后复权
|
|
266
|
+
timeout?: number // 超时时间(秒)
|
|
267
|
+
}
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
**返回值:**
|
|
271
|
+
`Promise<KLineDailyDongCaiResponse[]>` - K 线数据数组
|
|
272
|
+
|
|
273
|
+
## 贡献
|
|
274
|
+
|
|
275
|
+
欢迎提交 Issue 和 Pull Request 来改进这个项目。
|
|
276
|
+
|
|
277
|
+
## 许可证
|
|
278
|
+
|
|
279
|
+
本项目采用 MIT 许可证,详情请见 [LICENSE](./LICENSE) 文件。
|
|
280
|
+
|
|
281
|
+
## 相关链接
|
|
282
|
+
|
|
283
|
+
- [Vue.js 官方文档](https://vuejs.org/guide/introduction.html)
|
|
284
|
+
- [Vite 官方文档](https://vite.dev/guide/)
|
|
285
|
+
- [AKTools 官方文档](https://github.com/akfamily/aktools)
|
|
286
|
+
- [Canvas API MDN 文档](https://developer.mozilla.org/zh-CN/docs/Web/API/Canvas_API)
|
package/dist/App.vue.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
declare const _default: import('vue').DefineComponent<{}, {}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {}, string, import('vue').PublicProps, Readonly<{}> & Readonly<{}>, {}, {}, {}, {}, string, import('vue').ComponentProvideOptions, true, {},
|
|
1
|
+
declare const _default: import('vue').DefineComponent<{}, {}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {}, string, import('vue').PublicProps, Readonly<{}> & Readonly<{}>, {}, {}, {}, {}, string, import('vue').ComponentProvideOptions, true, {}, any>;
|
|
2
2
|
export default _default;
|
|
@@ -11,23 +11,36 @@ type __VLS_Props = {
|
|
|
11
11
|
yPaddingPx?: number;
|
|
12
12
|
showMA?: MAFlags;
|
|
13
13
|
autoScrollToRight?: boolean;
|
|
14
|
+
minKWidth?: number;
|
|
15
|
+
maxKWidth?: number;
|
|
16
|
+
/** 右侧价格轴宽度 */
|
|
17
|
+
rightAxisWidth?: number;
|
|
18
|
+
/** 底部时间轴高度 */
|
|
19
|
+
bottomAxisHeight?: number;
|
|
20
|
+
/** Pane 高度比例(主/副),默认 [0.85, 0.15] */
|
|
21
|
+
paneRatios?: [number, number];
|
|
14
22
|
};
|
|
15
|
-
/**
|
|
16
|
-
* rAF 节流:scroll/resize/data变化都用它触发
|
|
17
|
-
*/
|
|
18
23
|
declare function scheduleRender(): void;
|
|
19
24
|
declare function scrollToRight(): void;
|
|
20
25
|
declare const _default: import('vue').DefineComponent<__VLS_Props, {
|
|
21
26
|
scheduleRender: typeof scheduleRender;
|
|
22
27
|
scrollToRight: typeof scrollToRight;
|
|
23
28
|
}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {}, string, import('vue').PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {
|
|
29
|
+
yPaddingPx: number;
|
|
24
30
|
kWidth: number;
|
|
25
31
|
kGap: number;
|
|
26
|
-
yPaddingPx: number;
|
|
27
32
|
showMA: MAFlags;
|
|
33
|
+
rightAxisWidth: number;
|
|
34
|
+
bottomAxisHeight: number;
|
|
35
|
+
minKWidth: number;
|
|
36
|
+
maxKWidth: number;
|
|
28
37
|
autoScrollToRight: boolean;
|
|
38
|
+
paneRatios: [number, number];
|
|
29
39
|
}, {}, {}, {}, string, import('vue').ComponentProvideOptions, false, {
|
|
30
40
|
containerRef: HTMLDivElement;
|
|
31
|
-
|
|
32
|
-
|
|
41
|
+
canvasLayerRef: HTMLDivElement;
|
|
42
|
+
plotCanvasRef: HTMLCanvasElement;
|
|
43
|
+
yAxisCanvasRef: HTMLCanvasElement;
|
|
44
|
+
xAxisCanvasRef: HTMLCanvasElement;
|
|
45
|
+
}, any>;
|
|
33
46
|
export default _default;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { KLineData } from '../types/price';
|
|
2
|
+
type __VLS_Props = {
|
|
3
|
+
k: KLineData | null;
|
|
4
|
+
/** 当前k在 data 中的索引 */
|
|
5
|
+
index: number | null;
|
|
6
|
+
data: KLineData[];
|
|
7
|
+
pos: {
|
|
8
|
+
x: number;
|
|
9
|
+
y: number;
|
|
10
|
+
};
|
|
11
|
+
/** 把 tooltip 根元素回传给父组件(用于测量宽高) */
|
|
12
|
+
setEl?: (el: HTMLDivElement | null) => void;
|
|
13
|
+
};
|
|
14
|
+
declare const _default: import('vue').DefineComponent<__VLS_Props, {}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {}, string, import('vue').PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {}, {}, {}, {}, string, import('vue').ComponentProvideOptions, false, {}, any>;
|
|
15
|
+
export default _default;
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { ComputedRef, Ref } from 'vue';
|
|
2
|
+
import { KLineData } from '../types/price';
|
|
3
|
+
import { KLineViewport } from './useKLineRenderer';
|
|
4
|
+
export type TooltipSize = {
|
|
5
|
+
width: number;
|
|
6
|
+
height: number;
|
|
7
|
+
};
|
|
8
|
+
export type TooltipPos = {
|
|
9
|
+
x: number;
|
|
10
|
+
y: number;
|
|
11
|
+
};
|
|
12
|
+
export declare function useKLineInteraction(args: {
|
|
13
|
+
containerRef: Ref<HTMLDivElement | null>;
|
|
14
|
+
tooltipRef: Ref<HTMLDivElement | null>;
|
|
15
|
+
getData: () => KLineData[];
|
|
16
|
+
kWidth: Ref<number>;
|
|
17
|
+
kGap: Ref<number>;
|
|
18
|
+
minKWidth: number;
|
|
19
|
+
maxKWidth: number;
|
|
20
|
+
initialRatio: number;
|
|
21
|
+
rightAxisWidth: Ref<number>;
|
|
22
|
+
bottomAxisHeight: Ref<number>;
|
|
23
|
+
yPaddingPx: Ref<number>;
|
|
24
|
+
scheduleRender: () => void;
|
|
25
|
+
getViewport: () => KLineViewport | null;
|
|
26
|
+
crosshairPos?: Ref<{
|
|
27
|
+
x: number;
|
|
28
|
+
y: number;
|
|
29
|
+
} | null>;
|
|
30
|
+
crosshairIndex?: Ref<number | null>;
|
|
31
|
+
}): {
|
|
32
|
+
isDragging: Ref<boolean>;
|
|
33
|
+
hoveredIndex: Ref<number | null>;
|
|
34
|
+
hovered: ComputedRef<KLineData | null>;
|
|
35
|
+
crosshairPos: Ref<{
|
|
36
|
+
x: number;
|
|
37
|
+
y: number;
|
|
38
|
+
} | null>;
|
|
39
|
+
crosshairIndex: Ref<number | null>;
|
|
40
|
+
tooltipPos: Ref<TooltipPos>;
|
|
41
|
+
tooltipSize: Ref<TooltipSize>;
|
|
42
|
+
clearCrosshair: () => void;
|
|
43
|
+
clearHover: () => void;
|
|
44
|
+
onMouseDown: (e: MouseEvent) => void;
|
|
45
|
+
onMouseMove: (e: MouseEvent) => void;
|
|
46
|
+
onMouseUp: () => void;
|
|
47
|
+
onMouseLeave: () => void;
|
|
48
|
+
onScroll: () => void;
|
|
49
|
+
onWheel: (e: WheelEvent) => void;
|
|
50
|
+
};
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { Ref } from 'vue';
|
|
2
|
+
import { KLineData } from '../types/price';
|
|
3
|
+
import { PriceRange } from '../utils/kline/viewport';
|
|
4
|
+
export type MAFlags = {
|
|
5
|
+
ma5?: boolean;
|
|
6
|
+
ma10?: boolean;
|
|
7
|
+
ma20?: boolean;
|
|
8
|
+
};
|
|
9
|
+
export type CrosshairPos = {
|
|
10
|
+
x: number;
|
|
11
|
+
y: number;
|
|
12
|
+
} | null;
|
|
13
|
+
export type KLineOption = {
|
|
14
|
+
kWidth: number;
|
|
15
|
+
kGap: number;
|
|
16
|
+
yPaddingPx?: number;
|
|
17
|
+
};
|
|
18
|
+
export type KLineViewport = {
|
|
19
|
+
viewWidth: number;
|
|
20
|
+
viewHeight: number;
|
|
21
|
+
plotWidth: number;
|
|
22
|
+
plotHeight: number;
|
|
23
|
+
rightAxisWidth: number;
|
|
24
|
+
bottomAxisHeight: number;
|
|
25
|
+
scrollLeft: number;
|
|
26
|
+
start: number;
|
|
27
|
+
end: number;
|
|
28
|
+
priceRange: PriceRange;
|
|
29
|
+
dpr: number;
|
|
30
|
+
};
|
|
31
|
+
export declare function useKLineRenderer(args: {
|
|
32
|
+
plotCanvasRef: Ref<HTMLCanvasElement | null>;
|
|
33
|
+
yAxisCanvasRef: Ref<HTMLCanvasElement | null>;
|
|
34
|
+
xAxisCanvasRef: Ref<HTMLCanvasElement | null>;
|
|
35
|
+
canvasLayerRef: Ref<HTMLDivElement | null>;
|
|
36
|
+
containerRef: Ref<HTMLDivElement | null>;
|
|
37
|
+
getData: () => KLineData[];
|
|
38
|
+
getOption: () => KLineOption;
|
|
39
|
+
getShowMA: () => MAFlags;
|
|
40
|
+
rightAxisWidth: Ref<number>;
|
|
41
|
+
bottomAxisHeight: Ref<number>;
|
|
42
|
+
crosshairPos: Ref<CrosshairPos>;
|
|
43
|
+
crosshairIndex: Ref<number | null>;
|
|
44
|
+
}): {
|
|
45
|
+
scheduleRender: () => void;
|
|
46
|
+
render: () => void;
|
|
47
|
+
getLastViewport: () => KLineViewport | null;
|
|
48
|
+
destroy: () => void;
|
|
49
|
+
};
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
import { KLineData } from '../types/price';
|
|
2
|
+
import { Pane } from './layout/pane';
|
|
3
|
+
import { InteractionController } from './controller/interaction';
|
|
4
|
+
export type ChartDom = {
|
|
5
|
+
container: HTMLDivElement;
|
|
6
|
+
canvasLayer: HTMLDivElement;
|
|
7
|
+
plotCanvas: HTMLCanvasElement;
|
|
8
|
+
yAxisCanvas: HTMLCanvasElement;
|
|
9
|
+
xAxisCanvas: HTMLCanvasElement;
|
|
10
|
+
};
|
|
11
|
+
export type PaneSpec = {
|
|
12
|
+
id: string;
|
|
13
|
+
ratio: number;
|
|
14
|
+
};
|
|
15
|
+
export type ChartOptions = {
|
|
16
|
+
kWidth: number;
|
|
17
|
+
kGap: number;
|
|
18
|
+
yPaddingPx: number;
|
|
19
|
+
rightAxisWidth: number;
|
|
20
|
+
bottomAxisHeight: number;
|
|
21
|
+
minKWidth: number;
|
|
22
|
+
maxKWidth: number;
|
|
23
|
+
panes: PaneSpec[];
|
|
24
|
+
/** pane 之间的真实分隔空隙(逻辑像素) */
|
|
25
|
+
paneGap?: number;
|
|
26
|
+
};
|
|
27
|
+
export type Viewport = {
|
|
28
|
+
viewWidth: number;
|
|
29
|
+
viewHeight: number;
|
|
30
|
+
plotWidth: number;
|
|
31
|
+
plotHeight: number;
|
|
32
|
+
scrollLeft: number;
|
|
33
|
+
dpr: number;
|
|
34
|
+
};
|
|
35
|
+
export declare class Chart {
|
|
36
|
+
private dom;
|
|
37
|
+
private opt;
|
|
38
|
+
private data;
|
|
39
|
+
private raf;
|
|
40
|
+
private viewport;
|
|
41
|
+
private panes;
|
|
42
|
+
readonly interaction: InteractionController;
|
|
43
|
+
private onZoomChange?;
|
|
44
|
+
/**
|
|
45
|
+
* 注册缩放回调。
|
|
46
|
+
*
|
|
47
|
+
* 为什么需要它:
|
|
48
|
+
* - `zoomAt()` 会更新 core 内部的 `kWidth/kGap`,但项目的横向滚动范围来自 Vue 层 `.scroll-content` 的宽度。
|
|
49
|
+
* - Vue 需要先用新的 kWidth/kGap 重新计算内容宽度(触发 DOM 更新 scrollWidth),
|
|
50
|
+
* 再设置正确的 scrollLeft,否则会出现“放大后无法拖到最右侧”的问题。
|
|
51
|
+
*
|
|
52
|
+
* @param cb (newKWidth, newKGap, targetScrollLeft) => void
|
|
53
|
+
*/
|
|
54
|
+
setOnZoomChange(cb: (kWidth: number, kGap: number, targetScrollLeft: number) => void): void;
|
|
55
|
+
/**
|
|
56
|
+
* 创建图表实例。
|
|
57
|
+
*
|
|
58
|
+
* @param dom 由 Vue 组件传入的 DOM 句柄(container/canvasLayer/三层 canvas)
|
|
59
|
+
* @param opt 初始配置(kWidth/kGap、轴尺寸、pane 配置等)
|
|
60
|
+
*/
|
|
61
|
+
constructor(dom: ChartDom, opt: ChartOptions);
|
|
62
|
+
/** 获取所有 pane(包含布局信息与 renderers 列表) */
|
|
63
|
+
getPanes(): Pane[];
|
|
64
|
+
/**
|
|
65
|
+
* 设置某个 pane 的渲染器链(按顺序执行)。
|
|
66
|
+
*
|
|
67
|
+
* @param paneId pane 标识(例如 'main'/'sub')
|
|
68
|
+
* @param renderers 渲染器数组;会清空并替换原有列表(保持引用稳定)
|
|
69
|
+
*/
|
|
70
|
+
setPaneRenderers(paneId: string, renderers: Pane['renderers']): void;
|
|
71
|
+
/** 获取 ChartDom(供 InteractionController 等使用) */
|
|
72
|
+
getDom(): ChartDom;
|
|
73
|
+
/** 获取当前 ChartOptions(注意:返回的是内部当前快照) */
|
|
74
|
+
getOption(): ChartOptions;
|
|
75
|
+
/**
|
|
76
|
+
* 更新配置并触发布局/重绘。
|
|
77
|
+
* - 更新 panes 时会重建 Pane 实例并重新布局。
|
|
78
|
+
*/
|
|
79
|
+
updateOptions(partial: Partial<ChartOptions>): void;
|
|
80
|
+
/**
|
|
81
|
+
* 更新数据并请求重绘。
|
|
82
|
+
* @param data K 线数据数组(null/undefined 会被视为 [])
|
|
83
|
+
*/
|
|
84
|
+
updateData(data: KLineData[]): void;
|
|
85
|
+
/** 获取当前数据源(给 renderers/interaction 使用) */
|
|
86
|
+
getData(): KLineData[];
|
|
87
|
+
/**
|
|
88
|
+
* 内容总宽度(用于外部 scroll-content 撑开 scrollWidth)
|
|
89
|
+
* 规则:kGap + n*(kWidth+kGap) + rightAxisWidth
|
|
90
|
+
*/
|
|
91
|
+
getContentWidth(): number;
|
|
92
|
+
/**
|
|
93
|
+
* 容器尺寸变化时调用:
|
|
94
|
+
* - 重新计算 viewport(DPR/plotWidth/plotHeight)
|
|
95
|
+
* - 重新计算 pane 布局
|
|
96
|
+
* - 请求重绘
|
|
97
|
+
*/
|
|
98
|
+
resize(): void;
|
|
99
|
+
/**
|
|
100
|
+
* 请求下一帧重绘(RAF 合并)。
|
|
101
|
+
* 多次调用只会触发一次 draw,避免频繁重绘。
|
|
102
|
+
*/
|
|
103
|
+
scheduleDraw(): void;
|
|
104
|
+
/**
|
|
105
|
+
* 绘制一帧:
|
|
106
|
+
* 1) computeViewport(设置 canvas 尺寸)
|
|
107
|
+
* 2) 计算可视区 range(start/end)
|
|
108
|
+
* 3) 对每个 pane:更新 priceRange -> 执行 renderers(plotCanvas)-> 画 yAxis(yAxisCanvas)
|
|
109
|
+
* 4) 画 xAxis(xAxisCanvas)
|
|
110
|
+
* 5) 画 overlay(十字线、图例、边框等)
|
|
111
|
+
*/
|
|
112
|
+
draw(): void;
|
|
113
|
+
/**
|
|
114
|
+
* 在给定 mouseX(相对 container 左侧)处做缩放。
|
|
115
|
+
*
|
|
116
|
+
* 设计目标:缩放后保持 mouseX 指向的“数据索引”尽量稳定(以该点为中心缩放)。
|
|
117
|
+
*
|
|
118
|
+
* @param mouseX 鼠标相对 container 的 x(逻辑像素)
|
|
119
|
+
* @param scrollLeft 当前 container.scrollLeft
|
|
120
|
+
* @param deltaY WheelEvent.deltaY(>0 缩小,<0 放大)
|
|
121
|
+
*/
|
|
122
|
+
zoomAt(mouseX: number, scrollLeft: number, deltaY: number): void;
|
|
123
|
+
/**
|
|
124
|
+
* 销毁 chart:
|
|
125
|
+
* - 取消 RAF
|
|
126
|
+
* - 释放引用,避免内存泄漏
|
|
127
|
+
*/
|
|
128
|
+
destroy(): void;
|
|
129
|
+
/** 根据 opt.panes 重建 Pane 实例列表 */
|
|
130
|
+
private initPanes;
|
|
131
|
+
/**
|
|
132
|
+
* 计算每个 pane 的布局(top/height)。
|
|
133
|
+
* - 按 ratio 分配高度
|
|
134
|
+
* - 支持 paneGap 形成真实留白
|
|
135
|
+
*/
|
|
136
|
+
private layoutPanes;
|
|
137
|
+
/**
|
|
138
|
+
* 计算并应用 viewport:
|
|
139
|
+
* - 读取 container 尺寸
|
|
140
|
+
* - 计算 plot 区域尺寸(扣掉右轴/底轴)
|
|
141
|
+
* - 处理 DPR 以及最大像素限制(避免超大 canvas)
|
|
142
|
+
* - 设置 canvasLayer/三个 canvas 的 style 尺寸与实际像素尺寸
|
|
143
|
+
*/
|
|
144
|
+
private computeViewport;
|
|
145
|
+
}
|