@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.
- package/README.md +201 -201
- package/README.zh-CN.md +201 -201
- package/dist/controllers/index.d.ts +1 -0
- package/dist/controllers/index.d.ts.map +1 -1
- package/dist/controllers/index.js +1 -0
- package/dist/controllers/index.js.map +1 -1
- package/dist/engine/chart.d.ts +11 -19
- package/dist/engine/chart.d.ts.map +1 -1
- package/dist/engine/chart.js +92 -109
- package/dist/engine/chart.js.map +1 -1
- package/dist/engine/renderers/Indicator/indicatorData.d.ts +1 -0
- package/dist/engine/renderers/Indicator/indicatorData.d.ts.map +1 -1
- package/dist/engine/renderers/Indicator/indicatorData.js +1 -1
- package/dist/engine/renderers/Indicator/indicatorData.js.map +1 -1
- package/dist/engine/renderers/webgl/candleSurface.js +47 -47
- package/dist/engine/subPaneManager.d.ts +4 -0
- package/dist/engine/subPaneManager.d.ts.map +1 -1
- package/dist/engine/subPaneManager.js +13 -0
- package/dist/engine/subPaneManager.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.d.ts.map +1 -1
- package/dist/version.js +1 -2
- package/dist/version.js.map +1 -1
- package/package.json +129 -122
- package/src/__tests__/signal.test.ts +124 -124
- package/src/config/chartSettings.ts +66 -66
- package/src/controllers/__tests__/drawing.test.ts +214 -214
- package/src/controllers/__tests__/indicatorSelector.test.ts +481 -481
- package/src/controllers/__tests__/toolbar.test.ts +225 -225
- package/src/controllers/createChartController.ts +665 -665
- package/src/controllers/createDrawingController.ts +96 -96
- package/src/controllers/createIndicatorSelectorController.ts +307 -307
- package/src/controllers/createToolbarController.ts +146 -146
- package/src/controllers/index.ts +20 -19
- package/src/controllers/types.ts +284 -284
- package/src/engine/__tests__/chart.dpr.test.ts +401 -401
- package/src/engine/__tests__/paneRenderer.resize.test.ts +92 -92
- package/src/engine/chart-store.ts +121 -121
- package/src/engine/chart.d.ts +617 -617
- package/src/engine/chart.ts +2803 -2815
- package/src/engine/controller/__tests__/interaction.dpr.test.ts +259 -259
- package/src/engine/controller/interaction.ts +722 -722
- package/src/engine/controller/markerInteraction.ts +130 -130
- package/src/engine/controller/pinchTracker.ts +82 -82
- package/src/engine/controller/tooltipPosition.ts +48 -48
- package/src/engine/draw/__tests__/pixelAlign.spec.ts +176 -176
- package/src/engine/draw/pixelAlign.ts +259 -259
- package/src/engine/drawing/index.ts +655 -655
- package/src/engine/drawing/interaction.ts +842 -842
- package/src/engine/drawing/plugin.ts +343 -343
- package/src/engine/indicators/__tests__/__fixtures__/golden/atr.json +38 -38
- package/src/engine/indicators/__tests__/__fixtures__/golden/dema.json +14 -14
- package/src/engine/indicators/__tests__/__fixtures__/golden/hma.json +14 -14
- package/src/engine/indicators/__tests__/__fixtures__/golden/index.ts +55 -55
- package/src/engine/indicators/__tests__/__fixtures__/golden/kama.json +14 -14
- package/src/engine/indicators/__tests__/__fixtures__/golden/tema.json +14 -14
- package/src/engine/indicators/__tests__/__fixtures__/golden/wma.json +40 -40
- package/src/engine/indicators/__tests__/__fixtures__/synthetic.ts +65 -65
- package/src/engine/indicators/__tests__/_propertyAssertions.ts +76 -76
- package/src/engine/indicators/__tests__/atr.test.ts +153 -153
- package/src/engine/indicators/__tests__/calculators.test.ts +614 -614
- package/src/engine/indicators/__tests__/cmf-mfi.test.ts +100 -100
- package/src/engine/indicators/__tests__/dema.test.ts +73 -73
- package/src/engine/indicators/__tests__/donchian.test.ts +70 -70
- package/src/engine/indicators/__tests__/hma.test.ts +73 -73
- package/src/engine/indicators/__tests__/ichimoku.test.ts +105 -105
- package/src/engine/indicators/__tests__/kama.test.ts +80 -80
- package/src/engine/indicators/__tests__/keltner.test.ts +65 -65
- package/src/engine/indicators/__tests__/pivot-fib.test.ts +110 -110
- package/src/engine/indicators/__tests__/roc.test.ts +68 -68
- package/src/engine/indicators/__tests__/sar.test.ts +86 -86
- package/src/engine/indicators/__tests__/scheduler.test.ts +831 -831
- package/src/engine/indicators/__tests__/soa.test.ts +533 -533
- package/src/engine/indicators/__tests__/structure.test.ts +110 -110
- package/src/engine/indicators/__tests__/supertrend.test.ts +65 -65
- package/src/engine/indicators/__tests__/tema.test.ts +68 -68
- package/src/engine/indicators/__tests__/trix.test.ts +70 -70
- package/src/engine/indicators/__tests__/volatility.test.ts +117 -117
- package/src/engine/indicators/__tests__/volume.test.ts +115 -115
- package/src/engine/indicators/__tests__/volumeProfile.test.ts +74 -74
- package/src/engine/indicators/__tests__/vwap.test.ts +69 -69
- package/src/engine/indicators/__tests__/wma.test.ts +112 -112
- package/src/engine/indicators/__tests__/zones.test.ts +95 -95
- package/src/engine/indicators/atrState.ts +27 -27
- package/src/engine/indicators/bollState.ts +51 -51
- package/src/engine/indicators/calculators.ts +2593 -2593
- package/src/engine/indicators/cciState.ts +25 -25
- package/src/engine/indicators/chaikinVolState.ts +32 -32
- package/src/engine/indicators/cmfState.ts +27 -27
- package/src/engine/indicators/demaState.ts +27 -27
- package/src/engine/indicators/donchianState.ts +43 -43
- package/src/engine/indicators/eneState.ts +43 -43
- package/src/engine/indicators/expmaState.ts +43 -43
- package/src/engine/indicators/fastkState.ts +25 -25
- package/src/engine/indicators/fibState.ts +41 -41
- package/src/engine/indicators/hmaState.ts +27 -27
- package/src/engine/indicators/hvState.ts +28 -28
- package/src/engine/indicators/ichimokuState.ts +70 -70
- package/src/engine/indicators/indicator.worker.ts +169 -169
- package/src/engine/indicators/indicatorDefinitionRegistry.ts +62 -62
- package/src/engine/indicators/indicatorMetadata.ts +110 -110
- package/src/engine/indicators/indicatorRegistry.ts +106 -106
- package/src/engine/indicators/indicatorRuntime.ts +1548 -1548
- package/src/engine/indicators/kamaState.ts +34 -34
- package/src/engine/indicators/keltnerState.ts +49 -49
- package/src/engine/indicators/kstState.ts +42 -42
- package/src/engine/indicators/maState.ts +36 -36
- package/src/engine/indicators/macdState.ts +76 -76
- package/src/engine/indicators/mfiState.ts +27 -27
- package/src/engine/indicators/momState.ts +25 -25
- package/src/engine/indicators/obvState.ts +25 -25
- package/src/engine/indicators/parkinsonState.ts +28 -28
- package/src/engine/indicators/pivotState.ts +51 -51
- package/src/engine/indicators/pvtState.ts +25 -25
- package/src/engine/indicators/rocState.ts +27 -27
- package/src/engine/indicators/rsiState.ts +65 -65
- package/src/engine/indicators/sarState.ts +41 -41
- package/src/engine/indicators/scheduler.ts +1205 -1205
- package/src/engine/indicators/soa.ts +352 -352
- package/src/engine/indicators/stateComposer.ts +1262 -1262
- package/src/engine/indicators/stochState.ts +26 -26
- package/src/engine/indicators/structureState.ts +69 -69
- package/src/engine/indicators/supertrendState.ts +37 -37
- package/src/engine/indicators/temaState.ts +27 -27
- package/src/engine/indicators/trixState.ts +35 -35
- package/src/engine/indicators/vmaState.ts +27 -27
- package/src/engine/indicators/volumeProfileState.ts +63 -63
- package/src/engine/indicators/vwapState.ts +29 -29
- package/src/engine/indicators/wmaState.ts +27 -27
- package/src/engine/indicators/wmsrState.ts +25 -25
- package/src/engine/indicators/workerProtocol.ts +613 -613
- package/src/engine/indicators/zonesState.ts +47 -47
- package/src/engine/layout/pane.ts +161 -161
- package/src/engine/marker/registry.ts +265 -265
- package/src/engine/paneRenderer.ts +169 -169
- package/src/engine/renderers/Indicator/atr.ts +237 -237
- package/src/engine/renderers/Indicator/boll.ts +317 -317
- package/src/engine/renderers/Indicator/cci.ts +275 -275
- package/src/engine/renderers/Indicator/chaikinVol.ts +138 -138
- package/src/engine/renderers/Indicator/cmf.ts +137 -137
- package/src/engine/renderers/Indicator/dema.ts +136 -136
- package/src/engine/renderers/Indicator/donchian.ts +137 -137
- package/src/engine/renderers/Indicator/ene.ts +271 -271
- package/src/engine/renderers/Indicator/expma.ts +197 -197
- package/src/engine/renderers/Indicator/fastk.ts +316 -316
- package/src/engine/renderers/Indicator/fib.ts +141 -141
- package/src/engine/renderers/Indicator/hma.ts +136 -136
- package/src/engine/renderers/Indicator/hv.ts +124 -124
- package/src/engine/renderers/Indicator/ichimoku.ts +181 -181
- package/src/engine/renderers/Indicator/index.ts +241 -241
- package/src/engine/renderers/Indicator/indicatorData.ts +650 -650
- package/src/engine/renderers/Indicator/kama.ts +136 -136
- package/src/engine/renderers/Indicator/keltner.ts +137 -137
- package/src/engine/renderers/Indicator/kst.ts +302 -302
- package/src/engine/renderers/Indicator/ma.ts +200 -200
- package/src/engine/renderers/Indicator/macd.ts +477 -477
- package/src/engine/renderers/Indicator/macdLegend.ts +141 -141
- package/src/engine/renderers/Indicator/mainIndicatorLegend.ts +272 -272
- package/src/engine/renderers/Indicator/mfi.ts +142 -142
- package/src/engine/renderers/Indicator/mom.ts +311 -311
- package/src/engine/renderers/Indicator/obv.ts +123 -123
- package/src/engine/renderers/Indicator/parkinson.ts +124 -124
- package/src/engine/renderers/Indicator/pivot.ts +131 -131
- package/src/engine/renderers/Indicator/pvt.ts +123 -123
- package/src/engine/renderers/Indicator/roc.ts +143 -143
- package/src/engine/renderers/Indicator/rsi.ts +390 -390
- package/src/engine/renderers/Indicator/sar.ts +113 -113
- package/src/engine/renderers/Indicator/scale/atr_scale.ts +19 -19
- package/src/engine/renderers/Indicator/scale/cci_scale.ts +19 -19
- package/src/engine/renderers/Indicator/scale/fastk_scale.ts +19 -19
- package/src/engine/renderers/Indicator/scale/indicator_scale.ts +204 -204
- package/src/engine/renderers/Indicator/scale/kst_scale.ts +19 -19
- package/src/engine/renderers/Indicator/scale/macd_scale.ts +22 -22
- package/src/engine/renderers/Indicator/scale/mom_scale.ts +19 -19
- package/src/engine/renderers/Indicator/scale/rsi_scale.ts +19 -19
- package/src/engine/renderers/Indicator/scale/stoch_scale.ts +19 -19
- package/src/engine/renderers/Indicator/scale/volume_scale.ts +26 -26
- package/src/engine/renderers/Indicator/scale/wmsr_scale.ts +19 -19
- package/src/engine/renderers/Indicator/stoch.ts +359 -359
- package/src/engine/renderers/Indicator/structure.ts +126 -126
- package/src/engine/renderers/Indicator/subPaneConfig.ts +265 -265
- package/src/engine/renderers/Indicator/supertrend.ts +115 -115
- package/src/engine/renderers/Indicator/tema.ts +136 -136
- package/src/engine/renderers/Indicator/trix.ts +158 -158
- package/src/engine/renderers/Indicator/vma.ts +124 -124
- package/src/engine/renderers/Indicator/volumeProfile.ts +125 -125
- package/src/engine/renderers/Indicator/vwap.ts +123 -123
- package/src/engine/renderers/Indicator/wma.ts +136 -136
- package/src/engine/renderers/Indicator/wmsr.ts +328 -328
- package/src/engine/renderers/Indicator/zones.ts +104 -104
- package/src/engine/renderers/__tests__/boll.renderer.test.ts +314 -314
- package/src/engine/renderers/__tests__/ene.renderer.test.ts +305 -305
- package/src/engine/renderers/__tests__/expma.renderer.test.ts +279 -279
- package/src/engine/renderers/__tests__/ma.renderer.test.ts +426 -426
- package/src/engine/renderers/__tests__/mainIndicatorLegend.renderer.test.ts +502 -502
- package/src/engine/renderers/__tests__/yAxis.renderer.test.ts +173 -173
- package/src/engine/renderers/candle.ts +459 -459
- package/src/engine/renderers/crosshair.ts +69 -69
- package/src/engine/renderers/customMarkers.ts +162 -162
- package/src/engine/renderers/extremaMarkers.ts +246 -246
- package/src/engine/renderers/gridLines.ts +90 -90
- package/src/engine/renderers/lastPrice.ts +97 -97
- package/src/engine/renderers/paneTitle.ts +136 -136
- package/src/engine/renderers/subVolume.ts +236 -236
- package/src/engine/renderers/timeAxis.ts +121 -121
- package/src/engine/renderers/webgl/candleSurface.ts +955 -955
- package/src/engine/renderers/webgl/sharedWebGLSurface.ts +146 -146
- package/src/engine/renderers/yAxis.ts +105 -105
- package/src/engine/scale/__tests__/logFormula.spec.ts +148 -148
- package/src/engine/scale/logFormula.ts +130 -130
- package/src/engine/scale/price.ts +39 -39
- package/src/engine/scale/priceScale.ts +264 -264
- package/src/engine/subPaneManager.ts +442 -427
- package/src/engine/theme/colors.ts +642 -642
- package/src/engine/theme/fonts.ts +20 -20
- package/src/engine/utils/klineConfig.ts +49 -49
- package/src/engine/utils/tickCount.ts +11 -11
- package/src/engine/utils/tickPosition.ts +214 -214
- package/src/engine/utils/zoom.ts +83 -83
- package/src/engine/viewport/viewport.ts +67 -67
- package/src/index.ts +3 -3
- package/src/plugin/ConfigManager.ts +93 -93
- package/src/plugin/EventBus.ts +77 -77
- package/src/plugin/HookSystem.ts +106 -106
- package/src/plugin/PluginHost.ts +243 -243
- package/src/plugin/PluginRegistry.ts +92 -92
- package/src/plugin/StateStore.ts +73 -73
- package/src/plugin/index.ts +19 -19
- package/src/plugin/rendererPluginManager.ts +368 -368
- package/src/plugin/stateKeys.ts +8 -8
- package/src/plugin/types.ts +526 -526
- package/src/reactivity/index.ts +2 -2
- package/src/reactivity/signal.ts +119 -119
- package/src/semantic/controller.ts +251 -251
- package/src/semantic/drawShape.ts +260 -260
- package/src/semantic/index.ts +28 -28
- package/src/semantic/schema.json +256 -256
- package/src/semantic/types.ts +251 -251
- package/src/semantic/validator.ts +349 -349
- package/src/types/kLine.ts +13 -13
- package/src/types/price.ts +56 -56
- package/src/types/volumePrice.ts +33 -33
- package/src/utils/dateFormat.ts +208 -208
- package/src/utils/kLineDraw/axis.ts +562 -562
- package/src/utils/priceToY.ts +34 -34
- package/src/utils/volumePrice.ts +202 -202
- 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
|
+
}
|