@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,264 +1,264 @@
|
|
|
1
|
-
import type { PriceRange } from './price'
|
|
2
|
-
import type { ScaleType } from '../utils/tickPosition'
|
|
3
|
-
import {
|
|
4
|
-
type LogFormula,
|
|
5
|
-
toLog,
|
|
6
|
-
fromLog,
|
|
7
|
-
logFormulaForPriceRange,
|
|
8
|
-
logFormulasAreSame,
|
|
9
|
-
} from './logFormula'
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* Pane 级别的价格坐标系(价格 -> pane 内 Y)
|
|
13
|
-
* - y=0 在 pane 顶部,y=height 在 pane 底部
|
|
14
|
-
*/
|
|
15
|
-
export class PriceScale {
|
|
16
|
-
private range: PriceRange = { maxPrice: 100, minPrice: 0 }
|
|
17
|
-
private height = 1
|
|
18
|
-
private paddingTop = 0
|
|
19
|
-
private paddingBottom = 0
|
|
20
|
-
|
|
21
|
-
/** 价格偏移量(用于上下拖动平移价格轴)
|
|
22
|
-
* 在对数模式下,此偏移量为 log 空间偏移 */
|
|
23
|
-
private priceOffset = 0
|
|
24
|
-
|
|
25
|
-
/** 垂直缩放系数(1=默认,>1 放大,<1 缩小) */
|
|
26
|
-
private verticalScale = 1
|
|
27
|
-
|
|
28
|
-
/** 刻度类型:线性或对数 */
|
|
29
|
-
private scaleType: ScaleType = 'linear'
|
|
30
|
-
|
|
31
|
-
/** 对数变换公式(动态计算,适配极小价格) */
|
|
32
|
-
private logFormula: LogFormula = logFormulaForPriceRange(null)
|
|
33
|
-
|
|
34
|
-
setRange(r: PriceRange) {
|
|
35
|
-
this.range = r
|
|
36
|
-
if (this.scaleType === 'log' && r.minPrice > 0) {
|
|
37
|
-
const newFormula = logFormulaForPriceRange(r)
|
|
38
|
-
if (!logFormulasAreSame(newFormula, this.logFormula)) {
|
|
39
|
-
// 将旧公式的 log 偏移量转换到新公式空间
|
|
40
|
-
const oldLogMin = toLog(r.minPrice, this.logFormula)
|
|
41
|
-
const oldLogMax = toLog(r.maxPrice, this.logFormula)
|
|
42
|
-
const oldCenter = (oldLogMax + oldLogMin) / 2 + this.priceOffset
|
|
43
|
-
|
|
44
|
-
this.logFormula = newFormula
|
|
45
|
-
|
|
46
|
-
const newLogMin = toLog(r.minPrice, this.logFormula)
|
|
47
|
-
const newLogMax = toLog(r.maxPrice, this.logFormula)
|
|
48
|
-
const newBaseCenter = (newLogMax + newLogMin) / 2
|
|
49
|
-
this.priceOffset = this.clampOffset(oldCenter - newBaseCenter)
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
setHeight(h: number) {
|
|
55
|
-
this.height = Math.max(1, h)
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
setPadding(top: number, bottom: number) {
|
|
59
|
-
this.paddingTop = Math.max(0, top)
|
|
60
|
-
this.paddingBottom = Math.max(0, bottom)
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
getRange(): PriceRange {
|
|
64
|
-
return this.range
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
getPaddingTop(): number {
|
|
68
|
-
return this.paddingTop
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
getPaddingBottom(): number {
|
|
72
|
-
return this.paddingBottom
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
/**
|
|
76
|
-
* 设置价格偏移量
|
|
77
|
-
* @param offset 价格偏移
|
|
78
|
-
* - 线性模式:真实价格的线性偏移
|
|
79
|
-
* - 对数模式:log 空间的偏移量
|
|
80
|
-
*/
|
|
81
|
-
setPriceOffset(offset: number): void {
|
|
82
|
-
this.priceOffset = this.clampOffset(offset)
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
/**
|
|
86
|
-
* 获取当前价格偏移量
|
|
87
|
-
*/
|
|
88
|
-
getPriceOffset(): number {
|
|
89
|
-
return this.priceOffset
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
/**
|
|
93
|
-
* 重置价格偏移量
|
|
94
|
-
*/
|
|
95
|
-
resetPriceOffset(): void {
|
|
96
|
-
this.priceOffset = 0
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
/**
|
|
100
|
-
* 根据当前 range 和 verticalScale 对 priceOffset 进行 clamp,
|
|
101
|
-
* 防止视口完全离开数据范围。
|
|
102
|
-
*/
|
|
103
|
-
private clampOffset(offset: number): number {
|
|
104
|
-
if (this.scaleType === 'log' && this.range.minPrice > 0) {
|
|
105
|
-
const logMin = toLog(this.range.minPrice, this.logFormula)
|
|
106
|
-
const logMax = toLog(this.range.maxPrice, this.logFormula)
|
|
107
|
-
const logRange = logMax - logMin
|
|
108
|
-
if (logRange <= 0) return 0
|
|
109
|
-
const maxOffset = logRange * (1 + 1 / this.verticalScale) / 2
|
|
110
|
-
return Math.max(-maxOffset, Math.min(maxOffset, offset))
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
const rangeSize = this.range.maxPrice - this.range.minPrice
|
|
114
|
-
if (rangeSize <= 0) return 0
|
|
115
|
-
const maxOffset = rangeSize * (1 + 1 / this.verticalScale) / 2
|
|
116
|
-
return Math.max(-maxOffset, Math.min(maxOffset, offset))
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
/**
|
|
120
|
-
* 按拖拽位移缩放 Y 轴(deltaY < 0 放大,deltaY > 0 缩小)
|
|
121
|
-
*/
|
|
122
|
-
scaleByDelta(deltaY: number): void {
|
|
123
|
-
if (!Number.isFinite(deltaY) || deltaY === 0) return
|
|
124
|
-
const factor = Math.exp(-deltaY * 0.01)
|
|
125
|
-
const nextScale = this.verticalScale * factor
|
|
126
|
-
this.verticalScale = Math.min(8, Math.max(0.2, nextScale))
|
|
127
|
-
this.priceOffset = this.clampOffset(this.priceOffset)
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
getVerticalScale(): number {
|
|
131
|
-
return this.verticalScale
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
/**
|
|
135
|
-
* 设置刻度类型
|
|
136
|
-
* 切换时会自动转换 priceOffset 以保持视口位置
|
|
137
|
-
*/
|
|
138
|
-
setScaleType(type: ScaleType): void {
|
|
139
|
-
if (type === this.scaleType) return
|
|
140
|
-
|
|
141
|
-
if (type === 'log' && this.range.minPrice > 0) {
|
|
142
|
-
// 线性 → 对数:把线性偏移转为 log 空间偏移
|
|
143
|
-
this.logFormula = logFormulaForPriceRange(this.range)
|
|
144
|
-
const baseCenter = (this.range.maxPrice + this.range.minPrice) / 2
|
|
145
|
-
const realCenter = baseCenter + this.priceOffset
|
|
146
|
-
this.priceOffset = toLog(realCenter, this.logFormula) - toLog(baseCenter, this.logFormula)
|
|
147
|
-
this.priceOffset = this.clampOffset(this.priceOffset)
|
|
148
|
-
} else if (type === 'linear' && this.scaleType === 'log') {
|
|
149
|
-
// 对数 → 线性:把 log 空间偏移转为线性偏移
|
|
150
|
-
const logMin = toLog(this.range.minPrice, this.logFormula)
|
|
151
|
-
const logMax = toLog(this.range.maxPrice, this.logFormula)
|
|
152
|
-
const logCenter = (logMax + logMin) / 2 + this.priceOffset
|
|
153
|
-
const realCenter = fromLog(logCenter, this.logFormula)
|
|
154
|
-
const baseCenter = (this.range.maxPrice + this.range.minPrice) / 2
|
|
155
|
-
this.priceOffset = realCenter - baseCenter
|
|
156
|
-
this.priceOffset = this.clampOffset(this.priceOffset)
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
this.scaleType = type
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
/**
|
|
163
|
-
* 获取当前刻度类型
|
|
164
|
-
*/
|
|
165
|
-
getScaleType(): ScaleType {
|
|
166
|
-
return this.scaleType
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
/**
|
|
170
|
-
* 获取显示范围(考虑 priceOffset 和 verticalScale)
|
|
171
|
-
*
|
|
172
|
-
* 对数模式下:
|
|
173
|
-
* - 内部计算在 log 空间进行
|
|
174
|
-
* - 返回的价格为真实价格
|
|
175
|
-
*/
|
|
176
|
-
getDisplayRange(baseRange?: PriceRange): PriceRange {
|
|
177
|
-
const src = baseRange ?? this.range
|
|
178
|
-
const { minPrice, maxPrice } = src
|
|
179
|
-
|
|
180
|
-
// 对数模式:在 log 空间计算
|
|
181
|
-
if (this.scaleType === 'log' && minPrice > 0) {
|
|
182
|
-
const logMin = toLog(minPrice, this.logFormula)
|
|
183
|
-
const logMax = toLog(maxPrice, this.logFormula)
|
|
184
|
-
const logRange = logMax - logMin || 1
|
|
185
|
-
const logCenter = (logMax + logMin) / 2 + this.priceOffset
|
|
186
|
-
const logHalfRange = logRange / (2 * this.verticalScale)
|
|
187
|
-
return {
|
|
188
|
-
maxPrice: fromLog(logCenter + logHalfRange, this.logFormula),
|
|
189
|
-
minPrice: fromLog(logCenter - logHalfRange, this.logFormula),
|
|
190
|
-
}
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
// 线性模式:原逻辑不变
|
|
194
|
-
const baseRangeSize = maxPrice - minPrice || 1
|
|
195
|
-
const centerPrice = (maxPrice + minPrice) / 2 + this.priceOffset
|
|
196
|
-
const halfRange = baseRangeSize / (2 * this.verticalScale)
|
|
197
|
-
return {
|
|
198
|
-
maxPrice: centerPrice + halfRange,
|
|
199
|
-
minPrice: centerPrice - halfRange,
|
|
200
|
-
}
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
/**
|
|
204
|
-
* 价格 → Y 坐标
|
|
205
|
-
* 统一使用 getDisplayRange 的结果进行映射
|
|
206
|
-
*/
|
|
207
|
-
priceToY(price: number): number {
|
|
208
|
-
const { maxPrice, minPrice } = this.getDisplayRange()
|
|
209
|
-
const viewHeight = Math.max(1, this.height - this.paddingTop - this.paddingBottom)
|
|
210
|
-
|
|
211
|
-
let ratio: number
|
|
212
|
-
if (this.scaleType === 'log' && minPrice > 0) {
|
|
213
|
-
const logMin = toLog(minPrice, this.logFormula)
|
|
214
|
-
const logMax = toLog(maxPrice, this.logFormula)
|
|
215
|
-
const logPrice = toLog(price, this.logFormula)
|
|
216
|
-
ratio = (logPrice - logMin) / (logMax - logMin || 1)
|
|
217
|
-
} else {
|
|
218
|
-
ratio = (price - minPrice) / (maxPrice - minPrice || 1)
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
// 注意:ratio = 0 对应 minPrice(底部),ratio = 1 对应 maxPrice(顶部)
|
|
222
|
-
// 但在屏幕上,y=0 是顶部,y=height 是底部
|
|
223
|
-
return this.paddingTop + viewHeight * (1 - ratio)
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
/**
|
|
227
|
-
* Y 坐标 → 价格
|
|
228
|
-
* 统一使用 getDisplayRange 的结果进行映射
|
|
229
|
-
*/
|
|
230
|
-
yToPrice(y: number): number {
|
|
231
|
-
const { maxPrice, minPrice } = this.getDisplayRange()
|
|
232
|
-
const viewHeight = Math.max(1, this.height - this.paddingTop - this.paddingBottom)
|
|
233
|
-
const ratio = 1 - (y - this.paddingTop) / viewHeight
|
|
234
|
-
|
|
235
|
-
if (this.scaleType === 'log' && minPrice > 0) {
|
|
236
|
-
const logMin = toLog(minPrice, this.logFormula)
|
|
237
|
-
const logMax = toLog(maxPrice, this.logFormula)
|
|
238
|
-
const logPrice = logMin + ratio * (logMax - logMin)
|
|
239
|
-
return fromLog(logPrice, this.logFormula)
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
return minPrice + ratio * (maxPrice - minPrice)
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
/**
|
|
246
|
-
* 根据像素偏移计算价格偏移
|
|
247
|
-
* @param deltaY Y轴像素偏移(正数向下拖动)
|
|
248
|
-
* @returns 对应的价格偏移量
|
|
249
|
-
* - 线性模式:真实价格的偏移
|
|
250
|
-
* - 对数模式:log 空间的偏移
|
|
251
|
-
*/
|
|
252
|
-
deltaYToPriceOffset(deltaY: number): number {
|
|
253
|
-
const viewHeight = Math.max(1, this.height - this.paddingTop - this.paddingBottom)
|
|
254
|
-
|
|
255
|
-
if (this.scaleType === 'log' && this.range.minPrice > 0) {
|
|
256
|
-
const logMin = toLog(this.range.minPrice, this.logFormula)
|
|
257
|
-
const logMax = toLog(this.range.maxPrice, this.logFormula)
|
|
258
|
-
return deltaY * ((logMax - logMin) / viewHeight)
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
const range = this.range.maxPrice - this.range.minPrice || 1
|
|
262
|
-
return deltaY * (range / viewHeight)
|
|
263
|
-
}
|
|
264
|
-
}
|
|
1
|
+
import type { PriceRange } from './price'
|
|
2
|
+
import type { ScaleType } from '../utils/tickPosition'
|
|
3
|
+
import {
|
|
4
|
+
type LogFormula,
|
|
5
|
+
toLog,
|
|
6
|
+
fromLog,
|
|
7
|
+
logFormulaForPriceRange,
|
|
8
|
+
logFormulasAreSame,
|
|
9
|
+
} from './logFormula'
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Pane 级别的价格坐标系(价格 -> pane 内 Y)
|
|
13
|
+
* - y=0 在 pane 顶部,y=height 在 pane 底部
|
|
14
|
+
*/
|
|
15
|
+
export class PriceScale {
|
|
16
|
+
private range: PriceRange = { maxPrice: 100, minPrice: 0 }
|
|
17
|
+
private height = 1
|
|
18
|
+
private paddingTop = 0
|
|
19
|
+
private paddingBottom = 0
|
|
20
|
+
|
|
21
|
+
/** 价格偏移量(用于上下拖动平移价格轴)
|
|
22
|
+
* 在对数模式下,此偏移量为 log 空间偏移 */
|
|
23
|
+
private priceOffset = 0
|
|
24
|
+
|
|
25
|
+
/** 垂直缩放系数(1=默认,>1 放大,<1 缩小) */
|
|
26
|
+
private verticalScale = 1
|
|
27
|
+
|
|
28
|
+
/** 刻度类型:线性或对数 */
|
|
29
|
+
private scaleType: ScaleType = 'linear'
|
|
30
|
+
|
|
31
|
+
/** 对数变换公式(动态计算,适配极小价格) */
|
|
32
|
+
private logFormula: LogFormula = logFormulaForPriceRange(null)
|
|
33
|
+
|
|
34
|
+
setRange(r: PriceRange) {
|
|
35
|
+
this.range = r
|
|
36
|
+
if (this.scaleType === 'log' && r.minPrice > 0) {
|
|
37
|
+
const newFormula = logFormulaForPriceRange(r)
|
|
38
|
+
if (!logFormulasAreSame(newFormula, this.logFormula)) {
|
|
39
|
+
// 将旧公式的 log 偏移量转换到新公式空间
|
|
40
|
+
const oldLogMin = toLog(r.minPrice, this.logFormula)
|
|
41
|
+
const oldLogMax = toLog(r.maxPrice, this.logFormula)
|
|
42
|
+
const oldCenter = (oldLogMax + oldLogMin) / 2 + this.priceOffset
|
|
43
|
+
|
|
44
|
+
this.logFormula = newFormula
|
|
45
|
+
|
|
46
|
+
const newLogMin = toLog(r.minPrice, this.logFormula)
|
|
47
|
+
const newLogMax = toLog(r.maxPrice, this.logFormula)
|
|
48
|
+
const newBaseCenter = (newLogMax + newLogMin) / 2
|
|
49
|
+
this.priceOffset = this.clampOffset(oldCenter - newBaseCenter)
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
setHeight(h: number) {
|
|
55
|
+
this.height = Math.max(1, h)
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
setPadding(top: number, bottom: number) {
|
|
59
|
+
this.paddingTop = Math.max(0, top)
|
|
60
|
+
this.paddingBottom = Math.max(0, bottom)
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
getRange(): PriceRange {
|
|
64
|
+
return this.range
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
getPaddingTop(): number {
|
|
68
|
+
return this.paddingTop
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
getPaddingBottom(): number {
|
|
72
|
+
return this.paddingBottom
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* 设置价格偏移量
|
|
77
|
+
* @param offset 价格偏移
|
|
78
|
+
* - 线性模式:真实价格的线性偏移
|
|
79
|
+
* - 对数模式:log 空间的偏移量
|
|
80
|
+
*/
|
|
81
|
+
setPriceOffset(offset: number): void {
|
|
82
|
+
this.priceOffset = this.clampOffset(offset)
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* 获取当前价格偏移量
|
|
87
|
+
*/
|
|
88
|
+
getPriceOffset(): number {
|
|
89
|
+
return this.priceOffset
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* 重置价格偏移量
|
|
94
|
+
*/
|
|
95
|
+
resetPriceOffset(): void {
|
|
96
|
+
this.priceOffset = 0
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* 根据当前 range 和 verticalScale 对 priceOffset 进行 clamp,
|
|
101
|
+
* 防止视口完全离开数据范围。
|
|
102
|
+
*/
|
|
103
|
+
private clampOffset(offset: number): number {
|
|
104
|
+
if (this.scaleType === 'log' && this.range.minPrice > 0) {
|
|
105
|
+
const logMin = toLog(this.range.minPrice, this.logFormula)
|
|
106
|
+
const logMax = toLog(this.range.maxPrice, this.logFormula)
|
|
107
|
+
const logRange = logMax - logMin
|
|
108
|
+
if (logRange <= 0) return 0
|
|
109
|
+
const maxOffset = logRange * (1 + 1 / this.verticalScale) / 2
|
|
110
|
+
return Math.max(-maxOffset, Math.min(maxOffset, offset))
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const rangeSize = this.range.maxPrice - this.range.minPrice
|
|
114
|
+
if (rangeSize <= 0) return 0
|
|
115
|
+
const maxOffset = rangeSize * (1 + 1 / this.verticalScale) / 2
|
|
116
|
+
return Math.max(-maxOffset, Math.min(maxOffset, offset))
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* 按拖拽位移缩放 Y 轴(deltaY < 0 放大,deltaY > 0 缩小)
|
|
121
|
+
*/
|
|
122
|
+
scaleByDelta(deltaY: number): void {
|
|
123
|
+
if (!Number.isFinite(deltaY) || deltaY === 0) return
|
|
124
|
+
const factor = Math.exp(-deltaY * 0.01)
|
|
125
|
+
const nextScale = this.verticalScale * factor
|
|
126
|
+
this.verticalScale = Math.min(8, Math.max(0.2, nextScale))
|
|
127
|
+
this.priceOffset = this.clampOffset(this.priceOffset)
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
getVerticalScale(): number {
|
|
131
|
+
return this.verticalScale
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* 设置刻度类型
|
|
136
|
+
* 切换时会自动转换 priceOffset 以保持视口位置
|
|
137
|
+
*/
|
|
138
|
+
setScaleType(type: ScaleType): void {
|
|
139
|
+
if (type === this.scaleType) return
|
|
140
|
+
|
|
141
|
+
if (type === 'log' && this.range.minPrice > 0) {
|
|
142
|
+
// 线性 → 对数:把线性偏移转为 log 空间偏移
|
|
143
|
+
this.logFormula = logFormulaForPriceRange(this.range)
|
|
144
|
+
const baseCenter = (this.range.maxPrice + this.range.minPrice) / 2
|
|
145
|
+
const realCenter = baseCenter + this.priceOffset
|
|
146
|
+
this.priceOffset = toLog(realCenter, this.logFormula) - toLog(baseCenter, this.logFormula)
|
|
147
|
+
this.priceOffset = this.clampOffset(this.priceOffset)
|
|
148
|
+
} else if (type === 'linear' && this.scaleType === 'log') {
|
|
149
|
+
// 对数 → 线性:把 log 空间偏移转为线性偏移
|
|
150
|
+
const logMin = toLog(this.range.minPrice, this.logFormula)
|
|
151
|
+
const logMax = toLog(this.range.maxPrice, this.logFormula)
|
|
152
|
+
const logCenter = (logMax + logMin) / 2 + this.priceOffset
|
|
153
|
+
const realCenter = fromLog(logCenter, this.logFormula)
|
|
154
|
+
const baseCenter = (this.range.maxPrice + this.range.minPrice) / 2
|
|
155
|
+
this.priceOffset = realCenter - baseCenter
|
|
156
|
+
this.priceOffset = this.clampOffset(this.priceOffset)
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
this.scaleType = type
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* 获取当前刻度类型
|
|
164
|
+
*/
|
|
165
|
+
getScaleType(): ScaleType {
|
|
166
|
+
return this.scaleType
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* 获取显示范围(考虑 priceOffset 和 verticalScale)
|
|
171
|
+
*
|
|
172
|
+
* 对数模式下:
|
|
173
|
+
* - 内部计算在 log 空间进行
|
|
174
|
+
* - 返回的价格为真实价格
|
|
175
|
+
*/
|
|
176
|
+
getDisplayRange(baseRange?: PriceRange): PriceRange {
|
|
177
|
+
const src = baseRange ?? this.range
|
|
178
|
+
const { minPrice, maxPrice } = src
|
|
179
|
+
|
|
180
|
+
// 对数模式:在 log 空间计算
|
|
181
|
+
if (this.scaleType === 'log' && minPrice > 0) {
|
|
182
|
+
const logMin = toLog(minPrice, this.logFormula)
|
|
183
|
+
const logMax = toLog(maxPrice, this.logFormula)
|
|
184
|
+
const logRange = logMax - logMin || 1
|
|
185
|
+
const logCenter = (logMax + logMin) / 2 + this.priceOffset
|
|
186
|
+
const logHalfRange = logRange / (2 * this.verticalScale)
|
|
187
|
+
return {
|
|
188
|
+
maxPrice: fromLog(logCenter + logHalfRange, this.logFormula),
|
|
189
|
+
minPrice: fromLog(logCenter - logHalfRange, this.logFormula),
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// 线性模式:原逻辑不变
|
|
194
|
+
const baseRangeSize = maxPrice - minPrice || 1
|
|
195
|
+
const centerPrice = (maxPrice + minPrice) / 2 + this.priceOffset
|
|
196
|
+
const halfRange = baseRangeSize / (2 * this.verticalScale)
|
|
197
|
+
return {
|
|
198
|
+
maxPrice: centerPrice + halfRange,
|
|
199
|
+
minPrice: centerPrice - halfRange,
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* 价格 → Y 坐标
|
|
205
|
+
* 统一使用 getDisplayRange 的结果进行映射
|
|
206
|
+
*/
|
|
207
|
+
priceToY(price: number): number {
|
|
208
|
+
const { maxPrice, minPrice } = this.getDisplayRange()
|
|
209
|
+
const viewHeight = Math.max(1, this.height - this.paddingTop - this.paddingBottom)
|
|
210
|
+
|
|
211
|
+
let ratio: number
|
|
212
|
+
if (this.scaleType === 'log' && minPrice > 0) {
|
|
213
|
+
const logMin = toLog(minPrice, this.logFormula)
|
|
214
|
+
const logMax = toLog(maxPrice, this.logFormula)
|
|
215
|
+
const logPrice = toLog(price, this.logFormula)
|
|
216
|
+
ratio = (logPrice - logMin) / (logMax - logMin || 1)
|
|
217
|
+
} else {
|
|
218
|
+
ratio = (price - minPrice) / (maxPrice - minPrice || 1)
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
// 注意:ratio = 0 对应 minPrice(底部),ratio = 1 对应 maxPrice(顶部)
|
|
222
|
+
// 但在屏幕上,y=0 是顶部,y=height 是底部
|
|
223
|
+
return this.paddingTop + viewHeight * (1 - ratio)
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* Y 坐标 → 价格
|
|
228
|
+
* 统一使用 getDisplayRange 的结果进行映射
|
|
229
|
+
*/
|
|
230
|
+
yToPrice(y: number): number {
|
|
231
|
+
const { maxPrice, minPrice } = this.getDisplayRange()
|
|
232
|
+
const viewHeight = Math.max(1, this.height - this.paddingTop - this.paddingBottom)
|
|
233
|
+
const ratio = 1 - (y - this.paddingTop) / viewHeight
|
|
234
|
+
|
|
235
|
+
if (this.scaleType === 'log' && minPrice > 0) {
|
|
236
|
+
const logMin = toLog(minPrice, this.logFormula)
|
|
237
|
+
const logMax = toLog(maxPrice, this.logFormula)
|
|
238
|
+
const logPrice = logMin + ratio * (logMax - logMin)
|
|
239
|
+
return fromLog(logPrice, this.logFormula)
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
return minPrice + ratio * (maxPrice - minPrice)
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
/**
|
|
246
|
+
* 根据像素偏移计算价格偏移
|
|
247
|
+
* @param deltaY Y轴像素偏移(正数向下拖动)
|
|
248
|
+
* @returns 对应的价格偏移量
|
|
249
|
+
* - 线性模式:真实价格的偏移
|
|
250
|
+
* - 对数模式:log 空间的偏移
|
|
251
|
+
*/
|
|
252
|
+
deltaYToPriceOffset(deltaY: number): number {
|
|
253
|
+
const viewHeight = Math.max(1, this.height - this.paddingTop - this.paddingBottom)
|
|
254
|
+
|
|
255
|
+
if (this.scaleType === 'log' && this.range.minPrice > 0) {
|
|
256
|
+
const logMin = toLog(this.range.minPrice, this.logFormula)
|
|
257
|
+
const logMax = toLog(this.range.maxPrice, this.logFormula)
|
|
258
|
+
return deltaY * ((logMax - logMin) / viewHeight)
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
const range = this.range.maxPrice - this.range.minPrice || 1
|
|
262
|
+
return deltaY * (range / viewHeight)
|
|
263
|
+
}
|
|
264
|
+
}
|