@363045841yyt/klinechart-core 0.7.3 → 0.7.5

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 (247) hide show
  1. package/README.md +201 -201
  2. package/README.zh-CN.md +201 -201
  3. package/dist/controllers/index.d.ts +1 -0
  4. package/dist/controllers/index.d.ts.map +1 -1
  5. package/dist/controllers/index.js +1 -0
  6. package/dist/controllers/index.js.map +1 -1
  7. package/dist/engine/chart.d.ts +11 -19
  8. package/dist/engine/chart.d.ts.map +1 -1
  9. package/dist/engine/chart.js +92 -109
  10. package/dist/engine/chart.js.map +1 -1
  11. package/dist/engine/renderers/Indicator/indicatorData.d.ts +1 -0
  12. package/dist/engine/renderers/Indicator/indicatorData.d.ts.map +1 -1
  13. package/dist/engine/renderers/Indicator/indicatorData.js +1 -1
  14. package/dist/engine/renderers/Indicator/indicatorData.js.map +1 -1
  15. package/dist/engine/renderers/webgl/candleSurface.js +47 -47
  16. package/dist/engine/subPaneManager.d.ts +4 -0
  17. package/dist/engine/subPaneManager.d.ts.map +1 -1
  18. package/dist/engine/subPaneManager.js +13 -0
  19. package/dist/engine/subPaneManager.js.map +1 -1
  20. package/dist/version.d.ts +1 -1
  21. package/dist/version.d.ts.map +1 -1
  22. package/dist/version.js +1 -2
  23. package/dist/version.js.map +1 -1
  24. package/package.json +129 -122
  25. package/src/__tests__/signal.test.ts +124 -124
  26. package/src/config/chartSettings.ts +66 -66
  27. package/src/controllers/__tests__/drawing.test.ts +214 -214
  28. package/src/controllers/__tests__/indicatorSelector.test.ts +481 -481
  29. package/src/controllers/__tests__/toolbar.test.ts +225 -225
  30. package/src/controllers/createChartController.ts +665 -665
  31. package/src/controllers/createDrawingController.ts +96 -96
  32. package/src/controllers/createIndicatorSelectorController.ts +307 -307
  33. package/src/controllers/createToolbarController.ts +146 -146
  34. package/src/controllers/index.ts +20 -19
  35. package/src/controllers/types.ts +284 -284
  36. package/src/engine/__tests__/chart.dpr.test.ts +401 -401
  37. package/src/engine/__tests__/paneRenderer.resize.test.ts +92 -92
  38. package/src/engine/chart-store.ts +121 -121
  39. package/src/engine/chart.d.ts +617 -617
  40. package/src/engine/chart.ts +2803 -2815
  41. package/src/engine/controller/__tests__/interaction.dpr.test.ts +259 -259
  42. package/src/engine/controller/interaction.ts +722 -722
  43. package/src/engine/controller/markerInteraction.ts +130 -130
  44. package/src/engine/controller/pinchTracker.ts +82 -82
  45. package/src/engine/controller/tooltipPosition.ts +48 -48
  46. package/src/engine/draw/__tests__/pixelAlign.spec.ts +176 -176
  47. package/src/engine/draw/pixelAlign.ts +259 -259
  48. package/src/engine/drawing/index.ts +655 -655
  49. package/src/engine/drawing/interaction.ts +842 -842
  50. package/src/engine/drawing/plugin.ts +343 -343
  51. package/src/engine/indicators/__tests__/__fixtures__/golden/atr.json +38 -38
  52. package/src/engine/indicators/__tests__/__fixtures__/golden/dema.json +14 -14
  53. package/src/engine/indicators/__tests__/__fixtures__/golden/hma.json +14 -14
  54. package/src/engine/indicators/__tests__/__fixtures__/golden/index.ts +55 -55
  55. package/src/engine/indicators/__tests__/__fixtures__/golden/kama.json +14 -14
  56. package/src/engine/indicators/__tests__/__fixtures__/golden/tema.json +14 -14
  57. package/src/engine/indicators/__tests__/__fixtures__/golden/wma.json +40 -40
  58. package/src/engine/indicators/__tests__/__fixtures__/synthetic.ts +65 -65
  59. package/src/engine/indicators/__tests__/_propertyAssertions.ts +76 -76
  60. package/src/engine/indicators/__tests__/atr.test.ts +153 -153
  61. package/src/engine/indicators/__tests__/calculators.test.ts +614 -614
  62. package/src/engine/indicators/__tests__/cmf-mfi.test.ts +100 -100
  63. package/src/engine/indicators/__tests__/dema.test.ts +73 -73
  64. package/src/engine/indicators/__tests__/donchian.test.ts +70 -70
  65. package/src/engine/indicators/__tests__/hma.test.ts +73 -73
  66. package/src/engine/indicators/__tests__/ichimoku.test.ts +105 -105
  67. package/src/engine/indicators/__tests__/kama.test.ts +80 -80
  68. package/src/engine/indicators/__tests__/keltner.test.ts +65 -65
  69. package/src/engine/indicators/__tests__/pivot-fib.test.ts +110 -110
  70. package/src/engine/indicators/__tests__/roc.test.ts +68 -68
  71. package/src/engine/indicators/__tests__/sar.test.ts +86 -86
  72. package/src/engine/indicators/__tests__/scheduler.test.ts +831 -831
  73. package/src/engine/indicators/__tests__/soa.test.ts +533 -533
  74. package/src/engine/indicators/__tests__/structure.test.ts +110 -110
  75. package/src/engine/indicators/__tests__/supertrend.test.ts +65 -65
  76. package/src/engine/indicators/__tests__/tema.test.ts +68 -68
  77. package/src/engine/indicators/__tests__/trix.test.ts +70 -70
  78. package/src/engine/indicators/__tests__/volatility.test.ts +117 -117
  79. package/src/engine/indicators/__tests__/volume.test.ts +115 -115
  80. package/src/engine/indicators/__tests__/volumeProfile.test.ts +74 -74
  81. package/src/engine/indicators/__tests__/vwap.test.ts +69 -69
  82. package/src/engine/indicators/__tests__/wma.test.ts +112 -112
  83. package/src/engine/indicators/__tests__/zones.test.ts +95 -95
  84. package/src/engine/indicators/atrState.ts +27 -27
  85. package/src/engine/indicators/bollState.ts +51 -51
  86. package/src/engine/indicators/calculators.ts +2593 -2593
  87. package/src/engine/indicators/cciState.ts +25 -25
  88. package/src/engine/indicators/chaikinVolState.ts +32 -32
  89. package/src/engine/indicators/cmfState.ts +27 -27
  90. package/src/engine/indicators/demaState.ts +27 -27
  91. package/src/engine/indicators/donchianState.ts +43 -43
  92. package/src/engine/indicators/eneState.ts +43 -43
  93. package/src/engine/indicators/expmaState.ts +43 -43
  94. package/src/engine/indicators/fastkState.ts +25 -25
  95. package/src/engine/indicators/fibState.ts +41 -41
  96. package/src/engine/indicators/hmaState.ts +27 -27
  97. package/src/engine/indicators/hvState.ts +28 -28
  98. package/src/engine/indicators/ichimokuState.ts +70 -70
  99. package/src/engine/indicators/indicator.worker.ts +169 -169
  100. package/src/engine/indicators/indicatorDefinitionRegistry.ts +62 -62
  101. package/src/engine/indicators/indicatorMetadata.ts +110 -110
  102. package/src/engine/indicators/indicatorRegistry.ts +106 -106
  103. package/src/engine/indicators/indicatorRuntime.ts +1548 -1548
  104. package/src/engine/indicators/kamaState.ts +34 -34
  105. package/src/engine/indicators/keltnerState.ts +49 -49
  106. package/src/engine/indicators/kstState.ts +42 -42
  107. package/src/engine/indicators/maState.ts +36 -36
  108. package/src/engine/indicators/macdState.ts +76 -76
  109. package/src/engine/indicators/mfiState.ts +27 -27
  110. package/src/engine/indicators/momState.ts +25 -25
  111. package/src/engine/indicators/obvState.ts +25 -25
  112. package/src/engine/indicators/parkinsonState.ts +28 -28
  113. package/src/engine/indicators/pivotState.ts +51 -51
  114. package/src/engine/indicators/pvtState.ts +25 -25
  115. package/src/engine/indicators/rocState.ts +27 -27
  116. package/src/engine/indicators/rsiState.ts +65 -65
  117. package/src/engine/indicators/sarState.ts +41 -41
  118. package/src/engine/indicators/scheduler.ts +1205 -1205
  119. package/src/engine/indicators/soa.ts +352 -352
  120. package/src/engine/indicators/stateComposer.ts +1262 -1262
  121. package/src/engine/indicators/stochState.ts +26 -26
  122. package/src/engine/indicators/structureState.ts +69 -69
  123. package/src/engine/indicators/supertrendState.ts +37 -37
  124. package/src/engine/indicators/temaState.ts +27 -27
  125. package/src/engine/indicators/trixState.ts +35 -35
  126. package/src/engine/indicators/vmaState.ts +27 -27
  127. package/src/engine/indicators/volumeProfileState.ts +63 -63
  128. package/src/engine/indicators/vwapState.ts +29 -29
  129. package/src/engine/indicators/wmaState.ts +27 -27
  130. package/src/engine/indicators/wmsrState.ts +25 -25
  131. package/src/engine/indicators/workerProtocol.ts +613 -613
  132. package/src/engine/indicators/zonesState.ts +47 -47
  133. package/src/engine/layout/pane.ts +161 -161
  134. package/src/engine/marker/registry.ts +265 -265
  135. package/src/engine/paneRenderer.ts +169 -169
  136. package/src/engine/renderers/Indicator/atr.ts +237 -237
  137. package/src/engine/renderers/Indicator/boll.ts +317 -317
  138. package/src/engine/renderers/Indicator/cci.ts +275 -275
  139. package/src/engine/renderers/Indicator/chaikinVol.ts +138 -138
  140. package/src/engine/renderers/Indicator/cmf.ts +137 -137
  141. package/src/engine/renderers/Indicator/dema.ts +136 -136
  142. package/src/engine/renderers/Indicator/donchian.ts +137 -137
  143. package/src/engine/renderers/Indicator/ene.ts +271 -271
  144. package/src/engine/renderers/Indicator/expma.ts +197 -197
  145. package/src/engine/renderers/Indicator/fastk.ts +316 -316
  146. package/src/engine/renderers/Indicator/fib.ts +141 -141
  147. package/src/engine/renderers/Indicator/hma.ts +136 -136
  148. package/src/engine/renderers/Indicator/hv.ts +124 -124
  149. package/src/engine/renderers/Indicator/ichimoku.ts +181 -181
  150. package/src/engine/renderers/Indicator/index.ts +241 -241
  151. package/src/engine/renderers/Indicator/indicatorData.ts +650 -650
  152. package/src/engine/renderers/Indicator/kama.ts +136 -136
  153. package/src/engine/renderers/Indicator/keltner.ts +137 -137
  154. package/src/engine/renderers/Indicator/kst.ts +302 -302
  155. package/src/engine/renderers/Indicator/ma.ts +200 -200
  156. package/src/engine/renderers/Indicator/macd.ts +477 -477
  157. package/src/engine/renderers/Indicator/macdLegend.ts +141 -141
  158. package/src/engine/renderers/Indicator/mainIndicatorLegend.ts +272 -272
  159. package/src/engine/renderers/Indicator/mfi.ts +142 -142
  160. package/src/engine/renderers/Indicator/mom.ts +311 -311
  161. package/src/engine/renderers/Indicator/obv.ts +123 -123
  162. package/src/engine/renderers/Indicator/parkinson.ts +124 -124
  163. package/src/engine/renderers/Indicator/pivot.ts +131 -131
  164. package/src/engine/renderers/Indicator/pvt.ts +123 -123
  165. package/src/engine/renderers/Indicator/roc.ts +143 -143
  166. package/src/engine/renderers/Indicator/rsi.ts +390 -390
  167. package/src/engine/renderers/Indicator/sar.ts +113 -113
  168. package/src/engine/renderers/Indicator/scale/atr_scale.ts +19 -19
  169. package/src/engine/renderers/Indicator/scale/cci_scale.ts +19 -19
  170. package/src/engine/renderers/Indicator/scale/fastk_scale.ts +19 -19
  171. package/src/engine/renderers/Indicator/scale/indicator_scale.ts +204 -204
  172. package/src/engine/renderers/Indicator/scale/kst_scale.ts +19 -19
  173. package/src/engine/renderers/Indicator/scale/macd_scale.ts +22 -22
  174. package/src/engine/renderers/Indicator/scale/mom_scale.ts +19 -19
  175. package/src/engine/renderers/Indicator/scale/rsi_scale.ts +19 -19
  176. package/src/engine/renderers/Indicator/scale/stoch_scale.ts +19 -19
  177. package/src/engine/renderers/Indicator/scale/volume_scale.ts +26 -26
  178. package/src/engine/renderers/Indicator/scale/wmsr_scale.ts +19 -19
  179. package/src/engine/renderers/Indicator/stoch.ts +359 -359
  180. package/src/engine/renderers/Indicator/structure.ts +126 -126
  181. package/src/engine/renderers/Indicator/subPaneConfig.ts +265 -265
  182. package/src/engine/renderers/Indicator/supertrend.ts +115 -115
  183. package/src/engine/renderers/Indicator/tema.ts +136 -136
  184. package/src/engine/renderers/Indicator/trix.ts +158 -158
  185. package/src/engine/renderers/Indicator/vma.ts +124 -124
  186. package/src/engine/renderers/Indicator/volumeProfile.ts +125 -125
  187. package/src/engine/renderers/Indicator/vwap.ts +123 -123
  188. package/src/engine/renderers/Indicator/wma.ts +136 -136
  189. package/src/engine/renderers/Indicator/wmsr.ts +328 -328
  190. package/src/engine/renderers/Indicator/zones.ts +104 -104
  191. package/src/engine/renderers/__tests__/boll.renderer.test.ts +314 -314
  192. package/src/engine/renderers/__tests__/ene.renderer.test.ts +305 -305
  193. package/src/engine/renderers/__tests__/expma.renderer.test.ts +279 -279
  194. package/src/engine/renderers/__tests__/ma.renderer.test.ts +426 -426
  195. package/src/engine/renderers/__tests__/mainIndicatorLegend.renderer.test.ts +502 -502
  196. package/src/engine/renderers/__tests__/yAxis.renderer.test.ts +173 -173
  197. package/src/engine/renderers/candle.ts +459 -459
  198. package/src/engine/renderers/crosshair.ts +69 -69
  199. package/src/engine/renderers/customMarkers.ts +162 -162
  200. package/src/engine/renderers/extremaMarkers.ts +246 -246
  201. package/src/engine/renderers/gridLines.ts +90 -90
  202. package/src/engine/renderers/lastPrice.ts +97 -97
  203. package/src/engine/renderers/paneTitle.ts +136 -136
  204. package/src/engine/renderers/subVolume.ts +236 -236
  205. package/src/engine/renderers/timeAxis.ts +121 -121
  206. package/src/engine/renderers/webgl/candleSurface.ts +955 -955
  207. package/src/engine/renderers/webgl/sharedWebGLSurface.ts +146 -146
  208. package/src/engine/renderers/yAxis.ts +105 -105
  209. package/src/engine/scale/__tests__/logFormula.spec.ts +148 -148
  210. package/src/engine/scale/logFormula.ts +130 -130
  211. package/src/engine/scale/price.ts +39 -39
  212. package/src/engine/scale/priceScale.ts +264 -264
  213. package/src/engine/subPaneManager.ts +442 -427
  214. package/src/engine/theme/colors.ts +642 -642
  215. package/src/engine/theme/fonts.ts +20 -20
  216. package/src/engine/utils/klineConfig.ts +49 -49
  217. package/src/engine/utils/tickCount.ts +11 -11
  218. package/src/engine/utils/tickPosition.ts +214 -214
  219. package/src/engine/utils/zoom.ts +83 -83
  220. package/src/engine/viewport/viewport.ts +67 -67
  221. package/src/index.ts +3 -3
  222. package/src/plugin/ConfigManager.ts +93 -93
  223. package/src/plugin/EventBus.ts +77 -77
  224. package/src/plugin/HookSystem.ts +106 -106
  225. package/src/plugin/PluginHost.ts +243 -243
  226. package/src/plugin/PluginRegistry.ts +92 -92
  227. package/src/plugin/StateStore.ts +73 -73
  228. package/src/plugin/index.ts +19 -19
  229. package/src/plugin/rendererPluginManager.ts +368 -368
  230. package/src/plugin/stateKeys.ts +8 -8
  231. package/src/plugin/types.ts +526 -526
  232. package/src/reactivity/index.ts +2 -2
  233. package/src/reactivity/signal.ts +119 -119
  234. package/src/semantic/controller.ts +251 -251
  235. package/src/semantic/drawShape.ts +260 -260
  236. package/src/semantic/index.ts +28 -28
  237. package/src/semantic/schema.json +256 -256
  238. package/src/semantic/types.ts +251 -251
  239. package/src/semantic/validator.ts +349 -349
  240. package/src/types/kLine.ts +13 -13
  241. package/src/types/price.ts +56 -56
  242. package/src/types/volumePrice.ts +33 -33
  243. package/src/utils/dateFormat.ts +208 -208
  244. package/src/utils/kLineDraw/axis.ts +562 -562
  245. package/src/utils/priceToY.ts +34 -34
  246. package/src/utils/volumePrice.ts +202 -202
  247. package/src/version.ts +1 -1
