@363045841yyt/klinechart 0.1.1 → 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 -245
- 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
|
@@ -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
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { Chart } from '../chart';
|
|
2
|
+
/**
|
|
3
|
+
* 交互控制器:只依赖 Chart 公共 API,不依赖 Vue。
|
|
4
|
+
* 先落地:拖拽滚动 / wheel 缩放。
|
|
5
|
+
* hover/crosshair 后续按同样方式接入。
|
|
6
|
+
*/
|
|
7
|
+
export declare class InteractionController {
|
|
8
|
+
private chart;
|
|
9
|
+
private isDragging;
|
|
10
|
+
private dragStartX;
|
|
11
|
+
private scrollStartX;
|
|
12
|
+
private isTouchSession;
|
|
13
|
+
crosshairPos: {
|
|
14
|
+
x: number;
|
|
15
|
+
y: number;
|
|
16
|
+
} | null;
|
|
17
|
+
crosshairIndex: number | null;
|
|
18
|
+
hoveredIndex: number | null;
|
|
19
|
+
activePaneId: string | null;
|
|
20
|
+
tooltipPos: {
|
|
21
|
+
x: number;
|
|
22
|
+
y: number;
|
|
23
|
+
};
|
|
24
|
+
tooltipSize: {
|
|
25
|
+
width: number;
|
|
26
|
+
height: number;
|
|
27
|
+
};
|
|
28
|
+
constructor(chart: Chart);
|
|
29
|
+
/**
|
|
30
|
+
* Pointer 事件:用于移动端/触屏设备。
|
|
31
|
+
* 设计目标:手指一接触屏幕(pointerdown)就立刻更新 crosshair。
|
|
32
|
+
*/
|
|
33
|
+
onPointerDown(e: PointerEvent): void;
|
|
34
|
+
onPointerMove(e: PointerEvent): void;
|
|
35
|
+
onPointerUp(e: PointerEvent): void;
|
|
36
|
+
onPointerLeave(e: PointerEvent): void;
|
|
37
|
+
onMouseDown(e: MouseEvent): void;
|
|
38
|
+
onMouseMove(e: MouseEvent): void;
|
|
39
|
+
onMouseUp(): void;
|
|
40
|
+
onMouseLeave(): void;
|
|
41
|
+
onScroll(): void;
|
|
42
|
+
onWheel(e: WheelEvent): void;
|
|
43
|
+
setTooltipSize(size: {
|
|
44
|
+
width: number;
|
|
45
|
+
height: number;
|
|
46
|
+
}): void;
|
|
47
|
+
private clearHover;
|
|
48
|
+
private updateHover;
|
|
49
|
+
private updateHoverFromPoint;
|
|
50
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { KLineData } from '../../types/price';
|
|
2
|
+
import { PriceRange } from '../scale/price';
|
|
3
|
+
import { PriceScale } from '../scale/priceScale';
|
|
4
|
+
export type VisibleRange = {
|
|
5
|
+
start: number;
|
|
6
|
+
end: number;
|
|
7
|
+
};
|
|
8
|
+
/**
|
|
9
|
+
* Pane 级渲染器接口:在单个 pane 的坐标系中绘制内容。
|
|
10
|
+
*
|
|
11
|
+
* 约定:
|
|
12
|
+
* - 调用前 Chart 已经对 `ctx` 做了 `translate(0, pane.top)`,因此 y=0 对应 pane 顶部。
|
|
13
|
+
* - 如需随滚动的 world 坐标,需要 renderer 内部执行 `ctx.translate(-scrollLeft, 0)`。
|
|
14
|
+
*/
|
|
15
|
+
export interface PaneRenderer {
|
|
16
|
+
draw(args: {
|
|
17
|
+
ctx: CanvasRenderingContext2D;
|
|
18
|
+
pane: Pane;
|
|
19
|
+
data: KLineData[];
|
|
20
|
+
range: VisibleRange;
|
|
21
|
+
scrollLeft: number;
|
|
22
|
+
kWidth: number;
|
|
23
|
+
kGap: number;
|
|
24
|
+
dpr: number;
|
|
25
|
+
}): void;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Pane:代表一个“窗口区域”(主图 / 副图)。
|
|
29
|
+
*
|
|
30
|
+
* 负责:
|
|
31
|
+
* - 记录自身布局(top/height)
|
|
32
|
+
* - 维护可视价格范围(priceRange)
|
|
33
|
+
* - 拥有独立的 Y 轴缩放器(PriceScale)
|
|
34
|
+
* - 保存一组渲染器(renderers),在 Chart.draw 中按顺序执行。
|
|
35
|
+
*/
|
|
36
|
+
export declare class Pane {
|
|
37
|
+
readonly id: string;
|
|
38
|
+
top: number;
|
|
39
|
+
height: number;
|
|
40
|
+
/** 当前 pane 的可视价格范围(用于右侧轴、以及渲染器内部) */
|
|
41
|
+
priceRange: PriceRange;
|
|
42
|
+
/** pane 独立 Y 轴 */
|
|
43
|
+
readonly yAxis: PriceScale;
|
|
44
|
+
/** 该 pane 的渲染器列表 */
|
|
45
|
+
readonly renderers: PaneRenderer[];
|
|
46
|
+
/**
|
|
47
|
+
* @param id pane 标识符(例如 'main'、'sub'),用于在 Chart/Interaction 中识别 pane。
|
|
48
|
+
*/
|
|
49
|
+
constructor(id: string);
|
|
50
|
+
/**
|
|
51
|
+
* 设置 pane 的垂直布局。
|
|
52
|
+
*
|
|
53
|
+
* @param top 相对 plotCanvas 顶部的偏移(逻辑像素)
|
|
54
|
+
* @param height pane 高度(逻辑像素)
|
|
55
|
+
*/
|
|
56
|
+
setLayout(top: number, height: number): void;
|
|
57
|
+
/**
|
|
58
|
+
* 设置 Y 轴上下 padding(影响 priceToY 映射的上下留白)。
|
|
59
|
+
*/
|
|
60
|
+
setPadding(top: number, bottom: number): void;
|
|
61
|
+
/**
|
|
62
|
+
* 注册一个 pane 级渲染器。
|
|
63
|
+
*/
|
|
64
|
+
addRenderer(r: PaneRenderer): void;
|
|
65
|
+
/**
|
|
66
|
+
* 根据当前可见索引区间更新 priceRange,并同步到 yAxis。
|
|
67
|
+
*
|
|
68
|
+
* @param data 全量 K 线数据
|
|
69
|
+
* @param range 当前视口可见的索引范围(由 getVisibleRange 计算)
|
|
70
|
+
*/
|
|
71
|
+
updateRange(data: KLineData[], range: VisibleRange): void;
|
|
72
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 十字线渲染(屏幕坐标系):
|
|
3
|
+
* - ctx 处于 plotCanvas 的屏幕坐标(不带 translate(-scrollLeft,0))
|
|
4
|
+
* - x/y 是相对 plot 区域左上角的坐标(即 container 内坐标)
|
|
5
|
+
*/
|
|
6
|
+
export declare function drawCrosshair(args: {
|
|
7
|
+
ctx: CanvasRenderingContext2D;
|
|
8
|
+
plotWidth: number;
|
|
9
|
+
plotHeight: number;
|
|
10
|
+
dpr: number;
|
|
11
|
+
x: number;
|
|
12
|
+
y: number;
|
|
13
|
+
}): void;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { Pane } from '../layout/pane';
|
|
2
|
+
import { KLineData } from '../../types/price';
|
|
3
|
+
export declare function drawCrosshairPriceLabelForPane(args: {
|
|
4
|
+
ctx: CanvasRenderingContext2D;
|
|
5
|
+
pane: Pane;
|
|
6
|
+
axisWidth: number;
|
|
7
|
+
dpr: number;
|
|
8
|
+
crosshairY: number;
|
|
9
|
+
yPaddingPx: number;
|
|
10
|
+
}): void;
|
|
11
|
+
export declare function drawCrosshairTimeLabelGlobal(args: {
|
|
12
|
+
ctx: CanvasRenderingContext2D;
|
|
13
|
+
data: KLineData[];
|
|
14
|
+
dpr: number;
|
|
15
|
+
crosshairX: number;
|
|
16
|
+
index: number;
|
|
17
|
+
}): void;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { PaneRenderer } from '../layout/pane';
|
|
2
|
+
export type MAFlags = {
|
|
3
|
+
ma5?: boolean;
|
|
4
|
+
ma10?: boolean;
|
|
5
|
+
ma20?: boolean;
|
|
6
|
+
};
|
|
7
|
+
/**
|
|
8
|
+
* MA 渲染:复用现有 drawMA*Line(world 坐标绘制:需 translate(-scrollLeft,0))。
|
|
9
|
+
* 注意:drawMA*Line 内部使用 priceToY + priceRange,因此可直接用 pane.priceRange。
|
|
10
|
+
*/
|
|
11
|
+
export declare function createMARenderer(showMA: MAFlags): PaneRenderer;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export declare function drawPaneBorders(args: {
|
|
2
|
+
ctx: CanvasRenderingContext2D;
|
|
3
|
+
dpr: number;
|
|
4
|
+
width: number;
|
|
5
|
+
panes: Array<{
|
|
6
|
+
top: number;
|
|
7
|
+
height: number;
|
|
8
|
+
}>;
|
|
9
|
+
color?: string;
|
|
10
|
+
omitOuterTop?: boolean;
|
|
11
|
+
omitOuterRight?: boolean;
|
|
12
|
+
omitOuterBottom?: boolean;
|
|
13
|
+
omitOuterLeft?: boolean;
|
|
14
|
+
}): void;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { KLineData } from '../../types/price';
|
|
2
|
+
export declare function drawTimeAxisLayer(args: {
|
|
3
|
+
ctx: CanvasRenderingContext2D;
|
|
4
|
+
data: KLineData[];
|
|
5
|
+
scrollLeft: number;
|
|
6
|
+
kWidth: number;
|
|
7
|
+
kGap: number;
|
|
8
|
+
startIndex: number;
|
|
9
|
+
endIndex: number;
|
|
10
|
+
dpr: number;
|
|
11
|
+
crosshair?: {
|
|
12
|
+
x: number;
|
|
13
|
+
index: number;
|
|
14
|
+
} | null;
|
|
15
|
+
}): void;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { PaneRenderer } from '../layout/pane';
|
|
2
|
+
/**
|
|
3
|
+
* 右侧轴渲染:每个 pane 各画一段。
|
|
4
|
+
* 说明:目前复用旧的 axis.ts(后续再迁 core 化)。
|
|
5
|
+
*/
|
|
6
|
+
export declare function createYAxisRenderer(opts: {
|
|
7
|
+
axisX: number;
|
|
8
|
+
axisWidth: number;
|
|
9
|
+
yPaddingPx: number;
|
|
10
|
+
ticks?: number;
|
|
11
|
+
}): PaneRenderer;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export type PriceRange = {
|
|
2
|
+
maxPrice: number;
|
|
3
|
+
minPrice: number;
|
|
4
|
+
};
|
|
5
|
+
export declare function priceToY(price: number, maxPrice: number, minPrice: number, canvasHeight: number, paddingTop: number, paddingBottom: number): number;
|
|
6
|
+
/**
|
|
7
|
+
* 将逻辑像素 y 反算回价格
|
|
8
|
+
* - y 是相对于绘图区顶部的逻辑像素坐标(不含额外 translate)
|
|
9
|
+
* - paddingTop/paddingBottom 需与 priceToY 使用一致
|
|
10
|
+
*/
|
|
11
|
+
export declare function yToPrice(y: number, maxPrice: number, minPrice: number, canvasHeight: number, paddingTop: number, paddingBottom: number): number;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { PriceRange } from './price';
|
|
2
|
+
/**
|
|
3
|
+
* Pane 级别的价格坐标系(价格 -> pane 内 Y)
|
|
4
|
+
* - y=0 在 pane 顶部,y=height 在 pane 底部
|
|
5
|
+
*/
|
|
6
|
+
export declare class PriceScale {
|
|
7
|
+
private range;
|
|
8
|
+
private height;
|
|
9
|
+
private paddingTop;
|
|
10
|
+
private paddingBottom;
|
|
11
|
+
setRange(r: PriceRange): void;
|
|
12
|
+
setHeight(h: number): void;
|
|
13
|
+
setPadding(top: number, bottom: number): void;
|
|
14
|
+
getRange(): PriceRange;
|
|
15
|
+
getPaddingTop(): number;
|
|
16
|
+
getPaddingBottom(): number;
|
|
17
|
+
priceToY(price: number): number;
|
|
18
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { KLineData } from '../../types/price';
|
|
2
|
+
import { PriceRange } from '../scale/price';
|
|
3
|
+
/**
|
|
4
|
+
* 计算当前视口可见的 K 线索引范围。
|
|
5
|
+
*
|
|
6
|
+
* - 基于 `scrollLeft` 与 `viewWidth` 推导可见的 worldX 区间。
|
|
7
|
+
* - 再用 `unit = kWidth + kGap` 映射到索引区间。
|
|
8
|
+
* - 会额外在左右各扩展 1 根(start-1/end+1),用于避免边缘裁剪带来的“断线/缺一根”观感。
|
|
9
|
+
*
|
|
10
|
+
* @param scrollLeft 容器当前横向滚动量(逻辑像素)
|
|
11
|
+
* @param viewWidth 绘图区域宽度(plotWidth,逻辑像素,不含右侧 yAxis)
|
|
12
|
+
* @param kWidth 单根 K 线宽度(逻辑像素)
|
|
13
|
+
* @param kGap K 线间距(逻辑像素)
|
|
14
|
+
* @param totalDataCount 数据总条数
|
|
15
|
+
*/
|
|
16
|
+
export declare function getVisibleRange(scrollLeft: number, viewWidth: number, kWidth: number, kGap: number, totalDataCount: number): {
|
|
17
|
+
start: number;
|
|
18
|
+
end: number;
|
|
19
|
+
};
|
|
20
|
+
/**
|
|
21
|
+
* 计算指定索引区间内的价格范围(max/min)。
|
|
22
|
+
*
|
|
23
|
+
* 主要用途:
|
|
24
|
+
* - 为 pane 的 y 轴缩放与刻度提供 priceRange
|
|
25
|
+
* - 为渲染器(网格线、极值标注等)提供可视区参考范围
|
|
26
|
+
*
|
|
27
|
+
* 注意:
|
|
28
|
+
* - `endIndex` 为开区间(不包含)
|
|
29
|
+
* - 若区间内无有效数据,会返回兜底范围 `{ maxPrice: 100, minPrice: 0 }`
|
|
30
|
+
*/
|
|
31
|
+
export declare function getVisiblePriceRange(data: KLineData[], startIndex: number, endIndex: number): PriceRange;
|
package/dist/index.cjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});let e=require(`vue`);function t(e){return e.open>e.close?`down`:e.open<e.close?`up`:`flat`}function n(e,t,n,r,i,a){let o=t-n||1,s=(e-n)/o;return i+Math.max(1,r-i-a)*(1-s)}var r={info:`background:#1677ff;color:#fff;border:1px solid #1677ff;`,success:`background:#389e0d;color:#fff;border:1px solid #389e0d;`,warn:`background:#d46b08;color:#fff;border:1px solid #d46b08;`,error:`background:#cf1322;color:#fff;border:1px solid #cf1322;`},i=`padding:4px 8px;font-weight:600;border-radius:6px;`,a=`padding:4px 10px;border:1px solid #d9d9d9;background:#fff;color:#111;border-radius:6px;font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;`;function o(e,t,n){console.log(`%c${t}%c`,`${i}${r[e]}`,a,n)}var s=`rgba(214, 10, 34, 1)`,c=`rgba(3, 123, 102, 1)`;function l(e,r,i,a,l=1){if(o(`info`,`kLineDraw`,r),r.length===0)return;let u=a,d=i.yPaddingPx??0,f=Math.max(0,Math.min(d,Math.floor(u/2)-1)),p=f,m=f,h=r.reduce((e,t)=>Math.max(e,t.high),-1/0),g=r.reduce((e,t)=>Math.min(e,t.low),1/0);e.lineWidth=1/l;let _=i.kGap;for(let a=0;a<r.length;a++){let o=r[a];if(!o)continue;let d=n(o.high,h,g,u,p,m),f=n(o.low,h,g,u,p,m),v=n(o.open,h,g,u,p,m),y=n(o.close,h,g,u,p,m),b=Math.min(v,y),x=Math.max(Math.abs(v-y),2/l),S=t(o)===`up`?s:c;e.fillStyle=S,e.fillRect(_,b,i.kWidth,x),e.strokeStyle=S,e.lineWidth=3/l;let C=_+i.kWidth/2;e.beginPath(),e.moveTo(C,d),e.lineTo(C,b),e.stroke(),e.beginPath(),e.moveTo(C,b+x),e.lineTo(C,f),e.stroke(),_+=i.kWidth+i.kGap}}function u(e,t){let n=[...e].sort((e,t)=>e.timestamp-t.timestamp),r=n.length;if(t<=0)throw Error(`x must be > 0`);if(r<t)return[];let i=0;for(let e=0;e<t;e++)i+=n[e].close;let a=Array(r-t+1);a[0]=i/t;for(let e=t;e<r;e++)i+=n[e].close-n[e-t].close,a[e-t+1]=i/t;return a}function d(e,t,r,i,a,o,s=1){if(t.length===0)return;let c=[...t].sort((e,t)=>e.timestamp-t.timestamp),l=u(c,a);if(l.length===0)return;let d=i,f=r.yPaddingPx??0,p=Math.max(0,Math.min(f,Math.floor(d/2)-1)),m=p,h=p,g=c.reduce((e,t)=>Math.max(e,t.high),-1/0),_=c.reduce((e,t)=>Math.min(e,t.low),1/0);if(!Number.isFinite(g)||!Number.isFinite(_)||g<=_)return;let v=Array(l.length);for(let e=0;e<l.length;e++)v[e]=n(l[e],g,_,d,m,h);e.strokeStyle=o,e.lineWidth=2/s,e.beginPath();let y=!1;for(let t=0;t<v.length;t++){let n=v[t];if(!Number.isFinite(n))continue;let i=t+(a-1),o=r.kGap+i*(r.kWidth+r.kGap)+r.kWidth/2;y?e.lineTo(o,n):(e.moveTo(o,n),y=!0)}y&&e.stroke()}function f(e,t,n,r,i=1){d(e,t,n,r,10,`rgba(190, 131, 12, 1)`,i)}function p(e,t,n,r,i=1){d(e,t,n,r,20,`rgba(69, 112, 249, 1)`,i)}function m(e,t,n,r,i=1){d(e,t,n,r,5,`rgba(251, 186, 62, 1)`,i)}var h=(0,e.defineComponent)({__name:`KLineChart`,props:{data:{},kWidth:{default:10},kGap:{default:2},yPaddingPx:{default:60},showMA:{default:()=>({ma5:!0,ma10:!0,ma20:!0})},autoScrollToRight:{type:Boolean,default:!0}},setup(t,{expose:n}){let r=t,i=(0,e.ref)(null),a=(0,e.ref)(null),o=null;function s(){return{kWidth:r.kWidth,kGap:r.kGap,yPaddingPx:r.yPaddingPx}}function c(){let e=i.value,t=a.value;if(!e||!t||!r.data||r.data.length===0)return;let n=e.getContext(`2d`);if(!n)return;let o=r.data,c=t.getBoundingClientRect(),u=Math.max(1,Math.round(c.width)),d=Math.max(1,Math.round(c.height)),h=window.devicePixelRatio||1,g=s(),_=o.length,v=Math.max(u,g.kGap+_*(g.kWidth+g.kGap));e.style.width=`${v}px`,e.style.height=`${d}px`,e.width=Math.round(v*h),e.height=Math.round(d*h);let y=t.scrollLeft;n.setTransform(1,0,0,1,0,0),n.scale(h,h),n.clearRect(0,0,v,d),n.translate(-y,0),l(n,o,g,d,h),r.showMA.ma5&&m(n,o,g,d,h),r.showMA.ma10&&f(n,o,g,d,h),r.showMA.ma20&&p(n,o,g,d,h)}function u(){o!==null&&cancelAnimationFrame(o),o=requestAnimationFrame(()=>{o=null,c()})}function d(){let e=a.value;e&&(e.scrollLeft=e.scrollWidth,u())}return n({scheduleRender:u,scrollToRight:d}),(0,e.onMounted)(()=>{window.addEventListener(`resize`,u,{passive:!0}),u()}),(0,e.onUnmounted)(()=>{window.removeEventListener(`resize`,u),o!==null&&cancelAnimationFrame(o)}),(0,e.watch)(()=>[r.data,r.kWidth,r.kGap,r.yPaddingPx,r.showMA],async()=>{r.autoScrollToRight?(await(0,e.nextTick)(),d()):u()},{deep:!0}),(t,n)=>((0,e.openBlock)(),(0,e.createElementBlock)(`div`,{class:`chart-container`,ref_key:`containerRef`,ref:a,onScrollPassive:u},[(0,e.createElementVNode)(`canvas`,{ref_key:`canvasRef`,ref:i,class:`chart-canvas`},null,512)],544))}}),g=(e,t)=>{let n=e.__vccOpts||e;for(let[e,r]of t)n[e]=r;return n},_=g(h,[[`__scopeId`,`data-v-7f0057f3`]]);const v=_,y={install(e){e.component(`KLineChart`,v)}};exports.KLineChart=v,exports.KMapPlugin=y;
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});let e=require(`vue`);function t(e){let t=new Intl.DateTimeFormat(`zh-CN`,{timeZone:`Asia/Shanghai`,year:`numeric`,month:`2-digit`,day:`2-digit`}).formatToParts(new Date(e)).reduce((e,t)=>(t.type!==`literal`&&(e[t.type]=t.value),e),{});return`${t.year}-${t.month}-${t.day}`}function n(e){let t=new Date(e),n=t.getFullYear(),r=t.getMonth()+1;return r===1?{text:String(n),isYear:!0}:{text:String(r).padStart(2,`0`),isYear:!1}}function r(e){let t=new Date(e);return`${t.getFullYear()}-${t.getMonth()}`}const i=t,a=t,o=`rgba(214, 10, 34, 1)`,s=`rgba(3, 123, 102, 1)`,c=`rgba(0, 0, 0, 0.78)`;function l(e){return e>0?`rgba(214, 10, 34, 1)`:e<0?`rgba(3, 123, 102, 1)`:c}function u(e,t=2){let n=Math.abs(e);return n>=1e8?`${(e/1e8).toFixed(t)}亿`:n>=1e4?`${(e/1e4).toFixed(t)}万`:`${Math.round(e)}`}function d(e,t=2){return`${e>0?`+`:``}${e.toFixed(t)}`}function f(e,t=2){return`${e.toFixed(t)}%`}function p(e,t=2){return`${e>0?`+`:``}${e.toFixed(t)}%`}function m(e,t){let n=t?.close??e.open;return l(e.open-n)}function h(e){return l(e.close-e.open)}function g(e){return typeof e.changePercent==`number`?l(e.changePercent):typeof e.changeAmount==`number`?l(e.changeAmount):c}var _={class:`kline-tooltip__title`},v={key:0},y={class:`kline-tooltip__grid`},b={class:`row`},x={class:`row`},S={class:`row`},C={class:`row`},w={key:0,class:`row`},T={key:1,class:`row`},E={key:2,class:`row`},D={key:3,class:`row`},O={key:4,class:`row`},k={key:5,class:`row`},A=(0,e.defineComponent)({__name:`KLineTooltip`,props:{k:{},index:{},data:{},pos:{},setEl:{type:Function}},setup(t){let n=t;function r(e){n.setEl?.(e)}let a=(0,e.computed)(()=>{let e=n.k;if(!e)return c;let t=n.index;return m(e,typeof t==`number`&&t>0?n.data[t-1]:void 0)}),o=(0,e.computed)(()=>{let e=n.k;return e?h(e):c}),s=(0,e.computed)(()=>{let e=n.k;return e?g(e):c});return(n,c)=>t.k?((0,e.openBlock)(),(0,e.createElementBlock)(`div`,{key:0,ref:r,class:`kline-tooltip`,style:(0,e.normalizeStyle)({left:`${t.pos.x}px`,top:`${t.pos.y}px`})},[(0,e.createElementVNode)(`div`,_,[t.k.stockCode?((0,e.openBlock)(),(0,e.createElementBlock)(`span`,v,(0,e.toDisplayString)(t.k.stockCode),1)):(0,e.createCommentVNode)(``,!0),(0,e.createElementVNode)(`span`,null,(0,e.toDisplayString)((0,e.unref)(i)(t.k.timestamp)),1)]),(0,e.createElementVNode)(`div`,y,[(0,e.createElementVNode)(`div`,b,[c[0]||=(0,e.createElementVNode)(`span`,null,`开`,-1),(0,e.createElementVNode)(`span`,{style:(0,e.normalizeStyle)({color:a.value})},(0,e.toDisplayString)(t.k.open.toFixed(2)),5)]),(0,e.createElementVNode)(`div`,x,[c[1]||=(0,e.createElementVNode)(`span`,null,`高`,-1),(0,e.createElementVNode)(`span`,null,(0,e.toDisplayString)(t.k.high.toFixed(2)),1)]),(0,e.createElementVNode)(`div`,S,[c[2]||=(0,e.createElementVNode)(`span`,null,`低`,-1),(0,e.createElementVNode)(`span`,null,(0,e.toDisplayString)(t.k.low.toFixed(2)),1)]),(0,e.createElementVNode)(`div`,C,[c[3]||=(0,e.createElementVNode)(`span`,null,`收`,-1),(0,e.createElementVNode)(`span`,{style:(0,e.normalizeStyle)({color:o.value})},(0,e.toDisplayString)(t.k.close.toFixed(2)),5)]),typeof t.k.volume==`number`?((0,e.openBlock)(),(0,e.createElementBlock)(`div`,w,[c[4]||=(0,e.createElementVNode)(`span`,null,`成交量`,-1),(0,e.createElementVNode)(`span`,null,(0,e.toDisplayString)((0,e.unref)(u)(t.k.volume)),1)])):(0,e.createCommentVNode)(``,!0),typeof t.k.turnover==`number`?((0,e.openBlock)(),(0,e.createElementBlock)(`div`,T,[c[5]||=(0,e.createElementVNode)(`span`,null,`成交额`,-1),(0,e.createElementVNode)(`span`,null,(0,e.toDisplayString)((0,e.unref)(u)(t.k.turnover)),1)])):(0,e.createCommentVNode)(``,!0),typeof t.k.amplitude==`number`?((0,e.openBlock)(),(0,e.createElementBlock)(`div`,E,[c[6]||=(0,e.createElementVNode)(`span`,null,`振幅`,-1),(0,e.createElementVNode)(`span`,null,(0,e.toDisplayString)(t.k.amplitude),1)])):(0,e.createCommentVNode)(``,!0),typeof t.k.changePercent==`number`?((0,e.openBlock)(),(0,e.createElementBlock)(`div`,D,[c[7]||=(0,e.createElementVNode)(`span`,null,`涨跌幅`,-1),(0,e.createElementVNode)(`span`,{style:(0,e.normalizeStyle)({color:s.value})},(0,e.toDisplayString)((0,e.unref)(p)(t.k.changePercent)),5)])):(0,e.createCommentVNode)(``,!0),typeof t.k.changeAmount==`number`?((0,e.openBlock)(),(0,e.createElementBlock)(`div`,O,[c[8]||=(0,e.createElementVNode)(`span`,null,`涨跌额`,-1),(0,e.createElementVNode)(`span`,{style:(0,e.normalizeStyle)({color:s.value})},(0,e.toDisplayString)((0,e.unref)(d)(t.k.changeAmount)),5)])):(0,e.createCommentVNode)(``,!0),typeof t.k.turnoverRate==`number`?((0,e.openBlock)(),(0,e.createElementBlock)(`div`,k,[c[9]||=(0,e.createElementVNode)(`span`,null,`换手率`,-1),(0,e.createElementVNode)(`span`,null,(0,e.toDisplayString)((0,e.unref)(f)(t.k.turnoverRate)),1)])):(0,e.createCommentVNode)(``,!0)])],4)):(0,e.createCommentVNode)(``,!0)}}),j=(e,t)=>{let n=e.__vccOpts||e;for(let[e,r]of t)n[e]=r;return n},M=j(A,[[`__scopeId`,`data-v-8d05acf2`]]);function N(e,t,n,r,i){let a=n+r;return{start:Math.max(0,Math.floor(e/a)-1),end:Math.min(i,Math.ceil((e+t)/a)+1)}}function P(e,t,n){let r=-1/0,i=1/0;for(let a=t;a<n&&a<e.length;a++){let t=e[a];t&&(t.high>r&&(r=t.high),t.low<i&&(i=t.low))}return!Number.isFinite(r)||!Number.isFinite(i)?{maxPrice:100,minPrice:0}:{maxPrice:r,minPrice:i}}var F=class{range={maxPrice:100,minPrice:0};height=1;paddingTop=0;paddingBottom=0;setRange(e){this.range=e}setHeight(e){this.height=Math.max(1,e)}setPadding(e,t){this.paddingTop=Math.max(0,e),this.paddingBottom=Math.max(0,t)}getRange(){return this.range}getPaddingTop(){return this.paddingTop}getPaddingBottom(){return this.paddingBottom}priceToY(e){let{maxPrice:t,minPrice:n}=this.range,r=t-n||1,i=(e-n)/r,a=Math.max(1,this.height-this.paddingTop-this.paddingBottom);return this.paddingTop+a*(1-i)}},I=class{id;top=0;height=0;priceRange={maxPrice:100,minPrice:0};yAxis=new F;renderers=[];constructor(e){this.id=e}setLayout(e,t){this.top=e,this.height=Math.max(1,t),this.yAxis.setHeight(this.height)}setPadding(e,t){this.yAxis.setPadding(e,t)}addRenderer(e){this.renderers.push(e)}updateRange(e,t){this.priceRange=P(e,t.start,t.end),this.yAxis.setRange(this.priceRange)}},L=class{chart;isDragging=!1;dragStartX=0;scrollStartX=0;isTouchSession=!1;crosshairPos=null;crosshairIndex=null;hoveredIndex=null;activePaneId=null;tooltipPos={x:0,y:0};tooltipSize={width:220,height:180};constructor(e){this.chart=e}onPointerDown(e){if(e.isPrimary===!1)return;this.isTouchSession=e.pointerType===`touch`,this.isDragging=!0,this.updateHoverFromPoint(e.clientX,e.clientY);let t=this.chart.getDom().container;this.dragStartX=e.clientX,this.scrollStartX=t.scrollLeft,this.chart.scheduleDraw()}onPointerMove(e){if(e.isPrimary===!1)return;let t=this.chart.getDom().container;if(this.isDragging){let n=this.dragStartX-e.clientX;t.scrollLeft=this.scrollStartX+n,this.updateHoverFromPoint(e.clientX,e.clientY),this.chart.scheduleDraw();return}this.updateHoverFromPoint(e.clientX,e.clientY),this.chart.scheduleDraw()}onPointerUp(e){e.isPrimary!==!1&&(this.isDragging=!1)}onPointerLeave(e){e.isPrimary!==!1&&(this.isDragging=!1,this.isTouchSession=!1,this.clearHover(),this.chart.scheduleDraw())}onMouseDown(e){if(this.isTouchSession||e.button!==0)return;let t=this.chart.getDom().container;this.isDragging=!0,this.dragStartX=e.clientX,this.scrollStartX=t.scrollLeft,this.updateHoverFromPoint(e.clientX,e.clientY),this.chart.scheduleDraw(),e.preventDefault()}onMouseMove(e){if(this.isTouchSession)return;let t=this.chart.getDom().container;if(this.isDragging){let n=this.dragStartX-e.clientX;t.scrollLeft=this.scrollStartX+n;return}this.updateHover(e),this.chart.scheduleDraw()}onMouseUp(){this.isTouchSession||(this.isDragging=!1)}onMouseLeave(){this.isTouchSession||(this.isDragging=!1,this.clearHover(),this.chart.scheduleDraw())}onScroll(){this.clearHover(),this.chart.scheduleDraw()}onWheel(e){let t=this.chart.getDom().container,n=t.getBoundingClientRect(),r=e.clientX-n.left,i=t.scrollLeft;this.clearHover(),this.chart.zoomAt(r,i,e.deltaY)}setTooltipSize(e){this.tooltipSize=e}clearHover(){this.crosshairPos=null,this.crosshairIndex=null,this.hoveredIndex=null,this.activePaneId=null}updateHover(e){this.updateHoverFromPoint(e.clientX,e.clientY)}updateHoverFromPoint(e,t){let n=this.chart.getDom().container,r=n.getBoundingClientRect(),i=e-r.left,a=t-r.top,o=this.chart.getOption(),s=Math.max(1,Math.round(r.width)),c=Math.max(1,Math.round(r.height)),l=s-o.rightAxisWidth,u=c-o.bottomAxisHeight;if(i<0||a<0||i>l||a>u){this.clearHover();return}let d=n.scrollLeft,f=o.kWidth+o.kGap,p=d+i-o.kGap;if(p<0){this.clearHover();return}let m=this.chart.getData(),h=Math.floor(p/f),g=this.chart.getPanes().find(e=>a>=e.top&&a<=e.top+e.height);if(g?this.activePaneId=g.id:this.activePaneId=null,h>=0&&h<(m?.length??0)){this.crosshairIndex=h;let e=o.kGap+h*f+o.kWidth/2-d;this.crosshairPos={x:Math.min(Math.max(e,0),l),y:Math.min(Math.max(a,0),u)}}else this.crosshairIndex=null,this.crosshairPos={x:Math.min(Math.max(i,0),l),y:Math.min(Math.max(a,0),u)};let _=typeof this.crosshairIndex==`number`?m[this.crosshairIndex]:void 0;if(!_||!g){this.hoveredIndex=null;return}let v=a-g.top,y=g.yAxis.priceToY(_.open),b=g.yAxis.priceToY(_.close),x=g.yAxis.priceToY(_.high),S=g.yAxis.priceToY(_.low),C=Math.min(y,b),w=Math.max(y,b),T=p-this.crosshairIndex*f,E=o.kWidth/2,D=v>=C&&v<=w&&T>=0&&T<=o.kWidth,O=Math.abs(T-E)<=3&&v>=x&&v<=S;if(!D&&!O){this.hoveredIndex=null;return}this.hoveredIndex=this.crosshairIndex;let k=this.tooltipSize.width,A=this.tooltipSize.height,j=i+14,M=i-14-k,N=j+k+12<=s?j:M,P=a+14,F=Math.max(12,s-k-12),I=Math.max(12,c-A-12);this.tooltipPos={x:Math.min(Math.max(N,12),F),y:Math.min(Math.max(P,12),I)}}};function R(e,t,n,r,i,a){let o=t-n||1,s=(e-n)/o;return i+Math.max(1,r-i-a)*(1-s)}function ee(e,t,n,r,i,a){let o=t-n||1,s=Math.max(1,r-i-a);return n+(1-(Math.min(Math.max(e,i),i+s)-i)/s)*o}function z(e,t){return Math.round(e*t)/t}function B(e,t){return(Math.floor(e*t)+.5)/t}function V(e,t,n,r,i){let a=z(e,i),o=z(t,i),s=z(e+n,i),c=z(t+r,i);return{x:a,y:o,width:Math.max(1/i,s-a),height:Math.max(1/i,c-o)}}function H(e,t,n,r){if(t===n)return null;let i=Math.min(t,n),a=Math.max(t,n),o=Math.round(e*r),s=Math.round(i*r),c=Math.round(a*r);return{x:o/r,y:s/r,width:1/r,height:Math.max(1,c-s)/r}}function U(e,t,n,r){if(e===t)return null;let i=Math.min(e,t),a=Math.max(e,t),o=Math.round(i*r),s=Math.round(a*r),c=Math.round(n*r);return{x:o/r,y:c/r,width:Math.max(1,s-o)/r,height:1/r}}function te(e,t){let{x:n,y:r,width:i,height:a,priceRange:o,yPaddingPx:s=0,dpr:c,ticks:l=10,bgColor:u=`rgba(255,255,255,0.85)`,textColor:d=`rgba(0,0,0,0.65)`,lineColor:f=`rgba(0,0,0,0.12)`,fontSize:p=16,paddingX:m=12}=t,h=s,g=Math.max(0,Math.min(h,Math.floor(a/2)-1)),{maxPrice:_,minPrice:v}=o,y=_-v,b=y===0?0:y/(Math.max(2,l)-1);e.fillStyle=u,e.fillRect(n,r,i,a),e.strokeStyle=f,e.lineWidth=1,e.beginPath(),e.moveTo(B(n,c),r),e.lineTo(B(n,c),r+a),e.stroke(),e.font=`${p}px -apple-system,BlinkMacSystemFont,Trebuchet MS,Roboto,Ubuntu,sans-serif`,e.textBaseline=`middle`,e.textAlign=`left`;let x=n+m;for(let t=0;t<Math.max(2,l);t++){let i=y===0?_:_-b*t,o=Math.round(R(i,_,v,a,g,g)+r);e.strokeStyle=f,e.beginPath();let s=B(o,c);e.moveTo(n,s),e.lineTo(n+4,s),e.stroke(),e.fillStyle=d,e.fillText(i.toFixed(2),z(x,c),z(o,c))}}function ne(e,t){let{x:n,y:r,width:i,height:o,crosshairX:s,timestamp:c,dpr:l,bgColor:u=`rgba(0,0,0,0.55)`,textColor:d=`rgba(255,255,255,0.92)`,fontSize:f=12,paddingX:p=8,paddingY:m=4}=t,h=a(c);e.save(),e.font=`${f}px -apple-system,BlinkMacSystemFont,Trebuchet MS,Roboto,Ubuntu,sans-serif`,e.textBaseline=`middle`,e.textAlign=`center`;let g=Math.ceil(e.measureText(h).width),_=Math.min(i,g+p*2),v=Math.min(o,f+m*2),y=Math.min(Math.max(s,n+_/2),n+i-_/2),b=r+o/2,x=y-_/2,S=b-v/2;e.fillStyle=u,e.fillRect(z(x,l),z(S,l),z(_,l),z(v,l)),e.fillStyle=d,e.fillText(h,z(y,l),z(b,l)),e.restore()}function re(e,t){let{x:n,y:r,width:i,height:a,crosshairY:o,priceRange:s,yPaddingPx:c=0,dpr:l,bgColor:u=`rgba(0,0,0,0.55)`,textColor:d=`rgba(255,255,255,0.92)`,fontSize:f=12,paddingX:p=12}=t,m=Math.max(0,Math.min(c,Math.floor(a/2)-1)),{maxPrice:h,minPrice:g}=s,_=ee(o-r,h,g,a,m,m).toFixed(2);e.save(),e.font=`${f}px -apple-system,BlinkMacSystemFont,Trebuchet MS,Roboto,Ubuntu,sans-serif`,e.textBaseline=`middle`,e.textAlign=`left`;let v=e.measureText(_),y=f+6,b=Math.ceil(v.width+p*2),x=y,S=Math.min(i,b),C=Math.min(Math.max(o,r+x/2),r+a-x/2),w=C-x/2;e.fillStyle=u,e.fillRect(z(n,l),z(w,l),z(S,l),z(x,l)),e.fillStyle=d;let T=n+p;e.fillText(_,z(T,l),z(C,l)),e.restore()}function ie(e,t){let{x:i,y:a,width:o,height:s,data:c,scrollLeft:l,kWidth:u,kGap:d,startIndex:f,endIndex:p,dpr:m,bgColor:h=`rgba(255,255,255,0.85)`,textColor:g=`rgba(0,0,0,0.65)`,lineColor:_=`rgba(0,0,0,0.12)`,fontSize:v=16,paddingX:y=12}=t,b=u+d;e.fillStyle=h,e.fillRect(i,a,o,s),e.strokeStyle=_,e.lineWidth=1,e.beginPath(),e.moveTo(i,B(a,m)),e.lineTo(i+o,B(a,m)),e.stroke(),e.textAlign=`center`,e.textBaseline=`middle`;let x=a+s/2;for(let t=Math.max(f,1);t<p&&t<c.length;t++){let i=c[t],s=c[t-1];if(!(!i||!s)&&r(i.timestamp)!==r(s.timestamp)){let r=d+t*b-l,s=y,c=Math.max(y,o-y);if(r>=s&&r<=c){let t=Math.min(Math.max(r,s),c);e.strokeStyle=_,e.beginPath();let o=B(t,m);e.moveTo(o,a),e.lineTo(o,a+4),e.stroke();let{text:l,isYear:u}=n(i.timestamp);e.fillStyle=g,e.font=`${u?`bold `:``}${v}px -apple-system,BlinkMacSystemFont,Trebuchet MS,Roboto,Ubuntu,sans-serif`,e.fillText(l,z(t,m),z(x,m))}}}}function ae(e){return{draw({ctx:t,pane:n,dpr:r}){let i=typeof e.ticks==`number`?e.ticks:Math.max(2,Math.min(8,Math.round(n.height/80)));te(t,{x:e.axisX,y:n.top,width:e.axisWidth,height:n.height,priceRange:n.priceRange,yPaddingPx:e.yPaddingPx,dpr:r,ticks:i})}}}function oe(e){let{ctx:t,data:n,scrollLeft:r,kWidth:i,kGap:a,startIndex:o,endIndex:s,dpr:c,crosshair:l}=e,u=t.canvas.width/c,d=t.canvas.height/c;if(t.setTransform(1,0,0,1,0,0),t.scale(c,c),t.clearRect(0,0,u,d),ie(t,{x:0,y:0,width:u,height:d,data:n,scrollLeft:r,kWidth:i,kGap:a,startIndex:o,endIndex:s,dpr:c}),l&&typeof l.index==`number`){let e=n[l.index];e&&ne(t,{x:0,y:0,width:u,height:d,crosshairX:l.x,timestamp:e.timestamp,dpr:c})}}function W(e){let{ctx:t,plotWidth:n,plotHeight:r,dpr:i,x:a,y:o}=e;t.save(),t.beginPath(),t.rect(0,0,n,r),t.clip(),t.fillStyle=`rgba(0,0,0,0.28)`;let s=H(a,0,r,i);s&&t.fillRect(s.x,s.y,s.width,s.height);let c=U(0,n,o,i);c&&t.fillRect(c.x,c.y,c.width,c.height),t.restore()}function G(e){let{ctx:t,pane:n,axisWidth:r,dpr:i,crosshairY:a,yPaddingPx:o}=e;re(t,{x:0,y:n.top,width:r,height:n.height,crosshairY:a,priceRange:n.priceRange,yPaddingPx:o,dpr:i})}function K(e,t,n){if(t<n-1)return;let r=0;for(let i=0;i<n;i++){let n=e[t-i];if(!n)return;r+=n.close}return r/n}const q=`rgba(255, 193, 37, 1)`,J=`rgba(190, 131, 12, 1)`,Y=`rgba(69, 112, 249, 1)`;function X(e,t,n,r,i,a,o,s,c,l){if(t.length<c)return;let u=r,d=n.yPaddingPx??0,f=Math.max(0,Math.min(d,Math.floor(u/2)-1)),p=f,m=f,h,g;if(s)h=s.maxPrice,g=s.minPrice;else{h=-1/0,g=1/0;for(let e=a;e<o&&e<t.length;e++){let n=t[e];n&&(n.high>h&&(h=n.high),n.low<g&&(g=n.low))}}if(!Number.isFinite(h)||!Number.isFinite(g))return;let _=n.kWidth+n.kGap;e.strokeStyle=l,e.lineWidth=1,e.lineJoin=`round`,e.lineCap=`round`,e.beginPath();let v=!0,y=Math.max(a,c-1);for(let r=y;r<o&&r<t.length;r++){let a=0;for(let e=0;e<c;e++){let n=t[r-e];if(!n)return;a+=n.close}let o=a/c,s=n.kGap+r*_+n.kWidth/2,l=R(o,h,g,u,p,m),d=B(s,i),f=B(l,i);v?(e.moveTo(d,f),v=!1):e.lineTo(d,f)}e.stroke()}function se(e,t,n,r,i=1,a=0,o=t.length,s){X(e,t,n,r,i,a,o,s,5,q)}function ce(e,t,n,r,i=1,a=0,o=t.length,s){X(e,t,n,r,i,a,o,s,10,J)}function le(e,t,n,r,i=1,a=0,o=t.length,s){X(e,t,n,r,i,a,o,s,20,Y)}function ue(e){let{ctx:t,data:n,endIndex:r,showMA:i}=e;if(!n.length)return;t.save(),t.font=`12px Arial`,t.textBaseline=`top`,t.textAlign=`left`;let a=Math.min(r-1,n.length-1),o=[];if(i.ma5&&o.push({label:`MA5`,color:q,value:K(n,a,5)}),i.ma10&&o.push({label:`MA10`,color:J,value:K(n,a,10)}),i.ma20&&o.push({label:`MA20`,color:Y,value:K(n,a,20)}),o.length>0){let e=t.measureText(`均线`).width+10;for(let n of o){let r=typeof n.value==`number`?` ${n.value.toFixed(2)}`:``;e+=t.measureText(`${n.label}${r}`).width+10}e-=10;let n=Math.ceil(e+16);t.fillStyle=`rgba(255,255,255,0.85)`,t.fillRect(8,8,n,24);let r=16;t.fillStyle=`#333`,t.fillText(`均线`,r,14),r+=t.measureText(`均线`).width+10;for(let e of o){let n=typeof e.value==`number`?` ${e.value.toFixed(2)}`:``,i=`${e.label}${n}`;t.fillStyle=e.color,t.fillText(i,r,14),r+=t.measureText(i).width+10}}t.restore()}function de(e){let{ctx:t,dpr:n,paneTop:r,title:i}=e;t.save(),t.font=`12px Arial`,t.textBaseline=`top`,t.textAlign=`left`,t.fillStyle=`rgba(0,0,0,0.55)`;let a=r+8;t.fillText(i,8,a),t.restore()}function fe(e){let{ctx:t,dpr:n,width:r,panes:i,color:a=`rgba(0,0,0,0.12)`,omitOuterTop:o=!1,omitOuterRight:s=!1,omitOuterBottom:c=!1,omitOuterLeft:l=!1}=e;if(!i.length)return;t.save(),t.strokeStyle=a,t.lineWidth=1;let u=B(0,n),d=B(r,n),f=1/0,p=-1/0;for(let e of i)f=Math.min(f,e.top),p=Math.max(p,e.top+e.height);f=Number.isFinite(f)?f:0,p=Number.isFinite(p)?p:0;for(let e of i){let r=B(e.top,n),i=B(e.top+e.height,n),a=Math.abs(e.top-f)<1e-6,m=Math.abs(e.top+e.height-p)<1e-6;t.beginPath(),o&&a||(t.moveTo(u,r),t.lineTo(d,r)),s||(t.moveTo(d,r),t.lineTo(d,i)),c&&m||(t.moveTo(u,i),t.lineTo(d,i)),l||(t.moveTo(u,r),t.lineTo(u,i)),t.stroke()}t.restore()}var pe=class{dom;opt;data=[];raf=null;viewport=null;panes=[];interaction;onZoomChange;setOnZoomChange(e){this.onZoomChange=e}constructor(e,t){this.dom=e,this.opt=t,this.interaction=new L(this),this.initPanes()}getPanes(){return this.panes}setPaneRenderers(e,t){let n=this.panes.find(t=>t.id===e);if(n){n.renderers.length=0;for(let e of t)n.renderers.push(e);this.scheduleDraw()}}getDom(){return this.dom}getOption(){return this.opt}updateOptions(e){this.opt={...this.opt,...e},e.panes&&this.initPanes(),this.resize()}updateData(e){this.data=e??[],this.scheduleDraw()}getData(){return this.data}getContentWidth(){let e=this.data?.length??0;return this.opt.kGap+e*(this.opt.kWidth+this.opt.kGap)+this.opt.rightAxisWidth}resize(){this.computeViewport(),this.layoutPanes(),this.scheduleDraw()}scheduleDraw(){this.raf!=null&&cancelAnimationFrame(this.raf),this.raf=requestAnimationFrame(()=>{this.raf=null,this.draw()})}draw(){let e=this.computeViewport();if(!e)return;let t=this.dom.plotCanvas.getContext(`2d`),n=this.dom.yAxisCanvas.getContext(`2d`),r=this.dom.xAxisCanvas.getContext(`2d`);if(!t||!n||!r)return;t.setTransform(1,0,0,1,0,0),t.scale(e.dpr,e.dpr),t.clearRect(0,0,e.plotWidth,e.plotHeight),n.setTransform(1,0,0,1,0,0),n.scale(e.dpr,e.dpr),n.clearRect(0,0,this.opt.rightAxisWidth,e.plotHeight);let{start:i,end:a}=N(e.scrollLeft,e.plotWidth,this.opt.kWidth,this.opt.kGap,this.data.length),o={start:i,end:a};for(let r of this.panes){r.updateRange(this.data,o),t.save(),t.beginPath(),t.rect(0,r.top,e.plotWidth,r.height),t.clip(),t.translate(0,r.top);for(let n of r.renderers)n.draw({ctx:t,pane:r,data:this.data,range:o,scrollLeft:e.scrollLeft,kWidth:this.opt.kWidth,kGap:this.opt.kGap,dpr:e.dpr});t.restore(),ae({axisX:0,axisWidth:this.opt.rightAxisWidth,yPaddingPx:this.opt.yPaddingPx,ticks:r.id===`sub`?2:void 0}).draw({ctx:n,pane:r,data:this.data,range:o,scrollLeft:e.scrollLeft,kWidth:this.opt.kWidth,kGap:this.opt.kGap,dpr:e.dpr}),this.interaction.crosshairPos&&this.interaction.activePaneId===r.id&&G({ctx:n,pane:r,axisWidth:this.opt.rightAxisWidth,dpr:e.dpr,crosshairY:this.interaction.crosshairPos.y,yPaddingPx:this.opt.yPaddingPx})}fe({ctx:t,dpr:e.dpr,width:e.plotWidth,panes:[{top:0,height:e.plotHeight}]});let s=this.panes.find(e=>e.id===`sub`);s&&de({ctx:t,dpr:e.dpr,paneTop:s.top,title:`副图(占位)`}),oe({ctx:r,data:this.data,scrollLeft:e.scrollLeft,kWidth:this.opt.kWidth,kGap:this.opt.kGap,startIndex:o.start,endIndex:o.end,dpr:e.dpr,crosshair:this.interaction.crosshairPos&&typeof this.interaction.crosshairIndex==`number`?{x:this.interaction.crosshairPos.x,index:this.interaction.crosshairIndex}:null}),this.interaction.crosshairPos&&(t.save(),t.beginPath(),t.rect(0,0,e.plotWidth,e.plotHeight),t.clip(),W({ctx:t,plotWidth:e.plotWidth,plotHeight:e.plotHeight,dpr:e.dpr,x:this.interaction.crosshairPos.x,y:this.interaction.crosshairPos.y}),t.restore()),ue({ctx:t,data:this.data,endIndex:o.end,showMA:{ma5:!0,ma10:!0,ma20:!0},dpr:e.dpr})}zoomAt(e,t,n){let r=this.opt.kWidth+this.opt.kGap,i=(t+e)/r,a=n>0?-1:1,o=Math.max(this.opt.minKWidth,Math.min(this.opt.maxKWidth,this.opt.kWidth+a));if(o===this.opt.kWidth)return;let s=this.opt.kGap/this.opt.kWidth,c=Math.max(.5,o*s);this.opt={...this.opt,kWidth:o,kGap:c};let l=i*(o+c)-e;if(this.onZoomChange){this.onZoomChange(o,c,l);return}let u=this.dom.container,d=Math.max(0,u.scrollWidth-u.clientWidth);u.scrollLeft=Math.min(Math.max(0,l),d),this.scheduleDraw()}destroy(){this.raf!=null&&cancelAnimationFrame(this.raf),this.raf=null,this.viewport=null,this.panes=[],this.onZoomChange=void 0}initPanes(){this.panes=this.opt.panes.map(e=>new I(e.id))}layoutPanes(){let e=this.viewport;if(!e)return;let t=this.opt.panes.reduce((e,t)=>e+(t.ratio??0),0)||1,n=Math.max(0,this.opt.paneGap??0),r=0,i=Math.min(this.panes.length,this.opt.panes.length),a=n*Math.max(0,i-1),o=Math.max(1,e.plotHeight-a);for(let e=0;e<i;e++){let a=this.opt.panes[e],s=this.panes[e];if(!a||!s)continue;let c=e===i-1?o-r:Math.round(o*(a.ratio/t));s.setLayout(r,c),s.setPadding(this.opt.yPaddingPx,this.opt.yPaddingPx),r+=c+n}}computeViewport(){let e=this.dom.container;if(!e)return null;let t=e.getBoundingClientRect(),n=Math.max(1,Math.round(t.width)),r=Math.max(1,Math.round(t.height)),i=e.scrollLeft,a=n-this.opt.rightAxisWidth,o=r-this.opt.bottomAxisHeight,s=window.devicePixelRatio||1,c=16*1024*1024;n*s*(r*s)>c&&(s=Math.sqrt(c/(n*r))),this.dom.canvasLayer.style.width=`${n}px`,this.dom.canvasLayer.style.height=`${r}px`,this.dom.plotCanvas.style.width=`${a}px`,this.dom.plotCanvas.style.height=`${o}px`,this.dom.plotCanvas.width=Math.round(a*s),this.dom.plotCanvas.height=Math.round(o*s),this.dom.yAxisCanvas.style.width=`${this.opt.rightAxisWidth}px`,this.dom.yAxisCanvas.style.height=`${o}px`,this.dom.yAxisCanvas.width=Math.round(this.opt.rightAxisWidth*s),this.dom.yAxisCanvas.height=Math.round(o*s),this.dom.xAxisCanvas.style.width=`${a}px`,this.dom.xAxisCanvas.style.height=`${this.opt.bottomAxisHeight}px`,this.dom.xAxisCanvas.width=Math.round(a*s),this.dom.xAxisCanvas.height=Math.round(this.opt.bottomAxisHeight*s);let l={viewWidth:n,viewHeight:r,plotWidth:a,plotHeight:o,scrollLeft:i,dpr:s};return this.viewport=l,l}};function me(e){return e.open>e.close?`down`:e.open<e.close?`up`:`flat`}var he=`rgba(214, 10, 34, 1)`,ge=`rgba(3, 123, 102, 1)`;const _e={draw({ctx:e,pane:t,data:n,range:r,scrollLeft:i,kWidth:a,kGap:o,dpr:s}){if(!n.length)return;let c=a+o;e.save(),e.translate(-i,0);for(let i=r.start;i<r.end&&i<n.length;i++){let r=n[i];if(!r)continue;let l=t.yAxis.priceToY(r.open),u=t.yAxis.priceToY(r.close),d=t.yAxis.priceToY(r.high),f=t.yAxis.priceToY(r.low),p=o+i*c,m=V(p,Math.min(l,u),a,Math.max(Math.abs(l-u),1),s);e.fillStyle=me(r)===`up`?he:ge,e.fillRect(m.x,m.y,m.width,m.height);let h=p+a/2,g=m.y,_=m.y+m.height,v=Math.max(r.open,r.close),y=Math.min(r.open,r.close);if(r.high>v){let t=H(h,d,g,s);t&&e.fillRect(t.x,t.y,t.width,t.height)}if(r.low<y){let t=H(h,_,f,s);t&&e.fillRect(t.x,t.y,t.width,t.height)}}e.restore()}},Z={draw({ctx:e,pane:t,data:n,range:r,scrollLeft:i,kWidth:a,kGap:o,dpr:s}){if(!n.length)return;let c=a+o,l=t.id===`main`?6:2;e.save(),e.fillStyle=`rgba(0,0,0,0.06)`,e.translate(-i,0);let u=e.canvas.width/s,d=i,f=i+u,p=t.yAxis.getPaddingTop(),m=t.yAxis.getPaddingBottom(),h=p,g=Math.max(p,t.height-m),_=Math.max(0,g-h);for(let t=0;t<l;t++){let n=l<=1?0:t/(l-1),r=U(d,f,Math.round(h+n*_),s);r&&e.fillRect(r.x,r.y,r.width,r.height)}function v(e){let t=new Date(e);return`${t.getFullYear()}-${t.getMonth()}`}for(let i=Math.max(r.start,1);i<r.end&&i<n.length;i++){let r=n[i],a=n[i-1];if(!(!r||!a)&&v(r.timestamp)!==v(a.timestamp)){let n=H(o+i*c,0,t.height,s);n&&e.fillRect(n.x,n.y,n.width,n.height)}}e.restore()}},ve={draw({ctx:e,pane:t,data:n,range:r,scrollLeft:i,kWidth:a,kGap:o,dpr:s}){let c=n[n.length-1];if(!c)return;e.save(),e.translate(-i,0);let l=Math.round(t.yAxis.priceToY(c.close)),u=a+o,d=o+r.start*u,f=o+r.end*u;e.strokeStyle=`#F59999`,e.lineWidth=1,e.setLineDash([4,3]),e.beginPath();let p=(Math.floor(l*s)+.5)/s;e.moveTo(Math.round(d*s)/s,p),e.lineTo(Math.round(f*s)/s,p),e.stroke(),e.setLineDash([]),e.restore()}};function ye(e){return{draw({ctx:t,pane:n,data:r,range:i,scrollLeft:a,kWidth:o,kGap:s,dpr:c}){t.save(),t.translate(-a,0);let l={kWidth:o,kGap:s,yPaddingPx:0};e.ma5&&se(t,r,l,n.height,c,i.start,i.end,n.priceRange),e.ma10&&ce(t,r,l,n.height,c,i.start,i.end,n.priceRange),e.ma20&&le(t,r,l,n.height,c,i.start,i.end,n.priceRange),t.restore()}}}const be={draw({ctx:e,pane:t,data:n,range:r,scrollLeft:i,kWidth:a,kGap:o,dpr:s}){if(!n.length||t.id!==`main`)return;let c=Math.max(0,r.start),l=Math.min(n.length,r.end);if(l-c<=0)return;let u=-1/0,d=1/0,f=c,p=c;for(let e=c;e<l;e++){let t=n[e];t&&(t.high>=u&&(u=t.high,f=e),t.low<=d&&(d=t.low,p=e))}if(!Number.isFinite(u)||!Number.isFinite(d))return;let m=a+o,h=e=>o+e*m+a/2;e.save(),e.translate(-i,0),Q(e,h(f),t.yAxis.priceToY(u),u,s),Q(e,h(p),t.yAxis.priceToY(d),d,s),e.restore()}};function Q(e,t,n,r,i){let a=r.toFixed(2),o=U(t,t+30,n,i);o&&(e.fillStyle=`rgba(0,0,0,0.45)`,e.fillRect(o.x,o.y,o.width,o.height));let s=z(t+30,i),c=z(n,i);e.fillStyle=`rgba(0,0,0,0.45)`,e.beginPath(),e.arc(s,c,2,0,Math.PI*2),e.fill(),e.font=`12px Arial`,e.textBaseline=`middle`,e.textAlign=`left`,e.fillStyle=`rgba(0,0,0,0.70)`,e.fillText(a,z(t+30+4,i),z(n,i))}var xe={class:`chart-wrapper`},Se=(0,e.defineComponent)({__name:`KLineChart`,props:{data:{},kWidth:{default:10},kGap:{default:2},yPaddingPx:{default:0},showMA:{default:()=>({ma5:!0,ma10:!0,ma20:!0})},autoScrollToRight:{type:Boolean,default:!0},minKWidth:{default:2},maxKWidth:{default:50},rightAxisWidth:{default:70},bottomAxisHeight:{default:24},paneRatios:{default:()=>[.75,.25]}},setup(t,{expose:n}){let r=t,i=(0,e.ref)(null),a=(0,e.ref)(null),o=(0,e.ref)(null),s=(0,e.ref)(null),c=(0,e.ref)(null),l=(0,e.ref)(r.kWidth),u=(0,e.ref)(r.kGap),d=(0,e.shallowRef)(null);function f(){d.value?.scheduleDraw()}let p=(0,e.ref)(null);function m(e){if(p.value=e,!e)return;let t=e.getBoundingClientRect();d.value?.interaction.setTooltipSize({width:Math.max(180,Math.round(t.width)),height:Math.max(80,Math.round(t.height))})}let h=(0,e.ref)(!1),g=(0,e.ref)(null),_=(0,e.ref)({x:0,y:0}),v=(0,e.computed)(()=>{let e=g.value;return typeof e==`number`?r.data?.[e]??null:null}),y=(0,e.computed)(()=>g.value),b=(0,e.computed)(()=>_.value);function x(){let e=d.value?.interaction;if(!e){g.value=null;return}g.value=e.hoveredIndex??null;let t=e.tooltipPos;t&&(_.value={x:t.x,y:t.y})}function S(e){h.value=!0,d.value?.interaction.onMouseDown(e),x()}function C(e){h.value=!0,d.value?.interaction.onPointerDown(e),x()}function w(e){d.value?.interaction.onMouseMove(e),x()}function T(e){d.value?.interaction.onPointerMove(e),x()}function E(){h.value=!1,d.value?.interaction.onMouseUp(),x()}function D(e){h.value=!1,d.value?.interaction.onPointerUp(e),x()}function O(){h.value=!1,d.value?.interaction.onMouseLeave(),g.value=null}function k(e){h.value=!1,d.value?.interaction.onPointerLeave(e),g.value=null}function A(){d.value?.interaction.onScroll(),x()}function j(e){d.value?.interaction.onWheel(e),x()}let N=(0,e.computed)(()=>{let e=r.data?.length??0;return u.value+e*(l.value+u.value)+r.rightAxisWidth});function P(){let e=c.value;e&&(e.scrollLeft=e.scrollWidth,f())}return n({scheduleRender:f,scrollToRight:P}),(0,e.onMounted)(()=>{let t=c.value,n=s.value,p=i.value,m=a.value,h=o.value;if(!t||!n||!p||!m||!h)return;let g=[{id:`main`,ratio:r.paneRatios[0]},{id:`sub`,ratio:r.paneRatios[1]}],_=new pe({container:t,canvasLayer:n,plotCanvas:p,yAxisCanvas:m,xAxisCanvas:h},{kWidth:l.value,kGap:u.value,yPaddingPx:r.yPaddingPx,rightAxisWidth:r.rightAxisWidth,bottomAxisHeight:r.bottomAxisHeight,minKWidth:r.minKWidth,maxKWidth:r.maxKWidth,panes:g,paneGap:0});_.setOnZoomChange(async(t,n,r)=>{l.value=t,u.value=n,await(0,e.nextTick)(),await new Promise(e=>requestAnimationFrame(()=>e()));let i=c.value;if(!i)return;let a=Math.max(0,i.scrollWidth-i.clientWidth);i.scrollLeft=Math.min(Math.max(0,r),a),f()}),_.setPaneRenderers(`main`,[Z,ve,_e,be,ye(r.showMA)]),_.setPaneRenderers(`sub`,[Z]),d.value=_,_.updateData(r.data),_.resize();let v=()=>_.resize();window.addEventListener(`resize`,v,{passive:!0}),_.__onResize=v}),(0,e.onUnmounted)(()=>{let e=d.value;if(e){let t=e.__onResize;t&&window.removeEventListener(`resize`,t),e.destroy()}d.value=null}),(0,e.watch)(()=>[r.kWidth,r.kGap],([e,t])=>{typeof e==`number`&&(l.value=e),typeof t==`number`&&(u.value=t),d.value?.updateOptions({kWidth:l.value,kGap:u.value})}),(0,e.watch)(()=>[r.data,r.yPaddingPx,r.showMA],async()=>{d.value?.updateOptions({yPaddingPx:r.yPaddingPx}),d.value?.updateData(r.data),r.autoScrollToRight?(await(0,e.nextTick)(),P()):f()},{deep:!0}),(t,n)=>((0,e.openBlock)(),(0,e.createElementBlock)(`div`,xe,[(0,e.createElementVNode)(`div`,{class:(0,e.normalizeClass)([`chart-container`,{"is-dragging":h.value}]),ref_key:`containerRef`,ref:c,onScrollPassive:A,onMousedown:S,onMousemove:w,onMouseup:E,onMouseleave:O,onPointerdown:C,onPointermove:T,onPointerup:D,onPointerleave:k,onWheel:(0,e.withModifiers)(j,[`prevent`])},[(0,e.createElementVNode)(`div`,{class:`scroll-content`,style:(0,e.normalizeStyle)({width:N.value+`px`})},[(0,e.createElementVNode)(`div`,{class:`canvas-layer`,ref_key:`canvasLayerRef`,ref:s},[(0,e.createElementVNode)(`canvas`,{class:`plot-canvas`,ref_key:`plotCanvasRef`,ref:i},null,512),(0,e.createElementVNode)(`canvas`,{class:`y-axis-canvas`,ref_key:`yAxisCanvasRef`,ref:a},null,512),(0,e.createElementVNode)(`canvas`,{class:`x-axis-canvas`,ref_key:`xAxisCanvasRef`,ref:o},null,512),v.value?((0,e.openBlock)(),(0,e.createBlock)(M,{key:0,k:v.value,index:y.value,data:r.data,pos:b.value,"set-el":m},null,8,[`k`,`index`,`data`,`pos`])):(0,e.createCommentVNode)(``,!0)],512)],4)],34)]))}}),Ce=j(Se,[[`__scopeId`,`data-v-28c7f9b2`]]);const $=Ce,we={install(e){e.component(`KLineChart`,$)}};exports.KLineChart=$,exports.KMapPlugin=we;
|