@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,20 +1,20 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* 图表字体配置
|
|
3
|
-
* 字体栈:Trebuchet MS (Windows系统字体) -> Roboto -> Ubuntu -> 通用无衬线字体
|
|
4
|
-
* 通过 Google Fonts CDN 加载 Roboto 和 Ubuntu
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
/** 数字和文本的标准字体栈 */
|
|
8
|
-
export const FONT_FAMILY = '"Trebuchet MS", Roboto, Ubuntu, sans-serif'
|
|
9
|
-
|
|
10
|
-
/** 获取指定字号的字体字符串,用于 Canvas ctx.font */
|
|
11
|
-
export function getFont(size: number, options?: { bold?: boolean }): string {
|
|
12
|
-
const weight = options?.bold ? 'bold ' : ''
|
|
13
|
-
return `${weight}${size}px ${FONT_FAMILY}`
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
export function setCanvasFont(ctx: CanvasRenderingContext2D, font: string): void {
|
|
17
|
-
if (ctx.font !== font) {
|
|
18
|
-
ctx.font = font
|
|
19
|
-
}
|
|
20
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* 图表字体配置
|
|
3
|
+
* 字体栈:Trebuchet MS (Windows系统字体) -> Roboto -> Ubuntu -> 通用无衬线字体
|
|
4
|
+
* 通过 Google Fonts CDN 加载 Roboto 和 Ubuntu
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
/** 数字和文本的标准字体栈 */
|
|
8
|
+
export const FONT_FAMILY = '"Trebuchet MS", Roboto, Ubuntu, sans-serif'
|
|
9
|
+
|
|
10
|
+
/** 获取指定字号的字体字符串,用于 Canvas ctx.font */
|
|
11
|
+
export function getFont(size: number, options?: { bold?: boolean }): string {
|
|
12
|
+
const weight = options?.bold ? 'bold ' : ''
|
|
13
|
+
return `${weight}${size}px ${FONT_FAMILY}`
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function setCanvasFont(ctx: CanvasRenderingContext2D, font: string): void {
|
|
17
|
+
if (ctx.font !== font) {
|
|
18
|
+
ctx.font = font
|
|
19
|
+
}
|
|
20
|
+
}
|
|
@@ -1,49 +1,49 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* K 线物理像素配置工具函数
|
|
3
|
-
* 用于统一渲染、交互、视口计算的坐标系统
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* 计算奇数化后的 K 线宽度(物理像素),确保影线能完美居中显示
|
|
8
|
-
* @param kWidth K 线宽度(逻辑像素)
|
|
9
|
-
* @param dpr 设备像素比
|
|
10
|
-
* @returns 奇数化后的物理像素宽度
|
|
11
|
-
*/
|
|
12
|
-
export function calcKWidthPx(kWidth: number, dpr: number): number {
|
|
13
|
-
let kWidthPx = Math.round(kWidth * dpr)
|
|
14
|
-
if (kWidthPx % 2 === 0) {
|
|
15
|
-
kWidthPx += 1
|
|
16
|
-
}
|
|
17
|
-
return Math.max(1, kWidthPx)
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
/**
|
|
21
|
-
* 获取图表渲染使用的物理像素配置
|
|
22
|
-
* @param kWidth K 线宽度(逻辑像素)
|
|
23
|
-
* @param kGap K 线间隙(逻辑像素)
|
|
24
|
-
* @param dpr 设备像素比
|
|
25
|
-
* @returns 物理像素和逻辑像素的配置对象
|
|
26
|
-
*/
|
|
27
|
-
export function getPhysicalKLineConfig(kWidth: number, kGap: number, dpr: number) {
|
|
28
|
-
const kWidthPx = calcKWidthPx(kWidth, dpr)
|
|
29
|
-
const kGapPx = Math.round(kGap * dpr)
|
|
30
|
-
const unitPx = kWidthPx + kGapPx
|
|
31
|
-
const startXPx = kGapPx
|
|
32
|
-
|
|
33
|
-
// 转回逻辑像素(供需要逻辑像素的地方使用)
|
|
34
|
-
const kWidthLogical = kWidthPx / dpr
|
|
35
|
-
const kGapLogical = kGapPx / dpr
|
|
36
|
-
const unitLogical = unitPx / dpr
|
|
37
|
-
const startXLogical = startXPx / dpr
|
|
38
|
-
|
|
39
|
-
return {
|
|
40
|
-
kWidthPx,
|
|
41
|
-
kGapPx,
|
|
42
|
-
unitPx,
|
|
43
|
-
startXPx,
|
|
44
|
-
kWidthLogical,
|
|
45
|
-
kGapLogical,
|
|
46
|
-
unitLogical,
|
|
47
|
-
startXLogical,
|
|
48
|
-
}
|
|
49
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* K 线物理像素配置工具函数
|
|
3
|
+
* 用于统一渲染、交互、视口计算的坐标系统
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* 计算奇数化后的 K 线宽度(物理像素),确保影线能完美居中显示
|
|
8
|
+
* @param kWidth K 线宽度(逻辑像素)
|
|
9
|
+
* @param dpr 设备像素比
|
|
10
|
+
* @returns 奇数化后的物理像素宽度
|
|
11
|
+
*/
|
|
12
|
+
export function calcKWidthPx(kWidth: number, dpr: number): number {
|
|
13
|
+
let kWidthPx = Math.round(kWidth * dpr)
|
|
14
|
+
if (kWidthPx % 2 === 0) {
|
|
15
|
+
kWidthPx += 1
|
|
16
|
+
}
|
|
17
|
+
return Math.max(1, kWidthPx)
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* 获取图表渲染使用的物理像素配置
|
|
22
|
+
* @param kWidth K 线宽度(逻辑像素)
|
|
23
|
+
* @param kGap K 线间隙(逻辑像素)
|
|
24
|
+
* @param dpr 设备像素比
|
|
25
|
+
* @returns 物理像素和逻辑像素的配置对象
|
|
26
|
+
*/
|
|
27
|
+
export function getPhysicalKLineConfig(kWidth: number, kGap: number, dpr: number) {
|
|
28
|
+
const kWidthPx = calcKWidthPx(kWidth, dpr)
|
|
29
|
+
const kGapPx = Math.round(kGap * dpr)
|
|
30
|
+
const unitPx = kWidthPx + kGapPx
|
|
31
|
+
const startXPx = kGapPx
|
|
32
|
+
|
|
33
|
+
// 转回逻辑像素(供需要逻辑像素的地方使用)
|
|
34
|
+
const kWidthLogical = kWidthPx / dpr
|
|
35
|
+
const kGapLogical = kGapPx / dpr
|
|
36
|
+
const unitLogical = unitPx / dpr
|
|
37
|
+
const startXLogical = startXPx / dpr
|
|
38
|
+
|
|
39
|
+
return {
|
|
40
|
+
kWidthPx,
|
|
41
|
+
kGapPx,
|
|
42
|
+
unitPx,
|
|
43
|
+
startXPx,
|
|
44
|
+
kWidthLogical,
|
|
45
|
+
kGapLogical,
|
|
46
|
+
unitLogical,
|
|
47
|
+
startXLogical,
|
|
48
|
+
}
|
|
49
|
+
}
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* 根据面板高度计算网格线/价格标签数量
|
|
3
|
-
* 刻度间距固定约 60px
|
|
4
|
-
* @param height 面板高度(逻辑像素)
|
|
5
|
-
* @param isMain 是否为主图面板(暂未使用,保留参数兼容)
|
|
6
|
-
* @returns tick 数量
|
|
7
|
-
*/
|
|
8
|
-
export function calculateTickCount(height: number, isMain?: boolean): number {
|
|
9
|
-
const tickSpacing = 60 // 固定间距
|
|
10
|
-
return Math.max(3, Math.round(height / tickSpacing) + 1)
|
|
11
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* 根据面板高度计算网格线/价格标签数量
|
|
3
|
+
* 刻度间距固定约 60px
|
|
4
|
+
* @param height 面板高度(逻辑像素)
|
|
5
|
+
* @param isMain 是否为主图面板(暂未使用,保留参数兼容)
|
|
6
|
+
* @returns tick 数量
|
|
7
|
+
*/
|
|
8
|
+
export function calculateTickCount(height: number, isMain?: boolean): number {
|
|
9
|
+
const tickSpacing = 60 // 固定间距
|
|
10
|
+
return Math.max(3, Math.round(height / tickSpacing) + 1)
|
|
11
|
+
}
|
|
@@ -1,214 +1,214 @@
|
|
|
1
|
-
import { calculateTickCount } from './tickCount'
|
|
2
|
-
import { fromLog, logFormulaForPriceRange, toLog } from '../scale/logFormula'
|
|
3
|
-
|
|
4
|
-
export interface TickPosition {
|
|
5
|
-
index: number
|
|
6
|
-
t: number
|
|
7
|
-
y: number
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
export type ScaleType = 'linear' | 'log'
|
|
11
|
-
|
|
12
|
-
export interface CalculateTickPositionsOptions {
|
|
13
|
-
height: number
|
|
14
|
-
paddingTop: number
|
|
15
|
-
paddingBottom: number
|
|
16
|
-
isMain?: boolean
|
|
17
|
-
hideEdgeTicks?: boolean
|
|
18
|
-
scaleType?: ScaleType
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
export function calculateTickPositions(
|
|
22
|
-
options: CalculateTickPositionsOptions
|
|
23
|
-
): TickPosition[] {
|
|
24
|
-
const { height, paddingTop, paddingBottom, isMain, hideEdgeTicks } = options
|
|
25
|
-
|
|
26
|
-
const yStart = paddingTop
|
|
27
|
-
const yEnd = Math.max(paddingTop, height - paddingBottom)
|
|
28
|
-
const viewH = Math.max(0, yEnd - yStart)
|
|
29
|
-
|
|
30
|
-
const ticks = calculateTickCount(height, isMain)
|
|
31
|
-
const positions: TickPosition[] = []
|
|
32
|
-
|
|
33
|
-
for (let i = 0; i < ticks; i++) {
|
|
34
|
-
if (hideEdgeTicks && (i === 0 || i === ticks - 1)) continue
|
|
35
|
-
|
|
36
|
-
const t = ticks <= 1 ? 0 : i / (ticks - 1)
|
|
37
|
-
const y = yStart + t * viewH
|
|
38
|
-
|
|
39
|
-
positions.push({ index: i, t, y })
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
return positions
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
export interface TickPositionWithValue extends TickPosition {
|
|
46
|
-
value: number
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
export interface CalculateValueTickPositionsOptions extends CalculateTickPositionsOptions {
|
|
50
|
-
valueMin: number
|
|
51
|
-
valueMax: number
|
|
52
|
-
logBase?: number
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
const LOG_EPSILON = 1e-10
|
|
56
|
-
const LOG_ROUND_CANDIDATES = [1, 1.2, 1.5, 2, 2.5, 3, 4, 5, 6, 7.5, 8, 10]
|
|
57
|
-
|
|
58
|
-
function roundLogTickValue(value: number): number {
|
|
59
|
-
if (!Number.isFinite(value) || value <= 0) {
|
|
60
|
-
return value
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
const exp = Math.floor(Math.log10(value))
|
|
64
|
-
const magnitude = Math.pow(10, exp)
|
|
65
|
-
const normalized = value / magnitude
|
|
66
|
-
|
|
67
|
-
let best = LOG_ROUND_CANDIDATES[0]
|
|
68
|
-
let minDiff = Infinity
|
|
69
|
-
for (const candidate of LOG_ROUND_CANDIDATES) {
|
|
70
|
-
const diff = Math.abs(candidate - normalized)
|
|
71
|
-
if (diff < minDiff) {
|
|
72
|
-
minDiff = diff
|
|
73
|
-
best = candidate
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
return best * magnitude
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
function generatePixelUniformLogTicks(options: {
|
|
81
|
-
height: number
|
|
82
|
-
paddingTop: number
|
|
83
|
-
paddingBottom: number
|
|
84
|
-
valueMin: number
|
|
85
|
-
valueMax: number
|
|
86
|
-
targetCount: number
|
|
87
|
-
}): TickPositionWithValue[] {
|
|
88
|
-
const { height, paddingTop, paddingBottom, valueMin, valueMax, targetCount } = options
|
|
89
|
-
|
|
90
|
-
const effectiveMin = Math.max(valueMin, LOG_EPSILON)
|
|
91
|
-
const effectiveMax = Math.max(valueMax, effectiveMin * 1.001)
|
|
92
|
-
const yStart = paddingTop
|
|
93
|
-
const yEnd = Math.max(paddingTop, height - paddingBottom)
|
|
94
|
-
const viewH = Math.max(0, yEnd - yStart)
|
|
95
|
-
|
|
96
|
-
if (viewH <= 0) {
|
|
97
|
-
return []
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
const formula = logFormulaForPriceRange({ minPrice: effectiveMin, maxPrice: effectiveMax })
|
|
101
|
-
const logMin = toLog(effectiveMin, formula)
|
|
102
|
-
const logMax = toLog(effectiveMax, formula)
|
|
103
|
-
const logRange = logMax - logMin
|
|
104
|
-
|
|
105
|
-
if (!Number.isFinite(logRange) || Math.abs(logRange) < 1e-10) {
|
|
106
|
-
return []
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
const total = Math.max(2, targetCount)
|
|
110
|
-
const spacingPx = total <= 1 ? viewH : viewH / (total - 1)
|
|
111
|
-
const minSpacingPx = spacingPx * 0.6
|
|
112
|
-
const maxRoundShiftPx = spacingPx * 0.15
|
|
113
|
-
|
|
114
|
-
const positions: TickPositionWithValue[] = []
|
|
115
|
-
let prevY: number | null = null
|
|
116
|
-
|
|
117
|
-
for (let i = 0; i < total; i++) {
|
|
118
|
-
const idealT = total <= 1 ? 0 : i / (total - 1)
|
|
119
|
-
const idealY = yStart + idealT * viewH
|
|
120
|
-
const logical = logMax - idealT * logRange
|
|
121
|
-
const rawValue = fromLog(logical, formula)
|
|
122
|
-
|
|
123
|
-
let value = rawValue
|
|
124
|
-
let t = idealT
|
|
125
|
-
let y = idealY
|
|
126
|
-
|
|
127
|
-
const roundedValue = roundLogTickValue(rawValue)
|
|
128
|
-
if (Number.isFinite(roundedValue) && roundedValue > 0) {
|
|
129
|
-
const roundedLogical = toLog(roundedValue, formula)
|
|
130
|
-
const roundedT = (logMax - roundedLogical) / logRange
|
|
131
|
-
const roundedY = yStart + roundedT * viewH
|
|
132
|
-
|
|
133
|
-
if (Number.isFinite(roundedY) && Math.abs(roundedY - idealY) <= maxRoundShiftPx) {
|
|
134
|
-
value = roundedValue
|
|
135
|
-
t = roundedT
|
|
136
|
-
y = roundedY
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
if (y < yStart - 0.5 || y > yEnd + 0.5) {
|
|
141
|
-
continue
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
if (prevY !== null && Math.abs(y - prevY) < minSpacingPx) {
|
|
145
|
-
continue
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
positions.push({ index: positions.length, t, y, value })
|
|
149
|
-
prevY = y
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
return positions
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
// calculateValueTickPositions 缓存
|
|
156
|
-
let _cvtpCacheKey = ''
|
|
157
|
-
let _cvtpCacheResult: TickPositionWithValue[] = []
|
|
158
|
-
|
|
159
|
-
function buildCvtpCacheKey(options: CalculateValueTickPositionsOptions): string {
|
|
160
|
-
return `${options.valueMin}:${options.valueMax}:${options.height}:${options.paddingTop}:${options.paddingBottom}:${options.isMain}:${options.scaleType ?? 'linear'}:${options.hideEdgeTicks ?? false}`
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
export function calculateValueTickPositions(
|
|
164
|
-
options: CalculateValueTickPositionsOptions
|
|
165
|
-
): TickPositionWithValue[] {
|
|
166
|
-
// 缓存命中:所有参数相同则直接返回
|
|
167
|
-
const key = buildCvtpCacheKey(options)
|
|
168
|
-
if (key === _cvtpCacheKey) return _cvtpCacheResult
|
|
169
|
-
|
|
170
|
-
const { valueMin, valueMax, scaleType = 'linear' } = options
|
|
171
|
-
|
|
172
|
-
if (scaleType === 'log') {
|
|
173
|
-
if (valueMin <= 0) {
|
|
174
|
-
// 递归调用走线性路径,线性路径会更新缓存
|
|
175
|
-
return calculateValueTickPositions({ ...options, scaleType: 'linear' })
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
const effectiveMin = Math.max(valueMin, LOG_EPSILON)
|
|
179
|
-
const effectiveMax = Math.max(valueMax, effectiveMin * 1.001)
|
|
180
|
-
const { height, paddingTop, paddingBottom, isMain, hideEdgeTicks } = options
|
|
181
|
-
const targetCount = Math.max(2, calculateTickCount(height, isMain))
|
|
182
|
-
|
|
183
|
-
let positions = generatePixelUniformLogTicks({
|
|
184
|
-
height,
|
|
185
|
-
paddingTop,
|
|
186
|
-
paddingBottom,
|
|
187
|
-
valueMin: effectiveMin,
|
|
188
|
-
valueMax: effectiveMax,
|
|
189
|
-
targetCount,
|
|
190
|
-
})
|
|
191
|
-
|
|
192
|
-
if (hideEdgeTicks && positions.length > 2) {
|
|
193
|
-
positions = positions.slice(1, -1)
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
const result = positions.map((position, index) => ({ ...position, index }))
|
|
197
|
-
_cvtpCacheKey = key
|
|
198
|
-
_cvtpCacheResult = result
|
|
199
|
-
return result
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
const basePositions = calculateTickPositions(options)
|
|
203
|
-
const totalTicks = calculateTickCount(options.height, options.isMain)
|
|
204
|
-
const valueRange = valueMax - valueMin || 1
|
|
205
|
-
|
|
206
|
-
const result = basePositions.map((pos) => {
|
|
207
|
-
const step = valueRange / Math.max(1, totalTicks - 1)
|
|
208
|
-
const value = valueMax - step * pos.index
|
|
209
|
-
return { ...pos, value }
|
|
210
|
-
})
|
|
211
|
-
_cvtpCacheKey = key
|
|
212
|
-
_cvtpCacheResult = result
|
|
213
|
-
return result
|
|
214
|
-
}
|
|
1
|
+
import { calculateTickCount } from './tickCount'
|
|
2
|
+
import { fromLog, logFormulaForPriceRange, toLog } from '../scale/logFormula'
|
|
3
|
+
|
|
4
|
+
export interface TickPosition {
|
|
5
|
+
index: number
|
|
6
|
+
t: number
|
|
7
|
+
y: number
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export type ScaleType = 'linear' | 'log'
|
|
11
|
+
|
|
12
|
+
export interface CalculateTickPositionsOptions {
|
|
13
|
+
height: number
|
|
14
|
+
paddingTop: number
|
|
15
|
+
paddingBottom: number
|
|
16
|
+
isMain?: boolean
|
|
17
|
+
hideEdgeTicks?: boolean
|
|
18
|
+
scaleType?: ScaleType
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function calculateTickPositions(
|
|
22
|
+
options: CalculateTickPositionsOptions
|
|
23
|
+
): TickPosition[] {
|
|
24
|
+
const { height, paddingTop, paddingBottom, isMain, hideEdgeTicks } = options
|
|
25
|
+
|
|
26
|
+
const yStart = paddingTop
|
|
27
|
+
const yEnd = Math.max(paddingTop, height - paddingBottom)
|
|
28
|
+
const viewH = Math.max(0, yEnd - yStart)
|
|
29
|
+
|
|
30
|
+
const ticks = calculateTickCount(height, isMain)
|
|
31
|
+
const positions: TickPosition[] = []
|
|
32
|
+
|
|
33
|
+
for (let i = 0; i < ticks; i++) {
|
|
34
|
+
if (hideEdgeTicks && (i === 0 || i === ticks - 1)) continue
|
|
35
|
+
|
|
36
|
+
const t = ticks <= 1 ? 0 : i / (ticks - 1)
|
|
37
|
+
const y = yStart + t * viewH
|
|
38
|
+
|
|
39
|
+
positions.push({ index: i, t, y })
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return positions
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export interface TickPositionWithValue extends TickPosition {
|
|
46
|
+
value: number
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export interface CalculateValueTickPositionsOptions extends CalculateTickPositionsOptions {
|
|
50
|
+
valueMin: number
|
|
51
|
+
valueMax: number
|
|
52
|
+
logBase?: number
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const LOG_EPSILON = 1e-10
|
|
56
|
+
const LOG_ROUND_CANDIDATES = [1, 1.2, 1.5, 2, 2.5, 3, 4, 5, 6, 7.5, 8, 10]
|
|
57
|
+
|
|
58
|
+
function roundLogTickValue(value: number): number {
|
|
59
|
+
if (!Number.isFinite(value) || value <= 0) {
|
|
60
|
+
return value
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const exp = Math.floor(Math.log10(value))
|
|
64
|
+
const magnitude = Math.pow(10, exp)
|
|
65
|
+
const normalized = value / magnitude
|
|
66
|
+
|
|
67
|
+
let best = LOG_ROUND_CANDIDATES[0]
|
|
68
|
+
let minDiff = Infinity
|
|
69
|
+
for (const candidate of LOG_ROUND_CANDIDATES) {
|
|
70
|
+
const diff = Math.abs(candidate - normalized)
|
|
71
|
+
if (diff < minDiff) {
|
|
72
|
+
minDiff = diff
|
|
73
|
+
best = candidate
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return best * magnitude
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function generatePixelUniformLogTicks(options: {
|
|
81
|
+
height: number
|
|
82
|
+
paddingTop: number
|
|
83
|
+
paddingBottom: number
|
|
84
|
+
valueMin: number
|
|
85
|
+
valueMax: number
|
|
86
|
+
targetCount: number
|
|
87
|
+
}): TickPositionWithValue[] {
|
|
88
|
+
const { height, paddingTop, paddingBottom, valueMin, valueMax, targetCount } = options
|
|
89
|
+
|
|
90
|
+
const effectiveMin = Math.max(valueMin, LOG_EPSILON)
|
|
91
|
+
const effectiveMax = Math.max(valueMax, effectiveMin * 1.001)
|
|
92
|
+
const yStart = paddingTop
|
|
93
|
+
const yEnd = Math.max(paddingTop, height - paddingBottom)
|
|
94
|
+
const viewH = Math.max(0, yEnd - yStart)
|
|
95
|
+
|
|
96
|
+
if (viewH <= 0) {
|
|
97
|
+
return []
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const formula = logFormulaForPriceRange({ minPrice: effectiveMin, maxPrice: effectiveMax })
|
|
101
|
+
const logMin = toLog(effectiveMin, formula)
|
|
102
|
+
const logMax = toLog(effectiveMax, formula)
|
|
103
|
+
const logRange = logMax - logMin
|
|
104
|
+
|
|
105
|
+
if (!Number.isFinite(logRange) || Math.abs(logRange) < 1e-10) {
|
|
106
|
+
return []
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
const total = Math.max(2, targetCount)
|
|
110
|
+
const spacingPx = total <= 1 ? viewH : viewH / (total - 1)
|
|
111
|
+
const minSpacingPx = spacingPx * 0.6
|
|
112
|
+
const maxRoundShiftPx = spacingPx * 0.15
|
|
113
|
+
|
|
114
|
+
const positions: TickPositionWithValue[] = []
|
|
115
|
+
let prevY: number | null = null
|
|
116
|
+
|
|
117
|
+
for (let i = 0; i < total; i++) {
|
|
118
|
+
const idealT = total <= 1 ? 0 : i / (total - 1)
|
|
119
|
+
const idealY = yStart + idealT * viewH
|
|
120
|
+
const logical = logMax - idealT * logRange
|
|
121
|
+
const rawValue = fromLog(logical, formula)
|
|
122
|
+
|
|
123
|
+
let value = rawValue
|
|
124
|
+
let t = idealT
|
|
125
|
+
let y = idealY
|
|
126
|
+
|
|
127
|
+
const roundedValue = roundLogTickValue(rawValue)
|
|
128
|
+
if (Number.isFinite(roundedValue) && roundedValue > 0) {
|
|
129
|
+
const roundedLogical = toLog(roundedValue, formula)
|
|
130
|
+
const roundedT = (logMax - roundedLogical) / logRange
|
|
131
|
+
const roundedY = yStart + roundedT * viewH
|
|
132
|
+
|
|
133
|
+
if (Number.isFinite(roundedY) && Math.abs(roundedY - idealY) <= maxRoundShiftPx) {
|
|
134
|
+
value = roundedValue
|
|
135
|
+
t = roundedT
|
|
136
|
+
y = roundedY
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
if (y < yStart - 0.5 || y > yEnd + 0.5) {
|
|
141
|
+
continue
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
if (prevY !== null && Math.abs(y - prevY) < minSpacingPx) {
|
|
145
|
+
continue
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
positions.push({ index: positions.length, t, y, value })
|
|
149
|
+
prevY = y
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
return positions
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// calculateValueTickPositions 缓存
|
|
156
|
+
let _cvtpCacheKey = ''
|
|
157
|
+
let _cvtpCacheResult: TickPositionWithValue[] = []
|
|
158
|
+
|
|
159
|
+
function buildCvtpCacheKey(options: CalculateValueTickPositionsOptions): string {
|
|
160
|
+
return `${options.valueMin}:${options.valueMax}:${options.height}:${options.paddingTop}:${options.paddingBottom}:${options.isMain}:${options.scaleType ?? 'linear'}:${options.hideEdgeTicks ?? false}`
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
export function calculateValueTickPositions(
|
|
164
|
+
options: CalculateValueTickPositionsOptions
|
|
165
|
+
): TickPositionWithValue[] {
|
|
166
|
+
// 缓存命中:所有参数相同则直接返回
|
|
167
|
+
const key = buildCvtpCacheKey(options)
|
|
168
|
+
if (key === _cvtpCacheKey) return _cvtpCacheResult
|
|
169
|
+
|
|
170
|
+
const { valueMin, valueMax, scaleType = 'linear' } = options
|
|
171
|
+
|
|
172
|
+
if (scaleType === 'log') {
|
|
173
|
+
if (valueMin <= 0) {
|
|
174
|
+
// 递归调用走线性路径,线性路径会更新缓存
|
|
175
|
+
return calculateValueTickPositions({ ...options, scaleType: 'linear' })
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
const effectiveMin = Math.max(valueMin, LOG_EPSILON)
|
|
179
|
+
const effectiveMax = Math.max(valueMax, effectiveMin * 1.001)
|
|
180
|
+
const { height, paddingTop, paddingBottom, isMain, hideEdgeTicks } = options
|
|
181
|
+
const targetCount = Math.max(2, calculateTickCount(height, isMain))
|
|
182
|
+
|
|
183
|
+
let positions = generatePixelUniformLogTicks({
|
|
184
|
+
height,
|
|
185
|
+
paddingTop,
|
|
186
|
+
paddingBottom,
|
|
187
|
+
valueMin: effectiveMin,
|
|
188
|
+
valueMax: effectiveMax,
|
|
189
|
+
targetCount,
|
|
190
|
+
})
|
|
191
|
+
|
|
192
|
+
if (hideEdgeTicks && positions.length > 2) {
|
|
193
|
+
positions = positions.slice(1, -1)
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
const result = positions.map((position, index) => ({ ...position, index }))
|
|
197
|
+
_cvtpCacheKey = key
|
|
198
|
+
_cvtpCacheResult = result
|
|
199
|
+
return result
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
const basePositions = calculateTickPositions(options)
|
|
203
|
+
const totalTicks = calculateTickCount(options.height, options.isMain)
|
|
204
|
+
const valueRange = valueMax - valueMin || 1
|
|
205
|
+
|
|
206
|
+
const result = basePositions.map((pos) => {
|
|
207
|
+
const step = valueRange / Math.max(1, totalTicks - 1)
|
|
208
|
+
const value = valueMax - step * pos.index
|
|
209
|
+
return { ...pos, value }
|
|
210
|
+
})
|
|
211
|
+
_cvtpCacheKey = key
|
|
212
|
+
_cvtpCacheResult = result
|
|
213
|
+
return result
|
|
214
|
+
}
|