@@ -1,69 +1,69 @@
1
- import type { RendererPlugin, RenderContext } from '../../plugin'
2
- import { RENDERER_PRIORITY, GLOBAL_PANE_ID } from '../../plugin'
3
- import { createHorizontalLineRect, createVerticalLineRect } from '../draw/pixelAlign'
4
- import { getColors } from '../theme/colors'
5
-
6
- /**
7
- * 创建十字线渲染器插件
8
- * 垂直线绘制到所有面板,水平线只绘制到活跃面板
9
- */
10
- export function createCrosshairRendererPlugin(options: {
11
- getCrosshairState: () => {
12
- pos: { x: number; y: number } | null
13
- activePaneId: string | null
14
- isDragging: boolean
15
- /** 十字线指向的价格(用于价格轴平移时跟随) */
16
- price: number | null
17
- }
18
- }): RendererPlugin {
19
- return {
20
- name: 'crosshair',
21
- version: '1.0.0',
22
- description: '十字线渲染器',
23
- debugName: '十字线',
24
- paneId: GLOBAL_PANE_ID,
25
- priority: RENDERER_PRIORITY.SYSTEM_CROSSHAIR,
26
- layer: 'overlay',
27
-
28
- draw(context: RenderContext) {
29
- const { pane, dpr, paneWidth, overlayCtx } = context
30
- const colors = getColors(context.theme)
31
- const state = options.getCrosshairState()
32
-
33
- if (state.isDragging || !state.pos) return
34
-
35
- const { x } = state.pos
36
- const isActive = pane.id === state.activePaneId
37
-
38
- // 使用价格计算 Y 坐标(支持价格轴平移)
39
- let localY = -1
40
- if (isActive && state.price !== null) {
41
- localY = pane.yAxis.priceToY(state.price)
42
- }
43
-
44
- // 优先使用 overlayCtx,若不存在则跳过(不回落到主画布)
45
- const ctx = overlayCtx
46
- if (!ctx) return
47
-
48
- ctx.save()
49
- ctx.beginPath()
50
- ctx.rect(0, 0, paneWidth, pane.height)
51
- ctx.clip()
52
-
53
- ctx.fillStyle = colors.CROSSHAIR.LINE
54
-
55
- // 绘制垂直线
56
- const v = createVerticalLineRect(x, 0, pane.height, dpr)
57
- if (v) ctx.fillRect(v.x, v.y, v.width, v.height)
58
-
59
- // 绘制水平线(仅在活跃面板)
60
- if (isActive && localY >= 0) {
61
- const safeY = Math.min(localY, pane.height - 1 / dpr)
62
- const h = createHorizontalLineRect(0, paneWidth, safeY, dpr)
63
- if (h) ctx.fillRect(h.x, h.y, h.width, h.height)
64
- }
65
-
66
- ctx.restore()
67
- },
68
- }
69
- }
1
+ import type { RendererPlugin, RenderContext } from '../../plugin'
2
+ import { RENDERER_PRIORITY, GLOBAL_PANE_ID } from '../../plugin'
3
+ import { createHorizontalLineRect, createVerticalLineRect } from '../draw/pixelAlign'
4
+ import { getColors } from '../theme/colors'
5
+
6
+ /**
7
+ * 创建十字线渲染器插件
8
+ * 垂直线绘制到所有面板,水平线只绘制到活跃面板
9
+ */
10
+ export function createCrosshairRendererPlugin(options: {
11
+ getCrosshairState: () => {
12
+ pos: { x: number; y: number } | null
13
+ activePaneId: string | null
14
+ isDragging: boolean
15
+ /** 十字线指向的价格(用于价格轴平移时跟随) */
16
+ price: number | null
17
+ }
18
+ }): RendererPlugin {
19
+ return {
20
+ name: 'crosshair',
21
+ version: '1.0.0',
22
+ description: '十字线渲染器',
23
+ debugName: '十字线',
24
+ paneId: GLOBAL_PANE_ID,
25
+ priority: RENDERER_PRIORITY.SYSTEM_CROSSHAIR,
26
+ layer: 'overlay',
27
+
28
+ draw(context: RenderContext) {
29
+ const { pane, dpr, paneWidth, overlayCtx } = context
30
+ const colors = getColors(context.theme)
31
+ const state = options.getCrosshairState()
32
+
33
+ if (state.isDragging || !state.pos) return
34
+
35
+ const { x } = state.pos
36
+ const isActive = pane.id === state.activePaneId
37
+
38
+ // 使用价格计算 Y 坐标(支持价格轴平移)
39
+ let localY = -1
40
+ if (isActive && state.price !== null) {
41
+ localY = pane.yAxis.priceToY(state.price)
42
+ }
43
+
44
+ // 优先使用 overlayCtx,若不存在则跳过(不回落到主画布)
45
+ const ctx = overlayCtx
46
+ if (!ctx) return
47
+
48
+ ctx.save()
49
+ ctx.beginPath()
50
+ ctx.rect(0, 0, paneWidth, pane.height)
51
+ ctx.clip()
52
+
53
+ ctx.fillStyle = colors.CROSSHAIR.LINE
54
+
55
+ // 绘制垂直线
56
+ const v = createVerticalLineRect(x, 0, pane.height, dpr)
57
+ if (v) ctx.fillRect(v.x, v.y, v.width, v.height)
58
+
59
+ // 绘制水平线(仅在活跃面板)
60
+ if (isActive && localY >= 0) {
61
+ const safeY = Math.min(localY, pane.height - 1 / dpr)
62
+ const h = createHorizontalLineRect(0, paneWidth, safeY, dpr)
63
+ if (h) ctx.fillRect(h.x, h.y, h.width, h.height)
64
+ }
65
+
66
+ ctx.restore()
67
+ },
68
+ }
69
+ }
@@ -1,162 +1,162 @@
1
- import type { RendererPlugin, RenderContext, MarkerManagerLike } from '../../plugin'
2
- import { RENDERER_PRIORITY, GLOBAL_PANE_ID } from '../../plugin'
3
- import type { KLineData } from '../../types/price'
4
- import type { CustomMarkerEntity, CustomMarkerShape } from '../marker/registry'
5
- import { drawShape, drawLabel, hitTestShape } from '../../semantic/drawShape'
6
- import { roundToPhysicalPixel } from '../draw/pixelAlign'
7
-
8
- /** 默认标记尺寸(相对于 K 线宽度的缩放因子) */
9
- const DEFAULT_SIZE_SCALE = 1.2
10
- /** 最大标记尺寸(像素) */
11
- const MAX_MARKER_SIZE = 24
12
- /** 最小标记尺寸(像素) */
13
- const MIN_MARKER_SIZE = 6
14
-
15
- /** 根据形状判断默认渲染位置(true = 上方,false = 下方) */
16
- function isShapeRenderAboveKLine(shape: CustomMarkerShape): boolean {
17
- switch (shape) {
18
- case 'arrow_up':
19
- case 'flag':
20
- return true // 上涨信号、重要事件 → 默认在K线上方
21
- case 'arrow_down':
22
- case 'circle':
23
- case 'rectangle':
24
- case 'diamond':
25
- default:
26
- return false // 下跌信号、普通标记 → 默认在K线下方
27
- }
28
- }
29
-
30
- /**
31
- * 创建自定义标记渲染器插件
32
- * 负责渲染 semanticConfig 中配置的 customMarkers
33
- */
34
- export function createCustomMarkersRenderer(): RendererPlugin {
35
- return {
36
- name: 'customMarkers',
37
- version: '1.0.0',
38
- description: '自定义标记渲染器',
39
- debugName: '自定义标记',
40
- paneId: GLOBAL_PANE_ID,
41
- priority: RENDERER_PRIORITY.OVERLAY,
42
-
43
- draw(context: RenderContext): void {
44
- const { ctx, pane, data, range, scrollLeft, kWidth, kLineCenters, dpr, markerManager, zoomLevel } = context
45
- if (!markerManager) return
46
- if ((zoomLevel ?? 1) < 2) return
47
-
48
- const customMarkers = markerManager.getCustomMarkers() as CustomMarkerEntity[]
49
- if (!customMarkers || customMarkers.length === 0) return
50
-
51
- const klineData = data as KLineData[]
52
- if (!klineData.length) return
53
- if (pane.role !== 'price') return
54
-
55
- ctx.save()
56
- ctx.translate(-scrollLeft, 0)
57
-
58
- for (const marker of customMarkers) {
59
- // 1. timestamp → index(直接在原数组上二分查找)
60
- const kIndex = findIndexByTimestamp(klineData, marker.timestamp)
61
- if (kIndex === -1) continue
62
-
63
- // 2. 检查是否在可视区域内
64
- if (kIndex < range.start || kIndex >= range.end) continue
65
-
66
- // 3. 计算像素坐标
67
- const posIndex = kIndex - range.start
68
- if (posIndex < 0 || posIndex >= kLineCenters.length) continue
69
-
70
- const pixelX = kLineCenters[posIndex]!
71
-
72
- const kData = klineData[kIndex]!
73
- const userSize = marker.style?.size
74
- const actualSize = calculateMarkerSize(kWidth, userSize)
75
-
76
- // 4. 判断标记渲染位置(上方或下方)
77
- // 如果 offset.y < 0,说明用户想渲染在上方;否则根据形状默认判断
78
- const isAboveKLine = (marker.offset?.y ?? 0) < 0
79
- ? true
80
- : (marker.offset?.y ?? 0) > 0
81
- ? false
82
- : isShapeRenderAboveKLine(marker.shape)
83
-
84
- // 5. 计算 Y 坐标
85
- let pixelY: number
86
- if (isAboveKLine) {
87
- // 在K线上方(基于 high)
88
- const highY = pane.yAxis.priceToY(kData.high)
89
- pixelY = highY - actualSize / 2 - 4 + (marker.offset?.y ?? 0)
90
- } else {
91
- // 在K线下方(基于 low)
92
- const lowY = pane.yAxis.priceToY(kData.low)
93
- pixelY = lowY + actualSize / 2 + 4 + (marker.offset?.y ?? 0)
94
- }
95
-
96
- // 6. 应用 X 偏移
97
- const finalX = pixelX + (marker.offset?.x ?? 0)
98
- const finalY = pixelY
99
-
100
- // 7. 物理像素对齐
101
- // X: kLineCenters 已对齐,直接使用;Y: priceToY 是浮点数,需要对齐
102
- const alignedX = finalX
103
- const alignedY = finalY
104
- // 确保 size*2*dpr 为偶数,使 size/2 在物理像素上为整数
105
- const alignedSize = Math.round(actualSize * dpr / 2) * 2 / dpr
106
-
107
- // 8. 绘制形状和标签(使用对齐后的坐标和尺寸)
108
- drawShape(ctx, marker.shape, alignedX, alignedY, alignedSize, marker.style || {})
109
- if (marker.label) {
110
- // 标签位置:标记在K线上方 → 文字在标记上方;标记在K线下方 → 文字在标记下方
111
- drawLabel(ctx, marker.label, alignedX, alignedY, alignedSize, marker.style || {}, isAboveKLine)
112
- }
113
-
114
- // 9. 记录位置和实际大小用于 hitTest(使用对齐后的值)
115
- markerManager.setCustomMarkerPosition(marker.id, alignedX - scrollLeft, alignedY, alignedSize, marker.shape)
116
- }
117
-
118
- ctx.restore()
119
- },
120
- }
121
- }
122
-
123
- /**
124
- * 计算标记实际显示大小
125
- * @param kWidth K线宽度
126
- * @param userSize 用户配置的最大尺寸(可选)
127
- * @returns 实际显示大小(像素)
128
- */
129
- function calculateMarkerSize(kWidth: number, userSize?: number): number {
130
- // 基于 kWidth 的基准大小
131
- const baseSize = kWidth * DEFAULT_SIZE_SCALE
132
-
133
- // 如果用户指定了最大尺寸,则限制不超过该值
134
- const maxSize = userSize ?? MAX_MARKER_SIZE
135
-
136
- // 最终大小:取基准大小和最大值中的较小者,但不小于最小值
137
- return Math.max(MIN_MARKER_SIZE, Math.min(baseSize, maxSize))
138
- }
139
-
140
- /**
141
- * 直接在有序数据上二分查找 timestamp 对应的 index
142
- * 避免每帧分配新数组
143
- */
144
- function findIndexByTimestamp(data: KLineData[], targetTs: number): number {
145
- let left = 0
146
- let right = data.length - 1
147
-
148
- while (left <= right) {
149
- const mid = Math.floor((left + right) / 2)
150
- const midTs = data[mid]?.timestamp
151
- if (midTs === undefined) break
152
-
153
- if (midTs === targetTs) return mid
154
- if (midTs < targetTs) {
155
- left = mid + 1
156
- } else {
157
- right = mid - 1
158
- }
159
- }
160
-
161
- return -1
162
- }
1
+ import type { RendererPlugin, RenderContext, MarkerManagerLike } from '../../plugin'
2
+ import { RENDERER_PRIORITY, GLOBAL_PANE_ID } from '../../plugin'
3
+ import type { KLineData } from '../../types/price'
4
+ import type { CustomMarkerEntity, CustomMarkerShape } from '../marker/registry'
5
+ import { drawShape, drawLabel, hitTestShape } from '../../semantic/drawShape'
6
+ import { roundToPhysicalPixel } from '../draw/pixelAlign'
7
+
8
+ /** 默认标记尺寸(相对于 K 线宽度的缩放因子) */
9
+ const DEFAULT_SIZE_SCALE = 1.2
10
+ /** 最大标记尺寸(像素) */
11
+ const MAX_MARKER_SIZE = 24
12
+ /** 最小标记尺寸(像素) */
13
+ const MIN_MARKER_SIZE = 6
14
+
15
+ /** 根据形状判断默认渲染位置(true = 上方,false = 下方) */
16
+ function isShapeRenderAboveKLine(shape: CustomMarkerShape): boolean {
17
+ switch (shape) {
18
+ case 'arrow_up':
19
+ case 'flag':
20
+ return true // 上涨信号、重要事件 → 默认在K线上方
21
+ case 'arrow_down':
22
+ case 'circle':
23
+ case 'rectangle':
24
+ case 'diamond':
25
+ default:
26
+ return false // 下跌信号、普通标记 → 默认在K线下方
27
+ }
28
+ }
29
+
30
+ /**
31
+ * 创建自定义标记渲染器插件
32
+ * 负责渲染 semanticConfig 中配置的 customMarkers
33
+ */
34
+ export function createCustomMarkersRenderer(): RendererPlugin {
35
+ return {
36
+ name: 'customMarkers',
37
+ version: '1.0.0',
38
+ description: '自定义标记渲染器',
39
+ debugName: '自定义标记',
40
+ paneId: GLOBAL_PANE_ID,
41
+ priority: RENDERER_PRIORITY.OVERLAY,
42
+
43
+ draw(context: RenderContext): void {
44
+ const { ctx, pane, data, range, scrollLeft, kWidth, kLineCenters, dpr, markerManager, zoomLevel } = context
45
+ if (!markerManager) return
46
+ if ((zoomLevel ?? 1) < 2) return
47
+
48
+ const customMarkers = markerManager.getCustomMarkers() as CustomMarkerEntity[]
49
+ if (!customMarkers || customMarkers.length === 0) return
50
+
51
+ const klineData = data as KLineData[]
52
+ if (!klineData.length) return
53
+ if (pane.role !== 'price') return
54
+
55
+ ctx.save()
56
+ ctx.translate(-scrollLeft, 0)
57
+
58
+ for (const marker of customMarkers) {
59
+ // 1. timestamp → index(直接在原数组上二分查找)
60
+ const kIndex = findIndexByTimestamp(klineData, marker.timestamp)
61
+ if (kIndex === -1) continue
62
+
63
+ // 2. 检查是否在可视区域内
64
+ if (kIndex < range.start || kIndex >= range.end) continue
65
+
66
+ // 3. 计算像素坐标
67
+ const posIndex = kIndex - range.start
68
+ if (posIndex < 0 || posIndex >= kLineCenters.length) continue
69
+
70
+ const pixelX = kLineCenters[posIndex]!
71
+
72
+ const kData = klineData[kIndex]!
73
+ const userSize = marker.style?.size
74
+ const actualSize = calculateMarkerSize(kWidth, userSize)
75
+
76
+ // 4. 判断标记渲染位置(上方或下方)
77
+ // 如果 offset.y < 0,说明用户想渲染在上方;否则根据形状默认判断
78
+ const isAboveKLine = (marker.offset?.y ?? 0) < 0
79
+ ? true
80
+ : (marker.offset?.y ?? 0) > 0
81
+ ? false
82
+ : isShapeRenderAboveKLine(marker.shape)
83
+
84
+ // 5. 计算 Y 坐标
85
+ let pixelY: number
86
+ if (isAboveKLine) {
87
+ // 在K线上方(基于 high)
88
+ const highY = pane.yAxis.priceToY(kData.high)
89
+ pixelY = highY - actualSize / 2 - 4 + (marker.offset?.y ?? 0)
90
+ } else {
91
+ // 在K线下方(基于 low)
92
+ const lowY = pane.yAxis.priceToY(kData.low)
93
+ pixelY = lowY + actualSize / 2 + 4 + (marker.offset?.y ?? 0)
94
+ }
95
+
96
+ // 6. 应用 X 偏移
97
+ const finalX = pixelX + (marker.offset?.x ?? 0)
98
+ const finalY = pixelY
99
+
100
+ // 7. 物理像素对齐
101
+ // X: kLineCenters 已对齐,直接使用;Y: priceToY 是浮点数,需要对齐
102
+ const alignedX = finalX
103
+ const alignedY = finalY
104
+ // 确保 size*2*dpr 为偶数,使 size/2 在物理像素上为整数
105
+ const alignedSize = Math.round(actualSize * dpr / 2) * 2 / dpr
106
+
107
+ // 8. 绘制形状和标签(使用对齐后的坐标和尺寸)
108
+ drawShape(ctx, marker.shape, alignedX, alignedY, alignedSize, marker.style || {})
109
+ if (marker.label) {
110
+ // 标签位置:标记在K线上方 → 文字在标记上方;标记在K线下方 → 文字在标记下方
111
+ drawLabel(ctx, marker.label, alignedX, alignedY, alignedSize, marker.style || {}, isAboveKLine)
112
+ }
113
+
114
+ // 9. 记录位置和实际大小用于 hitTest(使用对齐后的值)
115
+ markerManager.setCustomMarkerPosition(marker.id, alignedX - scrollLeft, alignedY, alignedSize, marker.shape)
116
+ }
117
+
118
+ ctx.restore()
119
+ },
120
+ }
121
+ }
122
+
123
+ /**
124
+ * 计算标记实际显示大小
125
+ * @param kWidth K线宽度
126
+ * @param userSize 用户配置的最大尺寸(可选)
127
+ * @returns 实际显示大小(像素)
128
+ */
129
+ function calculateMarkerSize(kWidth: number, userSize?: number): number {
130
+ // 基于 kWidth 的基准大小
131
+ const baseSize = kWidth * DEFAULT_SIZE_SCALE
132
+
133
+ // 如果用户指定了最大尺寸,则限制不超过该值
134
+ const maxSize = userSize ?? MAX_MARKER_SIZE
135
+
136
+ // 最终大小:取基准大小和最大值中的较小者,但不小于最小值
137
+ return Math.max(MIN_MARKER_SIZE, Math.min(baseSize, maxSize))
138
+ }
139
+
140
+ /**
141
+ * 直接在有序数据上二分查找 timestamp 对应的 index
142
+ * 避免每帧分配新数组
143
+ */
144
+ function findIndexByTimestamp(data: KLineData[], targetTs: number): number {
145
+ let left = 0
146
+ let right = data.length - 1
147
+
148
+ while (left <= right) {
149
+ const mid = Math.floor((left + right) / 2)
150
+ const midTs = data[mid]?.timestamp
151
+ if (midTs === undefined) break
152
+
153
+ if (midTs === targetTs) return mid
154
+ if (midTs < targetTs) {
155
+ left = mid + 1
156
+ } else {
157
+ right = mid - 1
158
+ }
159
+ }
160
+
161
+ return -1
162
+ }