@363045841yyt/klinechart-core 0.9.0-alpha.2 → 0.9.0-alpha.3

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.
Files changed (190) hide show
  1. package/dist/controllers/createChartController.d.ts.map +1 -1
  2. package/dist/controllers/createChartController.js +9 -8
  3. package/dist/controllers/createChartController.js.map +1 -1
  4. package/dist/controllers/index.d.ts +1 -0
  5. package/dist/controllers/index.d.ts.map +1 -1
  6. package/dist/controllers/index.js.map +1 -1
  7. package/dist/controllers/types.d.ts +5 -0
  8. package/dist/controllers/types.d.ts.map +1 -1
  9. package/dist/data/binance.d.ts +1 -1
  10. package/dist/data/binance.d.ts.map +1 -1
  11. package/dist/data/binance.js +1 -1
  12. package/dist/data/binance.js.map +1 -1
  13. package/dist/engine/chart.d.ts +9 -9
  14. package/dist/engine/chart.d.ts.map +1 -1
  15. package/dist/engine/chart.js +69 -96
  16. package/dist/engine/chart.js.map +1 -1
  17. package/dist/engine/chartTypes.d.ts +5 -0
  18. package/dist/engine/chartTypes.d.ts.map +1 -1
  19. package/dist/engine/controller/interaction.d.ts +4 -2
  20. package/dist/engine/controller/interaction.d.ts.map +1 -1
  21. package/dist/engine/controller/interaction.js +18 -10
  22. package/dist/engine/controller/interaction.js.map +1 -1
  23. package/dist/engine/data/chartDataManager.d.ts +12 -18
  24. package/dist/engine/data/chartDataManager.d.ts.map +1 -1
  25. package/dist/engine/data/chartDataManager.js +46 -37
  26. package/dist/engine/data/chartDataManager.js.map +1 -1
  27. package/dist/engine/data/incrementalLoadHint.d.ts +2 -4
  28. package/dist/engine/data/incrementalLoadHint.d.ts.map +1 -1
  29. package/dist/engine/data/incrementalLoadHint.js +5 -2
  30. package/dist/engine/data/incrementalLoadHint.js.map +1 -1
  31. package/dist/engine/data/scrollCompensator.d.ts +3 -13
  32. package/dist/engine/data/scrollCompensator.d.ts.map +1 -1
  33. package/dist/engine/data/scrollCompensator.js +13 -14
  34. package/dist/engine/data/scrollCompensator.js.map +1 -1
  35. package/dist/engine/indicators/chartIndicatorManager.d.ts +16 -17
  36. package/dist/engine/indicators/chartIndicatorManager.d.ts.map +1 -1
  37. package/dist/engine/indicators/chartIndicatorManager.js +37 -37
  38. package/dist/engine/indicators/chartIndicatorManager.js.map +1 -1
  39. package/dist/engine/layout/chartPaneLayout.d.ts +11 -7
  40. package/dist/engine/layout/chartPaneLayout.d.ts.map +1 -1
  41. package/dist/engine/layout/chartPaneLayout.js +58 -16
  42. package/dist/engine/layout/chartPaneLayout.js.map +1 -1
  43. package/dist/engine/paneRenderer.d.ts +2 -0
  44. package/dist/engine/paneRenderer.d.ts.map +1 -1
  45. package/dist/engine/paneRenderer.js +15 -5
  46. package/dist/engine/paneRenderer.js.map +1 -1
  47. package/dist/engine/render/chartRenderer.d.ts +28 -22
  48. package/dist/engine/render/chartRenderer.d.ts.map +1 -1
  49. package/dist/engine/render/chartRenderer.js +117 -69
  50. package/dist/engine/render/chartRenderer.js.map +1 -1
  51. package/dist/engine/render/layers/leftYAxisLayer.d.ts +9 -2
  52. package/dist/engine/render/layers/leftYAxisLayer.d.ts.map +1 -1
  53. package/dist/engine/render/layers/leftYAxisLayer.js +11 -2
  54. package/dist/engine/render/layers/leftYAxisLayer.js.map +1 -1
  55. package/dist/engine/render/layers/mainIndicatorLegendLayer.d.ts +6 -4
  56. package/dist/engine/render/layers/mainIndicatorLegendLayer.d.ts.map +1 -1
  57. package/dist/engine/render/layers/mainIndicatorLegendLayer.js +6 -9
  58. package/dist/engine/render/layers/mainIndicatorLegendLayer.js.map +1 -1
  59. package/dist/engine/render/layers/mainIndicatorLegendLayer.test.d.ts +2 -0
  60. package/dist/engine/render/layers/mainIndicatorLegendLayer.test.d.ts.map +1 -0
  61. package/dist/engine/render/layers/mainIndicatorLegendLayer.test.js +19 -0
  62. package/dist/engine/render/layers/mainIndicatorLegendLayer.test.js.map +1 -0
  63. package/dist/engine/render/layers/yAxisLayer.d.ts +9 -2
  64. package/dist/engine/render/layers/yAxisLayer.d.ts.map +1 -1
  65. package/dist/engine/render/layers/yAxisLayer.js +11 -2
  66. package/dist/engine/render/layers/yAxisLayer.js.map +1 -1
  67. package/dist/engine/renderers/Indicator/mainIndicatorLegend.d.ts +7 -3
  68. package/dist/engine/renderers/Indicator/mainIndicatorLegend.d.ts.map +1 -1
  69. package/dist/engine/renderers/Indicator/mainIndicatorLegend.js +234 -330
  70. package/dist/engine/renderers/Indicator/mainIndicatorLegend.js.map +1 -1
  71. package/dist/engine/renderers/Indicator/mainIndicatorLegendContext.d.ts +72 -0
  72. package/dist/engine/renderers/Indicator/mainIndicatorLegendContext.d.ts.map +1 -0
  73. package/dist/engine/renderers/Indicator/mainIndicatorLegendContext.js +177 -0
  74. package/dist/engine/renderers/Indicator/mainIndicatorLegendContext.js.map +1 -0
  75. package/dist/engine/renderers/leftYAxis.d.ts +15 -2
  76. package/dist/engine/renderers/leftYAxis.d.ts.map +1 -1
  77. package/dist/engine/renderers/leftYAxis.js +56 -16
  78. package/dist/engine/renderers/leftYAxis.js.map +1 -1
  79. package/dist/engine/renderers/yAxis.d.ts +16 -6
  80. package/dist/engine/renderers/yAxis.d.ts.map +1 -1
  81. package/dist/engine/renderers/yAxis.js +49 -16
  82. package/dist/engine/renderers/yAxis.js.map +1 -1
  83. package/dist/engine/state/viewportState.d.ts +4 -1
  84. package/dist/engine/state/viewportState.d.ts.map +1 -1
  85. package/dist/engine/state/viewportState.js +40 -8
  86. package/dist/engine/state/viewportState.js.map +1 -1
  87. package/dist/engine/subPaneManager.d.ts +5 -3
  88. package/dist/engine/subPaneManager.d.ts.map +1 -1
  89. package/dist/engine/subPaneManager.js +3 -2
  90. package/dist/engine/subPaneManager.js.map +1 -1
  91. package/dist/engine/utils/chartZoomController.d.ts +9 -7
  92. package/dist/engine/utils/chartZoomController.d.ts.map +1 -1
  93. package/dist/engine/utils/chartZoomController.js +13 -12
  94. package/dist/engine/utils/chartZoomController.js.map +1 -1
  95. package/dist/engine/viewport/chartViewportManager.d.ts +5 -16
  96. package/dist/engine/viewport/chartViewportManager.d.ts.map +1 -1
  97. package/dist/engine/viewport/chartViewportManager.js +7 -48
  98. package/dist/engine/viewport/chartViewportManager.js.map +1 -1
  99. package/dist/engine/viewport/viewport.d.ts +14 -0
  100. package/dist/engine/viewport/viewport.d.ts.map +1 -1
  101. package/dist/engine/viewport/viewport.js +10 -0
  102. package/dist/engine/viewport/viewport.js.map +1 -1
  103. package/dist/foundation/plugin/rendererPluginManager.test.js +15 -0
  104. package/dist/foundation/plugin/rendererPluginManager.test.js.map +1 -1
  105. package/dist/foundation/plugin/types.d.ts +3 -0
  106. package/dist/foundation/plugin/types.d.ts.map +1 -1
  107. package/dist/foundation/plugin/types.js.map +1 -1
  108. package/dist/foundation/reactivity/frameTransaction.d.ts +11 -0
  109. package/dist/foundation/reactivity/frameTransaction.d.ts.map +1 -1
  110. package/dist/foundation/reactivity/frameTransaction.js +17 -2
  111. package/dist/foundation/reactivity/frameTransaction.js.map +1 -1
  112. package/dist/rendering/render/Renderer.d.ts +58 -74
  113. package/dist/rendering/render/Renderer.d.ts.map +1 -1
  114. package/dist/rendering/render/Renderer.js +8 -18
  115. package/dist/rendering/render/Renderer.js.map +1 -1
  116. package/dist/rendering/render/createWebGPURenderer.d.ts.map +1 -1
  117. package/dist/rendering/render/createWebGPURenderer.js +30 -17
  118. package/dist/rendering/render/createWebGPURenderer.js.map +1 -1
  119. package/dist/rendering/render/createWebGPUSurfaceBackend.js +3 -3
  120. package/dist/rendering/render/createWebGPUSurfaceBackend.js.map +1 -1
  121. package/dist/rendering/render/physicalLine.d.ts +4 -0
  122. package/dist/rendering/render/physicalLine.d.ts.map +1 -0
  123. package/dist/rendering/render/physicalLine.js +37 -0
  124. package/dist/rendering/render/physicalLine.js.map +1 -0
  125. package/dist/rendering/render/physicalRegion.d.ts +14 -0
  126. package/dist/rendering/render/physicalRegion.d.ts.map +1 -0
  127. package/dist/rendering/render/physicalRegion.js +15 -0
  128. package/dist/rendering/render/physicalRegion.js.map +1 -0
  129. package/dist/rendering/scene/createLayerFromPlugin.d.ts.map +1 -1
  130. package/dist/rendering/scene/createLayerFromPlugin.js +8 -3
  131. package/dist/rendering/scene/createLayerFromPlugin.js.map +1 -1
  132. package/dist/rendering/scene/createScene.d.ts.map +1 -1
  133. package/dist/rendering/scene/createScene.js +4 -3
  134. package/dist/rendering/scene/createScene.js.map +1 -1
  135. package/dist/version.d.ts +1 -1
  136. package/dist/version.js +1 -1
  137. package/package.json +1 -1
  138. package/src/__tests__/stateKernel.test.ts +1 -0
  139. package/src/controllers/createChartController.ts +9 -8
  140. package/src/controllers/index.ts +11 -0
  141. package/src/controllers/types.ts +7 -0
  142. package/src/data/binance.ts +1 -1
  143. package/src/engine/__tests__/chart.dpr.test.ts +3 -2
  144. package/src/engine/__tests__/paneRenderer.resize.test.ts +52 -54
  145. package/src/engine/__tests__/subPaneManager.test.ts +5 -3
  146. package/src/engine/chart.ts +76 -109
  147. package/src/engine/chartTypes.ts +5 -0
  148. package/src/engine/controller/__tests__/interaction.dpr.test.ts +39 -2
  149. package/src/engine/controller/interaction.ts +18 -10
  150. package/src/engine/data/__tests__/chartDataManager.incrementalLoad.test.ts +37 -17
  151. package/src/engine/data/__tests__/scrollCompensator.geometry.test.ts +87 -75
  152. package/src/engine/data/chartDataManager.ts +54 -59
  153. package/src/engine/data/incrementalLoadHint.ts +8 -7
  154. package/src/engine/data/scrollCompensator.ts +16 -24
  155. package/src/engine/indicators/__tests__/chartIndicatorManager.test.ts +18 -26
  156. package/src/engine/indicators/chartIndicatorManager.ts +62 -62
  157. package/src/engine/layout/chartPaneLayout.ts +73 -21
  158. package/src/engine/paneRenderer.ts +17 -10
  159. package/src/engine/render/chartRenderer.ts +187 -129
  160. package/src/engine/render/layers/leftYAxisLayer.ts +29 -7
  161. package/src/engine/render/layers/mainIndicatorLegendLayer.test.ts +26 -0
  162. package/src/engine/render/layers/mainIndicatorLegendLayer.ts +13 -10
  163. package/src/engine/render/layers/yAxisLayer.ts +29 -7
  164. package/src/engine/renderers/Indicator/mainIndicatorLegend.ts +270 -403
  165. package/src/engine/renderers/Indicator/mainIndicatorLegendContext.ts +282 -0
  166. package/src/engine/renderers/__tests__/mainIndicatorLegend.renderer.test.ts +73 -2
  167. package/src/engine/renderers/__tests__/yAxis.renderer.test.ts +10 -6
  168. package/src/engine/renderers/leftYAxis.ts +60 -18
  169. package/src/engine/renderers/yAxis.ts +57 -22
  170. package/src/engine/state/viewportState.ts +44 -8
  171. package/src/engine/subPaneManager.ts +8 -5
  172. package/src/engine/utils/chartZoomController.ts +22 -19
  173. package/src/engine/viewport/__tests__/visibleRange.clamp.test.ts +50 -0
  174. package/src/engine/viewport/chartViewportManager.ts +9 -60
  175. package/src/engine/viewport/viewport.ts +14 -0
  176. package/src/foundation/plugin/rendererPluginManager.test.ts +18 -0
  177. package/src/foundation/plugin/types.ts +3 -0
  178. package/src/foundation/reactivity/frameTransaction.ts +20 -6
  179. package/src/rendering/render/Renderer.ts +69 -85
  180. package/src/rendering/render/__tests__/physicalLine.test.ts +51 -0
  181. package/src/rendering/render/__tests__/physicalRegion.test.ts +23 -0
  182. package/src/rendering/render/__tests__/webgpuRenderer.test.ts +31 -1
  183. package/src/rendering/render/__tests__/webgpuSurfaceBackend.test.ts +11 -0
  184. package/src/rendering/render/createWebGPURenderer.ts +30 -17
  185. package/src/rendering/render/createWebGPUSurfaceBackend.ts +3 -3
  186. package/src/rendering/render/physicalLine.ts +45 -0
  187. package/src/rendering/render/physicalRegion.ts +32 -0
  188. package/src/rendering/scene/createLayerFromPlugin.ts +8 -3
  189. package/src/rendering/scene/createScene.ts +4 -3
  190. package/src/version.ts +1 -1
@@ -1,6 +1,6 @@
1
1
  import { RENDERER_PRIORITY } from '../../../foundation/plugin/index';
2
- import { resolveThemeColors } from '../../../foundation/tokens/index';
3
2
  import { getFont, setCanvasFont } from '../../../foundation/tokens/fonts';
3
+ import { buildLegendTemplateContext, } from './mainIndicatorLegendContext';
4
4
  const textWidthCache = new Map();
5
5
  const TEXT_WIDTH_CACHE_LIMIT = 512;
6
6
  function measureTextWidth(ctx, text) {
@@ -25,11 +25,13 @@ function measureTextWidth(ctx, text) {
25
25
  export function createMainIndicatorLegendRendererPlugin(options) {
26
26
  const config = {
27
27
  yPaddingPx: options.yPaddingPx,
28
+ renderMode: 'canvas',
28
29
  };
30
+ const onContext = options.onContext;
29
31
  let pluginHost = null;
30
32
  return {
31
33
  name: 'mainIndicatorLegend',
32
- version: '2.1.0',
34
+ version: '2.2.0',
33
35
  description: '主图指标图例渲染器(MA 数据来自 StateStore)',
34
36
  debugName: '主图指标图例',
35
37
  paneId: 'main',
@@ -43,352 +45,254 @@ export function createMainIndicatorLegendRendererPlugin(options) {
43
45
  return [];
44
46
  },
45
47
  draw(context) {
46
- const { overlayCtx, data, range, crosshairIndex } = context;
47
- const klineData = data;
48
- const colors = resolveThemeColors(context.theme, context.isAsiaMarket, context.colorPresetSettings);
49
- if (!klineData.length || !overlayCtx)
48
+ const legend = buildLegendTemplateContext({
49
+ context,
50
+ host: pluginHost,
51
+ yPaddingPx: config.yPaddingPx,
52
+ });
53
+ onContext?.(legend);
54
+ if (config.renderMode === 'external')
50
55
  return;
51
- const fontSize = 12;
52
- const lineHeight = fontSize + 6;
53
- const legendX = 12;
54
- const gap = 10;
55
- const legendYOffset = 6;
56
- overlayCtx.save();
57
- setCanvasFont(overlayCtx, getFont(fontSize));
58
- overlayCtx.textAlign = 'left';
59
- overlayCtx.textBaseline = 'top';
60
- const targetIndex = crosshairIndex ?? Math.min(range.end - 1, klineData.length - 1);
61
- const rows = [];
62
- // ── Timeshare legend ──
63
- if (context.period === 'timeshare') {
64
- const tsData = data;
65
- const preClose = context.settings?.preClose ?? tsData[0]?.price ?? 0;
66
- const item = tsData[targetIndex];
67
- if (item) {
68
- const changeAmount = item.price - preClose;
69
- const changePercent = preClose !== 0 ? (changeAmount / preClose) * 100 : 0;
70
- const changeColor = changeAmount >= 0 ? colors.candleUpBody : colors.candleDownBody;
71
- if (context.paneWidth >= 400) {
72
- rows.push({
73
- draw: (rowIndex) => {
74
- let x = legendX;
75
- const y = config.yPaddingPx / 2 + legendYOffset + rowIndex * lineHeight;
76
- overlayCtx.fillStyle = colors.text.primary;
77
- overlayCtx.fillText('现价 ', x, y);
78
- x += measureTextWidth(overlayCtx, '现价 ');
79
- overlayCtx.fillStyle = changeColor;
80
- overlayCtx.fillText(item.price.toFixed(2), x, y);
81
- x += measureTextWidth(overlayCtx, item.price.toFixed(2)) + gap;
82
- overlayCtx.fillStyle = colors.text.primary;
83
- overlayCtx.fillText('均价 ', x, y);
84
- x += measureTextWidth(overlayCtx, '均价 ');
85
- overlayCtx.fillText(item.average.toFixed(2), x, y);
86
- x += measureTextWidth(overlayCtx, item.average.toFixed(2)) + gap;
87
- overlayCtx.fillStyle = colors.text.primary;
88
- overlayCtx.fillText('涨跌 ', x, y);
89
- x += measureTextWidth(overlayCtx, '涨跌 ');
90
- overlayCtx.fillStyle = changeColor;
91
- const sign = changeAmount > 0 ? '+' : '';
92
- overlayCtx.fillText(`${sign}${changeAmount.toFixed(2)}`, x, y);
93
- x += measureTextWidth(overlayCtx, `${sign}${changeAmount.toFixed(2)}`) + gap;
94
- overlayCtx.fillStyle = colors.text.primary;
95
- overlayCtx.fillText('涨幅 ', x, y);
96
- x += measureTextWidth(overlayCtx, '涨幅 ');
97
- overlayCtx.fillStyle = changeColor;
98
- const pctSign = changePercent > 0 ? '+' : '';
99
- overlayCtx.fillText(`${pctSign}${changePercent.toFixed(2)}%`, x, y);
100
- x += measureTextWidth(overlayCtx, `${pctSign}${changePercent.toFixed(2)}%`) + gap;
101
- const volText = formatVolumeShort(item.volume);
102
- overlayCtx.fillStyle = colors.text.tertiary;
103
- overlayCtx.fillText('成交量 ', x, y);
104
- x += measureTextWidth(overlayCtx, '成交量 ');
105
- overlayCtx.fillStyle = colors.text.primary;
106
- overlayCtx.fillText(volText, x, y);
107
- x += measureTextWidth(overlayCtx, volText) + gap;
108
- const amtText = formatAmountShort(item.amount);
109
- overlayCtx.fillStyle = colors.text.tertiary;
110
- overlayCtx.fillText('成交额 ', x, y);
111
- x += measureTextWidth(overlayCtx, '成交额 ');
112
- overlayCtx.fillStyle = colors.text.primary;
113
- overlayCtx.fillText(amtText, x, y);
114
- },
115
- });
116
- }
117
- else {
118
- rows.push({
119
- draw: (rowIndex) => {
120
- let x = legendX;
121
- const y = config.yPaddingPx / 2 + legendYOffset + rowIndex * lineHeight;
122
- overlayCtx.fillStyle = colors.text.primary;
123
- overlayCtx.fillText('现价 ', x, y);
124
- x += measureTextWidth(overlayCtx, '现价 ');
125
- overlayCtx.fillStyle = changeColor;
126
- overlayCtx.fillText(item.price.toFixed(2), x, y);
127
- x += measureTextWidth(overlayCtx, item.price.toFixed(2)) + gap;
128
- overlayCtx.fillStyle = colors.text.primary;
129
- overlayCtx.fillText('均价 ', x, y);
130
- x += measureTextWidth(overlayCtx, '均价 ');
131
- overlayCtx.fillText(item.average.toFixed(2), x, y);
132
- x += measureTextWidth(overlayCtx, item.average.toFixed(2)) + gap;
133
- overlayCtx.fillStyle = colors.text.tertiary;
134
- overlayCtx.fillText('成交量 ', x, y);
135
- x += measureTextWidth(overlayCtx, '成交量 ');
136
- overlayCtx.fillStyle = colors.text.primary;
137
- overlayCtx.fillText(formatVolumeShort(item.volume), x, y);
138
- },
139
- });
140
- rows.push({
141
- draw: (rowIndex) => {
142
- let x = legendX;
143
- const y = config.yPaddingPx / 2 + legendYOffset + rowIndex * lineHeight;
144
- overlayCtx.fillStyle = colors.text.primary;
145
- overlayCtx.fillText('涨跌 ', x, y);
146
- x += measureTextWidth(overlayCtx, '涨跌 ');
147
- overlayCtx.fillStyle = changeColor;
148
- const sign = changeAmount > 0 ? '+' : '';
149
- overlayCtx.fillText(`${sign}${changeAmount.toFixed(2)}`, x, y);
150
- x += measureTextWidth(overlayCtx, `${sign}${changeAmount.toFixed(2)}`) + gap;
151
- overlayCtx.fillStyle = colors.text.primary;
152
- overlayCtx.fillText('涨幅 ', x, y);
153
- x += measureTextWidth(overlayCtx, '涨幅 ');
154
- overlayCtx.fillStyle = changeColor;
155
- const pctSign = changePercent > 0 ? '+' : '';
156
- overlayCtx.fillText(`${pctSign}${changePercent.toFixed(2)}%`, x, y);
157
- x += measureTextWidth(overlayCtx, `${pctSign}${changePercent.toFixed(2)}%`) + gap;
158
- overlayCtx.fillStyle = colors.text.tertiary;
159
- overlayCtx.fillText('成交额 ', x, y);
160
- x += measureTextWidth(overlayCtx, '成交额 ');
161
- overlayCtx.fillStyle = colors.text.primary;
162
- overlayCtx.fillText(formatAmountShort(item.amount), x, y);
163
- },
164
- });
165
- }
166
- }
167
- }
168
- if (typeof crosshairIndex === 'number') {
169
- const k = klineData[targetIndex];
170
- if (k && typeof k.close === 'number') {
171
- const isUp = k.close >= k.open;
172
- const volText = typeof k.volume === 'number' ? formatVolumeShort(k.volume) : null;
173
- const upColor = isUp ? colors.candleUpBody : colors.candleDownBody;
174
- if (context.paneWidth >= 400) {
175
- rows.push({
176
- draw: (rowIndex) => {
177
- let x = legendX;
178
- const y = config.yPaddingPx / 2 + legendYOffset + rowIndex * lineHeight;
179
- overlayCtx.fillStyle = colors.text.primary;
180
- overlayCtx.fillText('O ', x, y);
181
- x += measureTextWidth(overlayCtx, 'O ');
182
- overlayCtx.fillStyle = upColor;
183
- overlayCtx.fillText(k.open.toFixed(2), x, y);
184
- x += measureTextWidth(overlayCtx, k.open.toFixed(2)) + gap;
185
- overlayCtx.fillStyle = colors.text.primary;
186
- overlayCtx.fillText('H ', x, y);
187
- x += measureTextWidth(overlayCtx, 'H ');
188
- overlayCtx.fillText(k.high.toFixed(2), x, y);
189
- x += measureTextWidth(overlayCtx, k.high.toFixed(2)) + gap;
190
- overlayCtx.fillText('L ', x, y);
191
- x += measureTextWidth(overlayCtx, 'L ');
192
- overlayCtx.fillText(k.low.toFixed(2), x, y);
193
- x += measureTextWidth(overlayCtx, k.low.toFixed(2)) + gap;
194
- overlayCtx.fillStyle = colors.text.primary;
195
- overlayCtx.fillText('C ', x, y);
196
- x += measureTextWidth(overlayCtx, 'C ');
197
- overlayCtx.fillStyle = upColor;
198
- overlayCtx.fillText(k.close.toFixed(2), x, y);
199
- x += measureTextWidth(overlayCtx, k.close.toFixed(2)) + gap;
200
- if (volText) {
201
- overlayCtx.fillStyle = colors.text.tertiary;
202
- overlayCtx.fillText('Vol ', x, y);
203
- x += measureTextWidth(overlayCtx, 'Vol ');
204
- overlayCtx.fillStyle = colors.text.primary;
205
- overlayCtx.fillText(volText, x, y);
206
- }
207
- },
208
- });
209
- }
210
- else {
211
- rows.push({
212
- draw: (rowIndex) => {
213
- let x = legendX;
214
- const y = config.yPaddingPx / 2 + legendYOffset + rowIndex * lineHeight;
215
- overlayCtx.fillStyle = colors.text.primary;
216
- overlayCtx.fillText('O ', x, y);
217
- x += measureTextWidth(overlayCtx, 'O ');
218
- overlayCtx.fillStyle = upColor;
219
- overlayCtx.fillText(k.open.toFixed(2), x, y);
220
- x += measureTextWidth(overlayCtx, k.open.toFixed(2)) + gap;
221
- overlayCtx.fillStyle = colors.text.primary;
222
- overlayCtx.fillText('H ', x, y);
223
- x += measureTextWidth(overlayCtx, 'H ');
224
- overlayCtx.fillText(k.high.toFixed(2), x, y);
225
- x += measureTextWidth(overlayCtx, k.high.toFixed(2)) + gap;
226
- overlayCtx.fillText('L ', x, y);
227
- x += measureTextWidth(overlayCtx, 'L ');
228
- overlayCtx.fillText(k.low.toFixed(2), x, y);
229
- },
230
- });
231
- rows.push({
232
- draw: (rowIndex) => {
233
- let x = legendX;
234
- const y = config.yPaddingPx / 2 + legendYOffset + rowIndex * lineHeight;
235
- overlayCtx.fillStyle = colors.text.primary;
236
- overlayCtx.fillText('C ', x, y);
237
- x += measureTextWidth(overlayCtx, 'C ');
238
- overlayCtx.fillStyle = upColor;
239
- overlayCtx.fillText(k.close.toFixed(2), x, y);
240
- x += measureTextWidth(overlayCtx, k.close.toFixed(2)) + gap;
241
- if (volText) {
242
- overlayCtx.fillStyle = colors.text.tertiary;
243
- overlayCtx.fillText('Vol ', x, y);
244
- x += measureTextWidth(overlayCtx, 'Vol ');
245
- overlayCtx.fillStyle = colors.text.primary;
246
- overlayCtx.fillText(volText, x, y);
247
- }
248
- },
249
- });
250
- }
251
- }
252
- }
253
- const scheduler = pluginHost && typeof pluginHost.getService === 'function'
254
- ? pluginHost.getService('indicatorScheduler')
255
- : undefined;
256
- const mainIndicators = scheduler?.getMainIndicators() ?? [];
257
- for (const meta of mainIndicators) {
258
- if (!meta.getTitleInfo)
259
- continue;
260
- if (!scheduler?.isMainIndicatorActive(meta.name))
261
- continue;
262
- const params = scheduler?.getMainIndicatorParams(meta.name) ?? {};
263
- const titleInfo = meta.getTitleInfo(klineData, targetIndex, params, pluginHost, 'main');
264
- if (!titleInfo)
265
- continue;
266
- rows.push({
267
- draw: (rowIndex) => {
268
- let x = legendX;
269
- let y = config.yPaddingPx / 2 + legendYOffset + rowIndex * lineHeight;
270
- overlayCtx.fillStyle = colors.text.primary;
271
- overlayCtx.fillText(titleInfo.name, x, y);
272
- x += measureTextWidth(overlayCtx, titleInfo.name);
273
- if (titleInfo.params && titleInfo.params.length > 0) {
274
- const paramText = `(${titleInfo.params.join(',')})`;
275
- overlayCtx.fillStyle = colors.text.tertiary;
276
- overlayCtx.fillText(paramText, x, y);
277
- x += measureTextWidth(overlayCtx, paramText) + gap;
278
- }
279
- else {
280
- x += gap;
281
- }
282
- if (titleInfo.values) {
283
- y += 1;
284
- for (const item of titleInfo.values) {
285
- const valText = `${item.label} ${item.value.toFixed(3)}`;
286
- overlayCtx.fillStyle = item.color;
287
- overlayCtx.fillText(valText, x, y);
288
- x += measureTextWidth(overlayCtx, valText) + gap;
289
- }
290
- }
291
- },
292
- });
293
- }
294
- pushComparisonLegendRows(context, klineData, targetIndex, range, rows, config.yPaddingPx, overlayCtx, legendX, legendYOffset, lineHeight, gap, colors);
295
- rows.forEach((row, index) => row.draw(index));
296
- overlayCtx.restore();
56
+ if (!legend || !context.overlayCtx)
57
+ return;
58
+ paintLegendOnCanvas(context.overlayCtx, legend);
297
59
  },
298
60
  getConfig() {
299
61
  return {
300
62
  yPaddingPx: config.yPaddingPx,
63
+ renderMode: config.renderMode,
301
64
  };
302
65
  },
303
66
  setConfig(newConfig) {
304
67
  if (typeof newConfig.yPaddingPx === 'number') {
305
68
  config.yPaddingPx = newConfig.yPaddingPx;
306
69
  }
70
+ if (newConfig.renderMode === 'canvas' || newConfig.renderMode === 'external') {
71
+ config.renderMode = newConfig.renderMode;
72
+ }
307
73
  },
308
74
  };
309
75
  }
310
- function pushComparisonLegendRows(context, klineData, targetIndex, range, rows, yPaddingPx, overlayCtx, legendX, legendYOffset, lineHeight, gap, colors) {
311
- const comparisonSymbols = context.comparisonSymbols;
312
- const comparisonData = context.comparisonData;
313
- const comparisonColors = context.comparisonColors;
314
- if (!comparisonSymbols?.length || !comparisonData?.size)
315
- return;
316
- const baseIndex = Math.max(0, range.start);
317
- const baseItem = klineData[baseIndex];
318
- if (!baseItem || !Number.isFinite(baseItem.close) || baseItem.close <= 0)
319
- return;
320
- const baseDate = baseItem.date ?? '';
321
- for (const spec of comparisonSymbols) {
322
- const data = comparisonData.get(spec.symbol);
323
- if (!data?.length)
324
- continue;
325
- const baseline = baseDate
326
- ? findBaselineByDate(data, baseDate)
327
- : findBaselineByTimestamp(data, baseItem.timestamp);
328
- if (!baseline || baseline.close <= 0)
329
- continue;
330
- const byDate = new Map();
331
- for (const item of data) {
332
- byDate.set(item.date ?? String(item.timestamp), item);
76
+ function paintLegendOnCanvas(overlayCtx, legend) {
77
+ const { layout, colors } = legend;
78
+ const fontSize = 12;
79
+ const { x: legendX, y: baseY, lineHeight, gap, compact } = layout;
80
+ overlayCtx.save();
81
+ setCanvasFont(overlayCtx, getFont(fontSize));
82
+ overlayCtx.textAlign = 'left';
83
+ overlayCtx.textBaseline = 'top';
84
+ let rowIndex = 0;
85
+ const rowY = () => baseY + rowIndex * lineHeight;
86
+ if (legend.timeshare) {
87
+ const ts = legend.timeshare;
88
+ if (!compact) {
89
+ let x = legendX;
90
+ const y = rowY();
91
+ overlayCtx.fillStyle = colors.textPrimary;
92
+ overlayCtx.fillText('现价 ', x, y);
93
+ x += measureTextWidth(overlayCtx, '现价 ');
94
+ overlayCtx.fillStyle = ts.changeColor;
95
+ overlayCtx.fillText(ts.price.toFixed(2), x, y);
96
+ x += measureTextWidth(overlayCtx, ts.price.toFixed(2)) + gap;
97
+ overlayCtx.fillStyle = colors.textPrimary;
98
+ overlayCtx.fillText('均价 ', x, y);
99
+ x += measureTextWidth(overlayCtx, '均价 ');
100
+ overlayCtx.fillText(ts.average.toFixed(2), x, y);
101
+ x += measureTextWidth(overlayCtx, ts.average.toFixed(2)) + gap;
102
+ overlayCtx.fillStyle = colors.textPrimary;
103
+ overlayCtx.fillText('涨跌 ', x, y);
104
+ x += measureTextWidth(overlayCtx, '涨跌 ');
105
+ overlayCtx.fillStyle = ts.changeColor;
106
+ const sign = ts.changeAmount > 0 ? '+' : '';
107
+ overlayCtx.fillText(`${sign}${ts.changeAmount.toFixed(2)}`, x, y);
108
+ x += measureTextWidth(overlayCtx, `${sign}${ts.changeAmount.toFixed(2)}`) + gap;
109
+ overlayCtx.fillStyle = colors.textPrimary;
110
+ overlayCtx.fillText('涨幅 ', x, y);
111
+ x += measureTextWidth(overlayCtx, '涨幅 ');
112
+ overlayCtx.fillStyle = ts.changeColor;
113
+ const pctSign = ts.changePercent > 0 ? '+' : '';
114
+ overlayCtx.fillText(`${pctSign}${ts.changePercent.toFixed(2)}%`, x, y);
115
+ x += measureTextWidth(overlayCtx, `${pctSign}${ts.changePercent.toFixed(2)}%`) + gap;
116
+ overlayCtx.fillStyle = colors.textTertiary;
117
+ overlayCtx.fillText('成交量 ', x, y);
118
+ x += measureTextWidth(overlayCtx, '成交量 ');
119
+ overlayCtx.fillStyle = colors.textPrimary;
120
+ overlayCtx.fillText(ts.volumeText, x, y);
121
+ x += measureTextWidth(overlayCtx, ts.volumeText) + gap;
122
+ overlayCtx.fillStyle = colors.textTertiary;
123
+ overlayCtx.fillText('成交额 ', x, y);
124
+ x += measureTextWidth(overlayCtx, '成交额 ');
125
+ overlayCtx.fillStyle = colors.textPrimary;
126
+ overlayCtx.fillText(ts.amountText, x, y);
127
+ rowIndex++;
333
128
  }
334
- const mainItem = klineData[targetIndex];
335
- if (!mainItem)
336
- continue;
337
- const key = mainItem.date ?? String(mainItem.timestamp);
338
- const currentItem = byDate.get(key);
339
- if (!currentItem || !Number.isFinite(currentItem.close))
340
- continue;
341
- const pct = ((currentItem.close - baseline.close) / baseline.close) * 100;
342
- const color = comparisonColors?.get(spec.symbol) ?? '#f59e0b';
343
- rows.push({
344
- draw: (rowIndex) => {
129
+ else {
130
+ {
345
131
  let x = legendX;
346
- const y = yPaddingPx / 2 + legendYOffset + rowIndex * lineHeight;
347
- const dotRadius = 4;
348
- overlayCtx.fillStyle = color;
349
- overlayCtx.beginPath();
350
- const fontSize = lineHeight - 6;
351
- overlayCtx.arc(x + dotRadius, y + fontSize / 2 - 1, dotRadius, 0, Math.PI * 2);
352
- overlayCtx.fill();
353
- x += dotRadius * 2 + 4;
354
- overlayCtx.fillStyle = colors.text.primary;
355
- overlayCtx.fillText(spec.symbol, x, y);
356
- x += measureTextWidth(overlayCtx, spec.symbol) + gap;
357
- const sign = pct > 0 ? '+' : '';
358
- const pctText = `${sign}${pct.toFixed(2)}%`;
359
- overlayCtx.fillStyle =
360
- pct > 0 ? colors.candleUpBody : pct < 0 ? colors.candleDownBody : colors.text.primary;
361
- overlayCtx.fillText(pctText, x, y);
362
- },
363
- });
132
+ const y = rowY();
133
+ overlayCtx.fillStyle = colors.textPrimary;
134
+ overlayCtx.fillText('现价 ', x, y);
135
+ x += measureTextWidth(overlayCtx, '现价 ');
136
+ overlayCtx.fillStyle = ts.changeColor;
137
+ overlayCtx.fillText(ts.price.toFixed(2), x, y);
138
+ x += measureTextWidth(overlayCtx, ts.price.toFixed(2)) + gap;
139
+ overlayCtx.fillStyle = colors.textPrimary;
140
+ overlayCtx.fillText('均价 ', x, y);
141
+ x += measureTextWidth(overlayCtx, '均价 ');
142
+ overlayCtx.fillText(ts.average.toFixed(2), x, y);
143
+ x += measureTextWidth(overlayCtx, ts.average.toFixed(2)) + gap;
144
+ overlayCtx.fillStyle = colors.textTertiary;
145
+ overlayCtx.fillText('成交量 ', x, y);
146
+ x += measureTextWidth(overlayCtx, '成交量 ');
147
+ overlayCtx.fillStyle = colors.textPrimary;
148
+ overlayCtx.fillText(ts.volumeText, x, y);
149
+ rowIndex++;
150
+ }
151
+ {
152
+ let x = legendX;
153
+ const y = rowY();
154
+ overlayCtx.fillStyle = colors.textPrimary;
155
+ overlayCtx.fillText('涨跌 ', x, y);
156
+ x += measureTextWidth(overlayCtx, '涨跌 ');
157
+ overlayCtx.fillStyle = ts.changeColor;
158
+ const sign = ts.changeAmount > 0 ? '+' : '';
159
+ overlayCtx.fillText(`${sign}${ts.changeAmount.toFixed(2)}`, x, y);
160
+ x += measureTextWidth(overlayCtx, `${sign}${ts.changeAmount.toFixed(2)}`) + gap;
161
+ overlayCtx.fillStyle = colors.textPrimary;
162
+ overlayCtx.fillText('涨幅 ', x, y);
163
+ x += measureTextWidth(overlayCtx, '涨幅 ');
164
+ overlayCtx.fillStyle = ts.changeColor;
165
+ const pctSign = ts.changePercent > 0 ? '+' : '';
166
+ overlayCtx.fillText(`${pctSign}${ts.changePercent.toFixed(2)}%`, x, y);
167
+ x += measureTextWidth(overlayCtx, `${pctSign}${ts.changePercent.toFixed(2)}%`) + gap;
168
+ overlayCtx.fillStyle = colors.textTertiary;
169
+ overlayCtx.fillText('成交额 ', x, y);
170
+ x += measureTextWidth(overlayCtx, '成交额 ');
171
+ overlayCtx.fillStyle = colors.textPrimary;
172
+ overlayCtx.fillText(ts.amountText, x, y);
173
+ rowIndex++;
174
+ }
175
+ }
364
176
  }
365
- }
366
- function findBaselineByDate(data, date) {
367
- for (const item of data) {
368
- if (item.date && item.date >= date)
369
- return item;
177
+ if (legend.ohlc) {
178
+ const k = legend.ohlc;
179
+ if (!compact) {
180
+ let x = legendX;
181
+ const y = rowY();
182
+ overlayCtx.fillStyle = colors.textPrimary;
183
+ overlayCtx.fillText('O ', x, y);
184
+ x += measureTextWidth(overlayCtx, 'O ');
185
+ overlayCtx.fillStyle = k.color;
186
+ overlayCtx.fillText(k.open.toFixed(2), x, y);
187
+ x += measureTextWidth(overlayCtx, k.open.toFixed(2)) + gap;
188
+ overlayCtx.fillStyle = colors.textPrimary;
189
+ overlayCtx.fillText('H ', x, y);
190
+ x += measureTextWidth(overlayCtx, 'H ');
191
+ overlayCtx.fillText(k.high.toFixed(2), x, y);
192
+ x += measureTextWidth(overlayCtx, k.high.toFixed(2)) + gap;
193
+ overlayCtx.fillText('L ', x, y);
194
+ x += measureTextWidth(overlayCtx, 'L ');
195
+ overlayCtx.fillText(k.low.toFixed(2), x, y);
196
+ x += measureTextWidth(overlayCtx, k.low.toFixed(2)) + gap;
197
+ overlayCtx.fillStyle = colors.textPrimary;
198
+ overlayCtx.fillText('C ', x, y);
199
+ x += measureTextWidth(overlayCtx, 'C ');
200
+ overlayCtx.fillStyle = k.color;
201
+ overlayCtx.fillText(k.close.toFixed(2), x, y);
202
+ x += measureTextWidth(overlayCtx, k.close.toFixed(2)) + gap;
203
+ if (k.volumeText) {
204
+ overlayCtx.fillStyle = colors.textTertiary;
205
+ overlayCtx.fillText('Vol ', x, y);
206
+ x += measureTextWidth(overlayCtx, 'Vol ');
207
+ overlayCtx.fillStyle = colors.textPrimary;
208
+ overlayCtx.fillText(k.volumeText, x, y);
209
+ }
210
+ rowIndex++;
211
+ }
212
+ else {
213
+ {
214
+ let x = legendX;
215
+ const y = rowY();
216
+ overlayCtx.fillStyle = colors.textPrimary;
217
+ overlayCtx.fillText('O ', x, y);
218
+ x += measureTextWidth(overlayCtx, 'O ');
219
+ overlayCtx.fillStyle = k.color;
220
+ overlayCtx.fillText(k.open.toFixed(2), x, y);
221
+ x += measureTextWidth(overlayCtx, k.open.toFixed(2)) + gap;
222
+ overlayCtx.fillStyle = colors.textPrimary;
223
+ overlayCtx.fillText('H ', x, y);
224
+ x += measureTextWidth(overlayCtx, 'H ');
225
+ overlayCtx.fillText(k.high.toFixed(2), x, y);
226
+ x += measureTextWidth(overlayCtx, k.high.toFixed(2)) + gap;
227
+ overlayCtx.fillText('L ', x, y);
228
+ x += measureTextWidth(overlayCtx, 'L ');
229
+ overlayCtx.fillText(k.low.toFixed(2), x, y);
230
+ rowIndex++;
231
+ }
232
+ {
233
+ let x = legendX;
234
+ const y = rowY();
235
+ overlayCtx.fillStyle = colors.textPrimary;
236
+ overlayCtx.fillText('C ', x, y);
237
+ x += measureTextWidth(overlayCtx, 'C ');
238
+ overlayCtx.fillStyle = k.color;
239
+ overlayCtx.fillText(k.close.toFixed(2), x, y);
240
+ x += measureTextWidth(overlayCtx, k.close.toFixed(2)) + gap;
241
+ if (k.volumeText) {
242
+ overlayCtx.fillStyle = colors.textTertiary;
243
+ overlayCtx.fillText('Vol ', x, y);
244
+ x += measureTextWidth(overlayCtx, 'Vol ');
245
+ overlayCtx.fillStyle = colors.textPrimary;
246
+ overlayCtx.fillText(k.volumeText, x, y);
247
+ }
248
+ rowIndex++;
249
+ }
250
+ }
370
251
  }
371
- return null;
372
- }
373
- function findBaselineByTimestamp(data, timestamp) {
374
- for (const item of data) {
375
- if (item.timestamp >= timestamp)
376
- return item;
252
+ for (const titleInfo of legend.indicators) {
253
+ let x = legendX;
254
+ let y = rowY();
255
+ overlayCtx.fillStyle = colors.textPrimary;
256
+ overlayCtx.fillText(titleInfo.name, x, y);
257
+ x += measureTextWidth(overlayCtx, titleInfo.name);
258
+ if (titleInfo.params && titleInfo.params.length > 0) {
259
+ const paramText = `(${titleInfo.params.join(',')})`;
260
+ overlayCtx.fillStyle = colors.textTertiary;
261
+ overlayCtx.fillText(paramText, x, y);
262
+ x += measureTextWidth(overlayCtx, paramText) + gap;
263
+ }
264
+ else {
265
+ x += gap;
266
+ }
267
+ if (titleInfo.values) {
268
+ y += 1;
269
+ for (const item of titleInfo.values) {
270
+ const valText = `${item.label} ${item.value.toFixed(3)}`;
271
+ overlayCtx.fillStyle = item.color;
272
+ overlayCtx.fillText(valText, x, y);
273
+ x += measureTextWidth(overlayCtx, valText) + gap;
274
+ }
275
+ }
276
+ rowIndex++;
377
277
  }
378
- return null;
379
- }
380
- function formatVolumeShort(v) {
381
- if (v >= 1e8)
382
- return (v / 1e8).toFixed(2) + '亿';
383
- if (v >= 1e4)
384
- return (v / 1e4).toFixed(2) + '万';
385
- return v.toFixed(2);
386
- }
387
- function formatAmountShort(v) {
388
- if (v >= 1e8)
389
- return (v / 1e8).toFixed(2) + '亿';
390
- if (v >= 1e4)
391
- return (v / 1e4).toFixed(2) + '万';
392
- return v.toFixed(2);
278
+ for (const cmp of legend.comparisons) {
279
+ let x = legendX;
280
+ const y = rowY();
281
+ const dotRadius = 4;
282
+ overlayCtx.fillStyle = cmp.color;
283
+ overlayCtx.beginPath();
284
+ overlayCtx.arc(x + dotRadius, y + fontSize / 2 - 1, dotRadius, 0, Math.PI * 2);
285
+ overlayCtx.fill();
286
+ x += dotRadius * 2 + 4;
287
+ overlayCtx.fillStyle = colors.textPrimary;
288
+ overlayCtx.fillText(cmp.symbol, x, y);
289
+ x += measureTextWidth(overlayCtx, cmp.symbol) + gap;
290
+ const sign = cmp.percent > 0 ? '+' : '';
291
+ const pctText = `${sign}${cmp.percent.toFixed(2)}%`;
292
+ overlayCtx.fillStyle = cmp.percentColor;
293
+ overlayCtx.fillText(pctText, x, y);
294
+ rowIndex++;
295
+ }
296
+ overlayCtx.restore();
393
297
  }
394
298
  //# sourceMappingURL=mainIndicatorLegend.js.map