@363045841yyt/klinechart-core 0.9.0-alpha.2 → 0.9.0-alpha.4
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/dist/controllers/createChartController.d.ts.map +1 -1
- package/dist/controllers/createChartController.js +9 -8
- package/dist/controllers/createChartController.js.map +1 -1
- package/dist/controllers/index.d.ts +1 -0
- package/dist/controllers/index.d.ts.map +1 -1
- package/dist/controllers/index.js.map +1 -1
- package/dist/controllers/types.d.ts +5 -0
- package/dist/controllers/types.d.ts.map +1 -1
- package/dist/data/binance.d.ts +1 -1
- package/dist/data/binance.d.ts.map +1 -1
- package/dist/data/binance.js +1 -1
- package/dist/data/binance.js.map +1 -1
- package/dist/data/timeShareBuffer.d.ts +3 -0
- package/dist/data/timeShareBuffer.d.ts.map +1 -1
- package/dist/data/timeShareBuffer.js +13 -0
- package/dist/data/timeShareBuffer.js.map +1 -1
- package/dist/engine/chart.d.ts +9 -9
- package/dist/engine/chart.d.ts.map +1 -1
- package/dist/engine/chart.js +87 -101
- package/dist/engine/chart.js.map +1 -1
- package/dist/engine/chartTypes.d.ts +5 -0
- package/dist/engine/chartTypes.d.ts.map +1 -1
- package/dist/engine/controller/interaction.d.ts +4 -2
- package/dist/engine/controller/interaction.d.ts.map +1 -1
- package/dist/engine/controller/interaction.js +18 -10
- package/dist/engine/controller/interaction.js.map +1 -1
- package/dist/engine/data/chartDataManager.d.ts +14 -18
- package/dist/engine/data/chartDataManager.d.ts.map +1 -1
- package/dist/engine/data/chartDataManager.js +55 -37
- package/dist/engine/data/chartDataManager.js.map +1 -1
- package/dist/engine/data/incrementalLoadHint.d.ts +2 -4
- package/dist/engine/data/incrementalLoadHint.d.ts.map +1 -1
- package/dist/engine/data/incrementalLoadHint.js +5 -2
- package/dist/engine/data/incrementalLoadHint.js.map +1 -1
- package/dist/engine/data/scrollCompensator.d.ts +3 -13
- package/dist/engine/data/scrollCompensator.d.ts.map +1 -1
- package/dist/engine/data/scrollCompensator.js +13 -14
- package/dist/engine/data/scrollCompensator.js.map +1 -1
- package/dist/engine/indicators/chartIndicatorManager.d.ts +16 -17
- package/dist/engine/indicators/chartIndicatorManager.d.ts.map +1 -1
- package/dist/engine/indicators/chartIndicatorManager.js +37 -37
- package/dist/engine/indicators/chartIndicatorManager.js.map +1 -1
- package/dist/engine/layout/chartPaneLayout.d.ts +11 -7
- package/dist/engine/layout/chartPaneLayout.d.ts.map +1 -1
- package/dist/engine/layout/chartPaneLayout.js +58 -16
- package/dist/engine/layout/chartPaneLayout.js.map +1 -1
- package/dist/engine/modes/timeShareMath.d.ts +56 -0
- package/dist/engine/modes/timeShareMath.d.ts.map +1 -0
- package/dist/engine/modes/timeShareMath.js +90 -0
- package/dist/engine/modes/timeShareMath.js.map +1 -0
- package/dist/engine/modes/timeShareMode.d.ts +5 -0
- package/dist/engine/modes/timeShareMode.d.ts.map +1 -1
- package/dist/engine/modes/timeShareMode.js +27 -27
- package/dist/engine/modes/timeShareMode.js.map +1 -1
- package/dist/engine/paneRenderer.d.ts +2 -0
- package/dist/engine/paneRenderer.d.ts.map +1 -1
- package/dist/engine/paneRenderer.js +15 -5
- package/dist/engine/paneRenderer.js.map +1 -1
- package/dist/engine/render/chartRenderer.d.ts +28 -22
- package/dist/engine/render/chartRenderer.d.ts.map +1 -1
- package/dist/engine/render/chartRenderer.js +143 -85
- package/dist/engine/render/chartRenderer.js.map +1 -1
- package/dist/engine/render/layers/leftYAxisLayer.d.ts +9 -2
- package/dist/engine/render/layers/leftYAxisLayer.d.ts.map +1 -1
- package/dist/engine/render/layers/leftYAxisLayer.js +11 -2
- package/dist/engine/render/layers/leftYAxisLayer.js.map +1 -1
- package/dist/engine/render/layers/mainIndicatorLegendLayer.d.ts +6 -4
- package/dist/engine/render/layers/mainIndicatorLegendLayer.d.ts.map +1 -1
- package/dist/engine/render/layers/mainIndicatorLegendLayer.js +6 -9
- package/dist/engine/render/layers/mainIndicatorLegendLayer.js.map +1 -1
- package/dist/engine/render/layers/mainIndicatorLegendLayer.test.d.ts +2 -0
- package/dist/engine/render/layers/mainIndicatorLegendLayer.test.d.ts.map +1 -0
- package/dist/engine/render/layers/mainIndicatorLegendLayer.test.js +19 -0
- package/dist/engine/render/layers/mainIndicatorLegendLayer.test.js.map +1 -0
- package/dist/engine/render/layers/yAxisLayer.d.ts +9 -2
- package/dist/engine/render/layers/yAxisLayer.d.ts.map +1 -1
- package/dist/engine/render/layers/yAxisLayer.js +11 -2
- package/dist/engine/render/layers/yAxisLayer.js.map +1 -1
- package/dist/engine/renderers/Indicator/mainIndicatorLegend.d.ts +7 -3
- package/dist/engine/renderers/Indicator/mainIndicatorLegend.d.ts.map +1 -1
- package/dist/engine/renderers/Indicator/mainIndicatorLegend.js +234 -330
- package/dist/engine/renderers/Indicator/mainIndicatorLegend.js.map +1 -1
- package/dist/engine/renderers/Indicator/mainIndicatorLegendContext.d.ts +73 -0
- package/dist/engine/renderers/Indicator/mainIndicatorLegendContext.d.ts.map +1 -0
- package/dist/engine/renderers/Indicator/mainIndicatorLegendContext.js +182 -0
- package/dist/engine/renderers/Indicator/mainIndicatorLegendContext.js.map +1 -0
- package/dist/engine/renderers/leftYAxis.d.ts +15 -2
- package/dist/engine/renderers/leftYAxis.d.ts.map +1 -1
- package/dist/engine/renderers/leftYAxis.js +56 -16
- package/dist/engine/renderers/leftYAxis.js.map +1 -1
- package/dist/engine/renderers/timeShare.d.ts.map +1 -1
- package/dist/engine/renderers/timeShare.js +20 -9
- package/dist/engine/renderers/timeShare.js.map +1 -1
- package/dist/engine/renderers/yAxis.d.ts +16 -6
- package/dist/engine/renderers/yAxis.d.ts.map +1 -1
- package/dist/engine/renderers/yAxis.js +49 -16
- package/dist/engine/renderers/yAxis.js.map +1 -1
- package/dist/engine/state/viewportState.d.ts +4 -1
- package/dist/engine/state/viewportState.d.ts.map +1 -1
- package/dist/engine/state/viewportState.js +40 -8
- package/dist/engine/state/viewportState.js.map +1 -1
- package/dist/engine/subPaneManager.d.ts +5 -3
- package/dist/engine/subPaneManager.d.ts.map +1 -1
- package/dist/engine/subPaneManager.js +3 -2
- package/dist/engine/subPaneManager.js.map +1 -1
- package/dist/engine/utils/chartZoomController.d.ts +9 -7
- package/dist/engine/utils/chartZoomController.d.ts.map +1 -1
- package/dist/engine/utils/chartZoomController.js +13 -12
- package/dist/engine/utils/chartZoomController.js.map +1 -1
- package/dist/engine/viewport/chartViewportManager.d.ts +5 -16
- package/dist/engine/viewport/chartViewportManager.d.ts.map +1 -1
- package/dist/engine/viewport/chartViewportManager.js +7 -48
- package/dist/engine/viewport/chartViewportManager.js.map +1 -1
- package/dist/engine/viewport/viewport.d.ts +14 -0
- package/dist/engine/viewport/viewport.d.ts.map +1 -1
- package/dist/engine/viewport/viewport.js +10 -0
- package/dist/engine/viewport/viewport.js.map +1 -1
- package/dist/foundation/config/chartSettings.d.ts +0 -5
- package/dist/foundation/config/chartSettings.d.ts.map +1 -1
- package/dist/foundation/config/chartSettings.js +14 -0
- package/dist/foundation/config/chartSettings.js.map +1 -1
- package/dist/foundation/plugin/rendererPluginManager.test.js +15 -0
- package/dist/foundation/plugin/rendererPluginManager.test.js.map +1 -1
- package/dist/foundation/plugin/types.d.ts +3 -0
- package/dist/foundation/plugin/types.d.ts.map +1 -1
- package/dist/foundation/plugin/types.js.map +1 -1
- package/dist/foundation/reactivity/frameTransaction.d.ts +11 -0
- package/dist/foundation/reactivity/frameTransaction.d.ts.map +1 -1
- package/dist/foundation/reactivity/frameTransaction.js +17 -2
- package/dist/foundation/reactivity/frameTransaction.js.map +1 -1
- package/dist/foundation/utils/kLineDraw/axis.d.ts +3 -0
- package/dist/foundation/utils/kLineDraw/axis.d.ts.map +1 -1
- package/dist/foundation/utils/kLineDraw/axis.js +40 -9
- package/dist/foundation/utils/kLineDraw/axis.js.map +1 -1
- package/dist/foundation/utils/sessionTimeLabels.d.ts +59 -0
- package/dist/foundation/utils/sessionTimeLabels.d.ts.map +1 -0
- package/dist/foundation/utils/sessionTimeLabels.js +170 -0
- package/dist/foundation/utils/sessionTimeLabels.js.map +1 -0
- package/dist/foundation/utils/timeShareAxisLabels.d.ts +27 -0
- package/dist/foundation/utils/timeShareAxisLabels.d.ts.map +1 -0
- package/dist/foundation/utils/timeShareAxisLabels.js +45 -0
- package/dist/foundation/utils/timeShareAxisLabels.js.map +1 -0
- package/dist/rendering/render/Renderer.d.ts +58 -74
- package/dist/rendering/render/Renderer.d.ts.map +1 -1
- package/dist/rendering/render/Renderer.js +8 -18
- package/dist/rendering/render/Renderer.js.map +1 -1
- package/dist/rendering/render/createWebGPURenderer.d.ts.map +1 -1
- package/dist/rendering/render/createWebGPURenderer.js +30 -17
- package/dist/rendering/render/createWebGPURenderer.js.map +1 -1
- package/dist/rendering/render/createWebGPUSurfaceBackend.js +3 -3
- package/dist/rendering/render/createWebGPUSurfaceBackend.js.map +1 -1
- package/dist/rendering/render/physicalLine.d.ts +4 -0
- package/dist/rendering/render/physicalLine.d.ts.map +1 -0
- package/dist/rendering/render/physicalLine.js +37 -0
- package/dist/rendering/render/physicalLine.js.map +1 -0
- package/dist/rendering/render/physicalRegion.d.ts +14 -0
- package/dist/rendering/render/physicalRegion.d.ts.map +1 -0
- package/dist/rendering/render/physicalRegion.js +15 -0
- package/dist/rendering/render/physicalRegion.js.map +1 -0
- package/dist/rendering/scene/createLayerFromPlugin.d.ts.map +1 -1
- package/dist/rendering/scene/createLayerFromPlugin.js +8 -3
- package/dist/rendering/scene/createLayerFromPlugin.js.map +1 -1
- package/dist/rendering/scene/createScene.d.ts.map +1 -1
- package/dist/rendering/scene/createScene.js +4 -3
- package/dist/rendering/scene/createScene.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +1 -1
- package/src/__tests__/stateKernel.test.ts +1 -0
- package/src/controllers/createChartController.ts +9 -8
- package/src/controllers/index.ts +11 -0
- package/src/controllers/types.ts +7 -0
- package/src/data/__tests__/timeShareBuffer.test.ts +45 -0
- package/src/data/binance.ts +1 -1
- package/src/data/timeShareBuffer.ts +15 -0
- package/src/engine/__tests__/chart.dpr.test.ts +31 -2
- package/src/engine/__tests__/paneRenderer.resize.test.ts +52 -54
- package/src/engine/__tests__/subPaneManager.test.ts +5 -3
- package/src/engine/chart.ts +105 -115
- package/src/engine/chartTypes.ts +5 -0
- package/src/engine/controller/__tests__/interaction.dpr.test.ts +39 -2
- package/src/engine/controller/interaction.ts +18 -10
- package/src/engine/data/__tests__/chartDataManager.incrementalLoad.test.ts +37 -17
- package/src/engine/data/__tests__/scrollCompensator.geometry.test.ts +87 -75
- package/src/engine/data/chartDataManager.ts +64 -59
- package/src/engine/data/incrementalLoadHint.ts +8 -7
- package/src/engine/data/scrollCompensator.ts +16 -24
- package/src/engine/indicators/__tests__/chartIndicatorManager.test.ts +18 -26
- package/src/engine/indicators/chartIndicatorManager.ts +62 -62
- package/src/engine/layout/chartPaneLayout.ts +73 -21
- package/src/engine/modes/__tests__/timeShareMath.test.ts +185 -0
- package/src/engine/modes/__tests__/timeShareMode.test.ts +81 -0
- package/src/engine/modes/timeShareMath.ts +150 -0
- package/src/engine/modes/timeShareMode.ts +35 -28
- package/src/engine/paneRenderer.ts +17 -10
- package/src/engine/render/chartRenderer.ts +214 -145
- package/src/engine/render/layers/leftYAxisLayer.ts +29 -7
- package/src/engine/render/layers/mainIndicatorLegendLayer.test.ts +26 -0
- package/src/engine/render/layers/mainIndicatorLegendLayer.ts +13 -10
- package/src/engine/render/layers/yAxisLayer.ts +29 -7
- package/src/engine/renderers/Indicator/mainIndicatorLegend.ts +270 -403
- package/src/engine/renderers/Indicator/mainIndicatorLegendContext.ts +290 -0
- package/src/engine/renderers/__tests__/mainIndicatorLegend.renderer.test.ts +73 -2
- package/src/engine/renderers/__tests__/yAxis.renderer.test.ts +10 -6
- package/src/engine/renderers/leftYAxis.ts +60 -18
- package/src/engine/renderers/timeShare.ts +25 -9
- package/src/engine/renderers/yAxis.ts +57 -22
- package/src/engine/state/__tests__/settingsState.test.ts +9 -0
- package/src/engine/state/viewportState.ts +44 -8
- package/src/engine/subPaneManager.ts +8 -5
- package/src/engine/utils/chartZoomController.ts +22 -19
- package/src/engine/viewport/__tests__/visibleRange.clamp.test.ts +50 -0
- package/src/engine/viewport/chartViewportManager.ts +9 -60
- package/src/engine/viewport/viewport.ts +14 -0
- package/src/foundation/config/chartSettings.ts +13 -0
- package/src/foundation/plugin/rendererPluginManager.test.ts +18 -0
- package/src/foundation/plugin/types.ts +3 -0
- package/src/foundation/reactivity/frameTransaction.ts +20 -6
- package/src/foundation/utils/__tests__/sessionTimeLabels.test.ts +91 -0
- package/src/foundation/utils/kLineDraw/axis.ts +50 -9
- package/src/foundation/utils/sessionTimeLabels.ts +237 -0
- package/src/foundation/utils/timeShareAxisLabels.ts +94 -0
- package/src/rendering/render/Renderer.ts +69 -85
- package/src/rendering/render/__tests__/physicalLine.test.ts +51 -0
- package/src/rendering/render/__tests__/physicalRegion.test.ts +23 -0
- package/src/rendering/render/__tests__/webgpuRenderer.test.ts +31 -1
- package/src/rendering/render/__tests__/webgpuSurfaceBackend.test.ts +11 -0
- package/src/rendering/render/createWebGPURenderer.ts +30 -17
- package/src/rendering/render/createWebGPUSurfaceBackend.ts +3 -3
- package/src/rendering/render/physicalLine.ts +45 -0
- package/src/rendering/render/physicalRegion.ts +32 -0
- package/src/rendering/scene/createLayerFromPlugin.ts +8 -3
- package/src/rendering/scene/createScene.ts +4 -3
- package/src/version.ts +1 -1
package/src/engine/chart.ts
CHANGED
|
@@ -1,3 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Chart — 图表顶层编排器。
|
|
3
|
+
*
|
|
4
|
+
* 职责范围:
|
|
5
|
+
* - 持有 StateKernel(所有配置与状态的唯一数据源)
|
|
6
|
+
* - 管理 Viewport / 布局 / 渲染器生命周期
|
|
7
|
+
* - 提供 scheduleDraw / draw 入口供外部(交互、数据、插件)触发重绘
|
|
8
|
+
* - 协调插件宿主、指标控制器、标注管理器等子系统的初始化与销毁
|
|
9
|
+
*
|
|
10
|
+
* 不是绘制管线的一部分。绘制由 ChartRenderer 完成,Chart 仅负责代理调用并维护 runtimeProjection(批量投影)屏障。
|
|
11
|
+
*/
|
|
1
12
|
import { createAlertController } from '../features/alerts/index'
|
|
2
13
|
import {
|
|
3
14
|
createVolumeLookbacks,
|
|
@@ -15,10 +26,13 @@ import {
|
|
|
15
26
|
import { createLayerFromPlugin } from '../rendering/scene/createLayerFromPlugin'
|
|
16
27
|
import {
|
|
17
28
|
computed,
|
|
29
|
+
createSignal,
|
|
18
30
|
type Computed,
|
|
19
31
|
type ReadonlySignal,
|
|
20
32
|
type Signal,
|
|
33
|
+
type WritableSignal,
|
|
21
34
|
} from '../foundation/reactivity/signal'
|
|
35
|
+
import type { LegendTemplateContext } from './renderers/Indicator/mainIndicatorLegendContext'
|
|
22
36
|
|
|
23
37
|
import { InteractionController, type InteractionSnapshot } from './controller/interaction'
|
|
24
38
|
|
|
@@ -46,10 +60,9 @@ import { UpdateLevel, type VisibleRange } from './layout/pane'
|
|
|
46
60
|
import { KLineMode } from './modes/kLineMode'
|
|
47
61
|
import { TimeShareMode } from './modes/timeShareMode'
|
|
48
62
|
import { PaneRenderer } from './paneRenderer'
|
|
49
|
-
import { ChartRenderer } from './render/chartRenderer'
|
|
63
|
+
import { ChartRenderer, mergeUpdateLevel } from './render/chartRenderer'
|
|
50
64
|
import { ChartStateKernel } from './state/chartStateKernel'
|
|
51
65
|
import { ChartViewportManager } from './viewport/chartViewportManager'
|
|
52
|
-
import { getVisibleRange } from './viewport/viewport'
|
|
53
66
|
import { ChartZoomController } from './utils/chartZoomController'
|
|
54
67
|
import { getPhysicalKLineConfig } from './utils/klineConfig'
|
|
55
68
|
import type {
|
|
@@ -117,8 +130,13 @@ export class Chart {
|
|
|
117
130
|
|
|
118
131
|
/** 渲染器 */
|
|
119
132
|
private renderer: ChartRenderer
|
|
133
|
+
/** runRuntimeProjection 嵌套深度,大于 0 时推迟 scheduleDraw */
|
|
120
134
|
private runtimeProjectionDepth = 0
|
|
121
|
-
|
|
135
|
+
/** 被推迟的绘制级别,退出嵌套后统一 flush */
|
|
136
|
+
private pendingProjectionLevel: UpdateLevel | null = null
|
|
137
|
+
/** 主图图例模板上下文(每帧由 mainIndicatorLegend 发布) */
|
|
138
|
+
private readonly _legendTemplateContext: WritableSignal<LegendTemplateContext | null> =
|
|
139
|
+
createSignal<LegendTemplateContext | null>(null)
|
|
122
140
|
|
|
123
141
|
/** 绘图交互会话(锚点/预览/拖拽);工具 id 在 kernel */
|
|
124
142
|
private drawingSession: DrawingInteractionController | null = null
|
|
@@ -133,7 +151,13 @@ export class Chart {
|
|
|
133
151
|
zoomLevel: number
|
|
134
152
|
scaleTypes: Map<string, ScaleType>
|
|
135
153
|
mainIndicators: Array<{ id: string; params: Record<string, number | boolean | string> }>
|
|
136
|
-
subPanes: Array<{
|
|
154
|
+
subPanes: Array<{
|
|
155
|
+
paneId: string
|
|
156
|
+
indicatorId: string
|
|
157
|
+
params: Record<string, unknown>
|
|
158
|
+
}>
|
|
159
|
+
paneRatios: Record<string, number>
|
|
160
|
+
candleEnabled: boolean
|
|
137
161
|
} | null = null
|
|
138
162
|
|
|
139
163
|
/** 上次预警评估的最新 K 线时间戳(用于去重) */
|
|
@@ -275,17 +299,13 @@ export class Chart {
|
|
|
275
299
|
defaultPaneMinHeightPx: o.defaultPaneMinHeightPx,
|
|
276
300
|
}
|
|
277
301
|
},
|
|
278
|
-
|
|
302
|
+
viewport: this.kernel.viewport,
|
|
279
303
|
setKnownPaneIds: (ids) => this.rendererPluginManager.setKnownPaneIds(ids),
|
|
280
304
|
notifyPaneResize: (paneId, pane) =>
|
|
281
305
|
this.rendererPluginManager.notifyResize(paneId, wrapPaneInfo(pane)),
|
|
282
306
|
scheduleDraw: (level) => this.scheduleDraw(level),
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
commitLayout: (ratios, specs) => {
|
|
286
|
-
this.kernel.pane.actions.commitLayout(ratios, specs)
|
|
287
|
-
this.ensurePaneScaleTypesFromSettings()
|
|
288
|
-
},
|
|
307
|
+
pane: this.kernel.pane,
|
|
308
|
+
afterCommitLayout: () => this.ensurePaneScaleTypesFromSettings(),
|
|
289
309
|
})
|
|
290
310
|
|
|
291
311
|
this.alertController = createAlertController()
|
|
@@ -300,51 +320,25 @@ export class Chart {
|
|
|
300
320
|
kGap: this.kernel.viewport.readonly.kGap(),
|
|
301
321
|
}
|
|
302
322
|
},
|
|
303
|
-
getEffectiveDpr: () => this.viewportManager.getEffectiveDpr(),
|
|
304
|
-
getLogicalScrollLeft: () => this.viewportManager.getLogicalScrollLeft(),
|
|
305
|
-
getCachedScrollLeft: () => this.viewportManager.getCachedScrollLeft(),
|
|
306
|
-
setScrollLeft: (v) => {
|
|
307
|
-
this.viewportManager.setScrollLeft(v)
|
|
308
|
-
},
|
|
309
323
|
getDom: () => this.dom,
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
getVisibleRange: () => {
|
|
313
|
-
const vp = this.viewportManager.computeViewport()
|
|
314
|
-
if (!vp) return null
|
|
315
|
-
const dataLen = this.dataManager?.getData().length ?? 0
|
|
316
|
-
if (dataLen === 0) return null
|
|
317
|
-
return getVisibleRange(
|
|
318
|
-
vp.scrollLeft,
|
|
319
|
-
vp.plotWidth,
|
|
320
|
-
this.kernel.zoom.readonly.kWidth(),
|
|
321
|
-
this.kernel.viewport.readonly.kGap(),
|
|
322
|
-
dataLen,
|
|
323
|
-
vp.dpr,
|
|
324
|
-
)
|
|
325
|
-
},
|
|
326
|
-
getLeftLoadBufferWidth: () => this.kernel.viewport.readonly.leftLoadBufferWidth.peek(),
|
|
327
|
-
getContentWidth: () => this.kernel.viewport.readonly.contentWidth.peek(),
|
|
324
|
+
viewport: this.kernel.viewport,
|
|
325
|
+
comparison: this.kernel.comparison,
|
|
328
326
|
scheduleDraw: (level) => this.scheduleDraw(level),
|
|
329
327
|
resetInteraction: () => this.interaction.reset(),
|
|
330
328
|
getIndicatorScheduler: () => this.indicatorManager.indicatorSchedulerAccessor,
|
|
331
329
|
isPointerDown: () => this.interaction.isPointerDown(),
|
|
332
330
|
onTimeShareDataReady: (dataLength) => {
|
|
333
|
-
const vp = this.
|
|
331
|
+
const vp = this.getViewport()
|
|
334
332
|
if (!vp || vp.plotWidth <= 0) return
|
|
335
333
|
const result = this._activeMode.computeKWidth(dataLength, vp.plotWidth, vp.dpr)
|
|
336
334
|
if (result) {
|
|
337
335
|
this.applyRenderState(result.kWidth, result.kGap)
|
|
338
336
|
const leftBuffer = this.getLeftLoadBufferWidth()
|
|
339
|
-
this.
|
|
337
|
+
this.kernel.viewport.actions.scrollTo(leftBuffer)
|
|
340
338
|
}
|
|
341
339
|
},
|
|
342
340
|
onDataProcessed: (data, range) => this.evaluateAlerts(data, range),
|
|
343
341
|
setSymbols: (symbols) => this.kernel.actions.setSymbols(symbols),
|
|
344
|
-
setComparisonLoading: (loading) => this.kernel.comparison.actions.setLoading(loading),
|
|
345
|
-
comparisonSpecs$: this.kernel.comparison.readonly.specs,
|
|
346
|
-
comparisonColors$: this.kernel.comparison.readonly.colors,
|
|
347
|
-
comparisonLoading$: this.kernel.comparison.readonly.loading,
|
|
348
342
|
},
|
|
349
343
|
this.kernel.data,
|
|
350
344
|
this.kernel.dataManager,
|
|
@@ -352,22 +346,16 @@ export class Chart {
|
|
|
352
346
|
|
|
353
347
|
this.zoomController = new ChartZoomController(
|
|
354
348
|
{
|
|
355
|
-
|
|
356
|
-
|
|
349
|
+
viewport: this.kernel.viewport,
|
|
350
|
+
options: this.kernel.options,
|
|
351
|
+
period$: this.kernel.dataManager.readonly.currentPeriod,
|
|
357
352
|
getClientWidth: () =>
|
|
358
|
-
this.
|
|
353
|
+
this.getViewport()?.viewWidth ?? this.dom.container?.clientWidth ?? 0,
|
|
359
354
|
getDataLength: () => this.dataManager.getData().length,
|
|
360
355
|
getPlotWidth: () => this.getLeftLoadBufferWidth(),
|
|
361
|
-
setScrollLeft: (v) => {
|
|
362
|
-
this.viewportManager.setScrollLeft(v)
|
|
363
|
-
},
|
|
364
356
|
onChange: () => {
|
|
365
357
|
this.scheduleDraw()
|
|
366
358
|
},
|
|
367
|
-
getMinKWidth: () => this.kernel.options.readonly.options.peek().minKWidth,
|
|
368
|
-
getMaxKWidth: () => this.kernel.options.readonly.options.peek().maxKWidth,
|
|
369
|
-
getPeriod: () => this.kernel.dataManager.readonly.currentPeriod.peek(),
|
|
370
|
-
zoomLevelCount,
|
|
371
359
|
},
|
|
372
360
|
this.kernel.zoom,
|
|
373
361
|
)
|
|
@@ -387,8 +375,9 @@ export class Chart {
|
|
|
387
375
|
removeRenderer: (name) => this.removeRenderer(name),
|
|
388
376
|
updateRendererConfig: (name, config) => this.updateRendererConfig(name, config),
|
|
389
377
|
setRendererEnabled: (name, enabled) => this.setRendererEnabled(name, enabled),
|
|
390
|
-
|
|
391
|
-
|
|
378
|
+
paneRatios$: this.kernel.pane.readonly.paneRatios as ReadonlySignal<
|
|
379
|
+
Readonly<Record<string, number>>
|
|
380
|
+
>,
|
|
392
381
|
paneSpecs$: this.kernel.pane.readonly.paneSpecs,
|
|
393
382
|
projectPaneLayout: (specs, ratios) => {
|
|
394
383
|
this.layoutManager.projectState(specs, ratios)
|
|
@@ -405,24 +394,17 @@ export class Chart {
|
|
|
405
394
|
getLayer: (id) => this.renderer?.getScene()?.getLayer(id) ?? null,
|
|
406
395
|
setLayerVisibility: (id, visible) =>
|
|
407
396
|
this.renderer?.getScene()?.setLayerVisibility(id, visible),
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
this.kernel.actions.createSubPane(paneId, indicatorId, params),
|
|
420
|
-
removeSubPaneState: (paneId) => this.kernel.actions.removeSubPane(paneId),
|
|
421
|
-
replaceSubPaneState: (paneId, indicatorId, params) =>
|
|
422
|
-
this.kernel.actions.replaceSubPane(paneId, indicatorId, params),
|
|
423
|
-
updateSubPaneStateParams: (paneId, params) =>
|
|
424
|
-
this.kernel.actions.updateSubPaneParams(paneId, params),
|
|
425
|
-
clearSubPaneState: () => this.kernel.actions.clearSubPanes(),
|
|
397
|
+
indicator: this.kernel.indicator,
|
|
398
|
+
subPaneOps: {
|
|
399
|
+
entries: this.kernel.subPane.readonly.entries,
|
|
400
|
+
create: (paneId, indicatorId, params) =>
|
|
401
|
+
this.kernel.actions.createSubPane(paneId, indicatorId, params),
|
|
402
|
+
remove: (paneId) => this.kernel.actions.removeSubPane(paneId),
|
|
403
|
+
replace: (paneId, indicatorId, params) =>
|
|
404
|
+
this.kernel.actions.replaceSubPane(paneId, indicatorId, params),
|
|
405
|
+
setParams: (paneId, params) => this.kernel.actions.updateSubPaneParams(paneId, params),
|
|
406
|
+
clear: () => this.kernel.actions.clearSubPanes(),
|
|
407
|
+
},
|
|
426
408
|
runRendererTransaction: (run) => this.runRuntimeProjection(run),
|
|
427
409
|
})
|
|
428
410
|
|
|
@@ -456,10 +438,10 @@ export class Chart {
|
|
|
456
438
|
getSceneRenderer: () => this.rendererHost.renderer,
|
|
457
439
|
getPluginHost: () => this.pluginHost,
|
|
458
440
|
getRendererPluginManager: () => this.rendererPluginManager,
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
441
|
+
theme$: this.kernel.effectiveTheme$,
|
|
442
|
+
zoom: this.kernel.zoom,
|
|
443
|
+
options: this.kernel.options,
|
|
444
|
+
viewport: this.kernel.viewport,
|
|
463
445
|
getDataManager: () => this.dataManager,
|
|
464
446
|
getIndicatorManager: () => this.indicatorManager,
|
|
465
447
|
getActiveMode: () => this._activeMode,
|
|
@@ -468,6 +450,9 @@ export class Chart {
|
|
|
468
450
|
drawings$: this.kernel.drawing.readonly.drawings,
|
|
469
451
|
selectedDrawingId$: this.kernel.drawing.readonly.selectedDrawingId,
|
|
470
452
|
getOverlay: () => this.drawingSession?.getPaintOverlay() ?? [],
|
|
453
|
+
onLegendContext: (ctx) => {
|
|
454
|
+
this._legendTemplateContext.set(ctx)
|
|
455
|
+
},
|
|
471
456
|
})
|
|
472
457
|
this.renderer.registerDrawingPlugins()
|
|
473
458
|
this.renderer.initCoreRenderers()
|
|
@@ -476,7 +461,8 @@ export class Chart {
|
|
|
476
461
|
}
|
|
477
462
|
|
|
478
463
|
getViewport(): Viewport | null {
|
|
479
|
-
|
|
464
|
+
if (this.kernel.viewport.readonly.viewWidth.peek() === 0) return null
|
|
465
|
+
return this.kernel.viewport.readonly.viewport.peek()
|
|
480
466
|
}
|
|
481
467
|
|
|
482
468
|
/** 获取当前活跃的模式处理器 */
|
|
@@ -490,17 +476,21 @@ export class Chart {
|
|
|
490
476
|
const prev = this._activeMode
|
|
491
477
|
|
|
492
478
|
if (mode === this._timeShareMode) {
|
|
479
|
+
const candlePlugin = this.rendererPluginManager.getPlugin('candle')
|
|
493
480
|
this._savedTimeShareState = {
|
|
494
481
|
zoomLevel: this.kernel.zoom.readonly.zoomLevel.peek(),
|
|
495
482
|
scaleTypes: new Map(this.kernel.pane.readonly.paneScaleTypes.peek()),
|
|
496
483
|
mainIndicators: [],
|
|
497
484
|
subPanes: [],
|
|
485
|
+
paneRatios: { ...this.kernel.pane.readonly.paneRatios.peek() },
|
|
486
|
+
candleEnabled: candlePlugin?.enabled !== false,
|
|
498
487
|
}
|
|
499
488
|
for (const [id, entry] of this.kernel.indicator.readonly.mainIndicators.peek()) {
|
|
500
489
|
this._savedTimeShareState.mainIndicators.push({ id, params: { ...entry.params } })
|
|
501
490
|
}
|
|
502
491
|
this._savedTimeShareState.subPanes = this.indicatorManager.getSubPaneEntries().map((e) => ({
|
|
503
|
-
|
|
492
|
+
paneId: e.paneId,
|
|
493
|
+
indicatorId: e.indicatorId,
|
|
504
494
|
params: { ...e.params },
|
|
505
495
|
}))
|
|
506
496
|
// 分时强制 percent 进 kernel,再投影(不再由 updatePaneRange 每帧旁路写)
|
|
@@ -546,7 +536,7 @@ export class Chart {
|
|
|
546
536
|
|
|
547
537
|
if (mode === this._timeShareMode) {
|
|
548
538
|
for (const { id } of this._savedTimeShareState!.mainIndicators) {
|
|
549
|
-
if (id !== 'TIMESHARE') {
|
|
539
|
+
if (id !== 'TIMESHARE' && id !== 'timeShare') {
|
|
550
540
|
this.indicatorManager.disableMainIndicator(id)
|
|
551
541
|
}
|
|
552
542
|
}
|
|
@@ -555,13 +545,26 @@ export class Chart {
|
|
|
555
545
|
const saved = this._savedTimeShareState
|
|
556
546
|
if (saved) {
|
|
557
547
|
for (const { id, params } of saved.mainIndicators) {
|
|
558
|
-
if (id !== 'TIMESHARE') {
|
|
548
|
+
if (id !== 'TIMESHARE' && id !== 'timeShare') {
|
|
559
549
|
this.indicatorManager.enableMainIndicator(id, params)
|
|
560
550
|
}
|
|
561
551
|
}
|
|
562
|
-
for (const {
|
|
563
|
-
this.indicatorManager.
|
|
552
|
+
for (const { paneId, indicatorId, params } of saved.subPanes) {
|
|
553
|
+
this.indicatorManager.createSubPane(
|
|
554
|
+
paneId,
|
|
555
|
+
indicatorId as SubIndicatorType,
|
|
556
|
+
params as Record<string, number | boolean | string>,
|
|
557
|
+
)
|
|
558
|
+
}
|
|
559
|
+
// 恢复进入分时前的 pane 比例(createSubPane 会重算 3:1 分配)
|
|
560
|
+
if (Object.keys(saved.paneRatios).length > 0) {
|
|
561
|
+
const specs = this.kernel.pane.readonly.paneSpecs.peek().map((pane) => ({
|
|
562
|
+
...pane,
|
|
563
|
+
ratio: saved.paneRatios[pane.id] ?? pane.ratio,
|
|
564
|
+
}))
|
|
565
|
+
this.kernel.pane.actions.commitLayout(saved.paneRatios, specs)
|
|
564
566
|
}
|
|
567
|
+
this.setRendererEnabled('candle', saved.candleEnabled)
|
|
565
568
|
}
|
|
566
569
|
this._savedTimeShareState = null
|
|
567
570
|
}
|
|
@@ -572,7 +575,7 @@ export class Chart {
|
|
|
572
575
|
}
|
|
573
576
|
|
|
574
577
|
getCurrentDpr(): number {
|
|
575
|
-
return this.
|
|
578
|
+
return this.kernel.viewport.readonly.dpr.peek()
|
|
576
579
|
}
|
|
577
580
|
|
|
578
581
|
/** 获取当前周期 */
|
|
@@ -580,25 +583,6 @@ export class Chart {
|
|
|
580
583
|
return this.dataManager.currentPeriod
|
|
581
584
|
}
|
|
582
585
|
|
|
583
|
-
/** 获取缓存的 scrollLeft(避免读取 DOM 触发强制回流) */
|
|
584
|
-
getCachedScrollLeft(): number {
|
|
585
|
-
return this.viewportManager.getCachedScrollLeft()
|
|
586
|
-
}
|
|
587
|
-
|
|
588
|
-
/** 同步程序性 scrollLeft 写入后的缓存,避免等待异步 scroll 事件 */
|
|
589
|
-
syncScrollLeft(scrollLeft: number): void {
|
|
590
|
-
this.viewportManager.setScrollLeft(scrollLeft)
|
|
591
|
-
}
|
|
592
|
-
|
|
593
|
-
setScrollLeft(v: number): void {
|
|
594
|
-
this.viewportManager.setScrollLeft(v)
|
|
595
|
-
}
|
|
596
|
-
|
|
597
|
-
/** 获取逻辑 scrollLeft(减去左侧加载缓冲宽度,可为负值) */
|
|
598
|
-
getLogicalScrollLeft(): number {
|
|
599
|
-
return this.viewportManager.getLogicalScrollLeft()
|
|
600
|
-
}
|
|
601
|
-
|
|
602
586
|
/** 获取插件宿主 */
|
|
603
587
|
get plugin(): PluginHostImpl {
|
|
604
588
|
return this.pluginHost
|
|
@@ -1211,14 +1195,14 @@ export class Chart {
|
|
|
1211
1195
|
resize() {
|
|
1212
1196
|
if (this._activeMode === this._timeShareMode) {
|
|
1213
1197
|
const tsData = this.dataManager.getTimeShareData()
|
|
1214
|
-
const vp = this.
|
|
1198
|
+
const vp = this.getViewport()
|
|
1215
1199
|
if (!vp || vp.plotWidth <= 0) return
|
|
1216
1200
|
if (tsData.length > 0) {
|
|
1217
1201
|
const result = this._activeMode.computeKWidth(tsData.length, vp.plotWidth, vp.dpr)
|
|
1218
1202
|
if (result) {
|
|
1219
1203
|
this.applyRenderState(result.kWidth, result.kGap)
|
|
1220
1204
|
const leftBuffer = this.getLeftLoadBufferWidth()
|
|
1221
|
-
this.
|
|
1205
|
+
this.kernel.viewport.actions.scrollTo(leftBuffer)
|
|
1222
1206
|
}
|
|
1223
1207
|
}
|
|
1224
1208
|
this.renderer.clearCachedFrame()
|
|
@@ -1226,7 +1210,7 @@ export class Chart {
|
|
|
1226
1210
|
this.scheduleDraw()
|
|
1227
1211
|
return
|
|
1228
1212
|
}
|
|
1229
|
-
const vp = this.
|
|
1213
|
+
const vp = this.getViewport()
|
|
1230
1214
|
// 防御性检查:容器尺寸无效时跳过布局
|
|
1231
1215
|
if (!vp || vp.viewWidth < 10 || vp.viewHeight < 10) {
|
|
1232
1216
|
return
|
|
@@ -1241,22 +1225,27 @@ export class Chart {
|
|
|
1241
1225
|
* @param level 更新级别,默认为 All
|
|
1242
1226
|
*/
|
|
1243
1227
|
scheduleDraw(level: UpdateLevel = UpdateLevel.All): void {
|
|
1228
|
+
// 正在批量安装/卸载 renderer(pane 重排、指标切换、子图增删),等全部完成再 flush
|
|
1244
1229
|
if (this.runtimeProjectionDepth > 0) {
|
|
1245
|
-
this.
|
|
1230
|
+
this.pendingProjectionLevel = this.pendingProjectionLevel
|
|
1231
|
+
? mergeUpdateLevel(this.pendingProjectionLevel, level)
|
|
1232
|
+
: level
|
|
1246
1233
|
return
|
|
1247
1234
|
}
|
|
1248
1235
|
this.renderer.scheduleDraw(level)
|
|
1249
1236
|
}
|
|
1250
1237
|
|
|
1238
|
+
/** 在 RendererPluginManager 事务内执行 run,推迟期间的所有 scheduleDraw 到最外层统一 flush */
|
|
1251
1239
|
private runRuntimeProjection(run: () => void): void {
|
|
1252
1240
|
this.runtimeProjectionDepth++
|
|
1253
1241
|
try {
|
|
1254
1242
|
this.rendererPluginManager.transaction(run)
|
|
1255
1243
|
} finally {
|
|
1256
1244
|
this.runtimeProjectionDepth--
|
|
1257
|
-
if (this.runtimeProjectionDepth === 0 && this.
|
|
1258
|
-
|
|
1259
|
-
this.
|
|
1245
|
+
if (this.runtimeProjectionDepth === 0 && this.pendingProjectionLevel !== null) {
|
|
1246
|
+
const level = this.pendingProjectionLevel
|
|
1247
|
+
this.pendingProjectionLevel = null
|
|
1248
|
+
this.renderer?.scheduleDraw(level)
|
|
1260
1249
|
}
|
|
1261
1250
|
}
|
|
1262
1251
|
}
|
|
@@ -1310,10 +1299,6 @@ export class Chart {
|
|
|
1310
1299
|
await this.pluginHost.destroy()
|
|
1311
1300
|
}
|
|
1312
1301
|
|
|
1313
|
-
private computeViewport(): Viewport | null {
|
|
1314
|
-
return this.viewportManager.computeViewport()
|
|
1315
|
-
}
|
|
1316
|
-
|
|
1317
1302
|
// ==================== Facade API (High-level interface for adapters) ====================
|
|
1318
1303
|
|
|
1319
1304
|
/** interactionSnapshot lazy computed for the createChartController facade */
|
|
@@ -1329,7 +1314,7 @@ export class Chart {
|
|
|
1329
1314
|
|
|
1330
1315
|
/** 视口状态信号 */
|
|
1331
1316
|
get viewport(): ReadonlySignal<ViewportState> {
|
|
1332
|
-
return this.
|
|
1317
|
+
return this.kernel.viewport.readonly.viewportState
|
|
1333
1318
|
}
|
|
1334
1319
|
|
|
1335
1320
|
/** 数据信号 */
|
|
@@ -1359,12 +1344,12 @@ export class Chart {
|
|
|
1359
1344
|
|
|
1360
1345
|
/** 比较商品颜色信号 */
|
|
1361
1346
|
get comparisonColors(): ReadonlySignal<ReadonlyMap<string, string>> {
|
|
1362
|
-
return this.
|
|
1347
|
+
return this.kernel.comparison.readonly.colors
|
|
1363
1348
|
}
|
|
1364
1349
|
|
|
1365
1350
|
/** 比较商品加载信号 */
|
|
1366
1351
|
get comparisonLoading(): ReadonlySignal<boolean> {
|
|
1367
|
-
return this.
|
|
1352
|
+
return this.kernel.comparison.readonly.loading
|
|
1368
1353
|
}
|
|
1369
1354
|
|
|
1370
1355
|
/** 生效主题信号(settings.theme + systemTheme 推导) */
|
|
@@ -1414,6 +1399,11 @@ export class Chart {
|
|
|
1414
1399
|
return this._interactionSnapshot
|
|
1415
1400
|
}
|
|
1416
1401
|
|
|
1402
|
+
/** 主图左上角图例模板上下文(null 表示无数据) */
|
|
1403
|
+
get legendTemplateContext(): ReadonlySignal<LegendTemplateContext | null> {
|
|
1404
|
+
return this._legendTemplateContext
|
|
1405
|
+
}
|
|
1406
|
+
|
|
1417
1407
|
// ---------- Data ----------
|
|
1418
1408
|
|
|
1419
1409
|
setData(data: KLineData[]): void {
|
package/src/engine/chartTypes.ts
CHANGED
|
@@ -22,7 +22,10 @@ export type PaneRendererDom = {
|
|
|
22
22
|
mainCanvas: HTMLCanvasElement
|
|
23
23
|
overlayCanvas: HTMLCanvasElement
|
|
24
24
|
yAxisCanvas: HTMLCanvasElement
|
|
25
|
+
/** 轴区动态层(最新价标签、十字线价签),叠在 yAxisCanvas 上 */
|
|
26
|
+
yAxisOverlayCanvas: HTMLCanvasElement
|
|
25
27
|
leftYAxisCanvas?: HTMLCanvasElement
|
|
28
|
+
leftYAxisOverlayCanvas?: HTMLCanvasElement
|
|
26
29
|
}
|
|
27
30
|
|
|
28
31
|
export type ChartOptions = {
|
|
@@ -58,7 +61,9 @@ export type ViewportState = {
|
|
|
58
61
|
plotWidth: number
|
|
59
62
|
plotHeight: number
|
|
60
63
|
dpr: number
|
|
64
|
+
/** 可索引可见起点(已 clamp start>=0) */
|
|
61
65
|
visibleFrom: number
|
|
66
|
+
/** 可索引可见终点(开区间) */
|
|
62
67
|
visibleTo: number
|
|
63
68
|
kWidth: number
|
|
64
69
|
kGap: number
|
|
@@ -223,8 +223,22 @@ function createChartStub(args: {
|
|
|
223
223
|
dpr: args.dpr,
|
|
224
224
|
}),
|
|
225
225
|
getCurrentDpr: () => args.dpr,
|
|
226
|
-
|
|
227
|
-
|
|
226
|
+
kernel: {
|
|
227
|
+
viewport: {
|
|
228
|
+
readonly: {
|
|
229
|
+
scrollLeft: { peek: () => 0 },
|
|
230
|
+
scrollLeftLogical: { peek: () => 0 },
|
|
231
|
+
},
|
|
232
|
+
actions: {
|
|
233
|
+
scrollTo: () => undefined,
|
|
234
|
+
},
|
|
235
|
+
},
|
|
236
|
+
settings: {
|
|
237
|
+
readonly: {
|
|
238
|
+
settings: { peek: () => ({}) },
|
|
239
|
+
},
|
|
240
|
+
},
|
|
241
|
+
},
|
|
228
242
|
getMarkerManager: () => markerManager,
|
|
229
243
|
getPaneRenderers: () => paneRenderers,
|
|
230
244
|
getData: () => data,
|
|
@@ -302,6 +316,29 @@ describe('InteractionController DPR consumption', () => {
|
|
|
302
316
|
expect(interaction.crosshairPos).toBeNull()
|
|
303
317
|
expect(interaction.crosshairIndex).toBeNull()
|
|
304
318
|
})
|
|
319
|
+
|
|
320
|
+
it('indexes bars from sealed frameVisibleRange, not stale viewport.visibleFrom', () => {
|
|
321
|
+
// hit-test 与 positions 必须同帧同源;即使 viewport 快照与 seal range 不一致,也以 seal 为准
|
|
322
|
+
const chart = createChartStub({ dpr: 1, plotWidth: 300, plotHeight: 160 })
|
|
323
|
+
chart.viewport.peek = () => ({
|
|
324
|
+
zoomLevel: 1,
|
|
325
|
+
plotWidth: 300,
|
|
326
|
+
plotHeight: 160,
|
|
327
|
+
dpr: 1,
|
|
328
|
+
visibleFrom: 99,
|
|
329
|
+
visibleTo: 101,
|
|
330
|
+
kWidth: 6,
|
|
331
|
+
kGap: 2,
|
|
332
|
+
})
|
|
333
|
+
const interaction = new InteractionController(chart as never, createMockInteractionState())
|
|
334
|
+
interaction.setKLinePositions([0, 10], { start: 0, end: 2 }, 10)
|
|
335
|
+
|
|
336
|
+
interaction.onPointerMove({ clientX: 5, clientY: 40, isPrimary: true } as PointerEvent)
|
|
337
|
+
interaction.flushPendingHover()
|
|
338
|
+
|
|
339
|
+
expect(interaction.crosshairIndex).toBe(0)
|
|
340
|
+
expect(interaction.crosshairPos).not.toBeNull()
|
|
341
|
+
})
|
|
305
342
|
})
|
|
306
343
|
|
|
307
344
|
describe('InteractionController pane capability gating', () => {
|
|
@@ -89,6 +89,8 @@ export class InteractionController {
|
|
|
89
89
|
private framePositions: number[] | null = null
|
|
90
90
|
private frameCenters: number[] | null = null
|
|
91
91
|
private frameKWidthPx: number | null = null
|
|
92
|
+
/** 与 framePositions 同代的可见区间(已 clamp start>=0,与 calcKLinePositions 一致) */
|
|
93
|
+
private frameVisibleRange: { start: number; end: number } | null = null
|
|
92
94
|
private markerState = new MarkerInteractionState()
|
|
93
95
|
private lastHoverRenderKey = ''
|
|
94
96
|
private useTooltipAnchorPositioning = false
|
|
@@ -211,7 +213,7 @@ export class InteractionController {
|
|
|
211
213
|
if (!location) return
|
|
212
214
|
|
|
213
215
|
const { mouseX, mouseY } = location
|
|
214
|
-
const scrollLeft = this.chart.
|
|
216
|
+
const scrollLeft = this.chart.kernel.viewport.readonly.scrollLeftLogical.peek()
|
|
215
217
|
|
|
216
218
|
const markerManager = this.chart.getMarkerManager()
|
|
217
219
|
const worldX = scrollLeft + mouseX
|
|
@@ -248,7 +250,7 @@ export class InteractionController {
|
|
|
248
250
|
// 触屏始终以 pan 模式开始,长按后才切换为 explore
|
|
249
251
|
this.dragStartX = e.clientX
|
|
250
252
|
this.dragStartY = e.clientY
|
|
251
|
-
this.scrollStartX = this.chart.
|
|
253
|
+
this.scrollStartX = this.chart.kernel.viewport.readonly.scrollLeft.peek()
|
|
252
254
|
this._cachedMaxScrollLeft = -1
|
|
253
255
|
const captureContainer = this.chart.getDom().container
|
|
254
256
|
captureContainer?.setPointerCapture(e.pointerId)
|
|
@@ -348,6 +350,7 @@ export class InteractionController {
|
|
|
348
350
|
this.framePositions = null
|
|
349
351
|
this.frameCenters = null
|
|
350
352
|
this.frameKWidthPx = null
|
|
353
|
+
this.frameVisibleRange = null
|
|
351
354
|
this.hoverFlushPending = false
|
|
352
355
|
this.clearHover()
|
|
353
356
|
if (options.scheduleDraw !== false) {
|
|
@@ -419,7 +422,7 @@ export class InteractionController {
|
|
|
419
422
|
}
|
|
420
423
|
const clamped = Math.min(Math.max(0, this.scrollStartX + deltaX), this._cachedMaxScrollLeft)
|
|
421
424
|
const dpr = this.chart.getCurrentDpr()
|
|
422
|
-
this.chart.
|
|
425
|
+
this.chart.kernel.viewport.actions.scrollTo(Math.round(clamped * dpr) / dpr)
|
|
423
426
|
|
|
424
427
|
const deltaY = e.clientY - this.dragStartY
|
|
425
428
|
this.dragStartY = e.clientY
|
|
@@ -467,26 +470,30 @@ export class InteractionController {
|
|
|
467
470
|
* ChartRenderer 在 paint 前调用;引用未变则跳过 hover 重算。
|
|
468
471
|
*
|
|
469
472
|
* @param positions K 线起始 x 坐标数组
|
|
470
|
-
* @param visibleRange
|
|
473
|
+
* @param visibleRange 与 positions 同代的可见区间(start 已 clamp 到 >=0)
|
|
471
474
|
* @param kWidthPx K 线宽度(物理像素)
|
|
472
475
|
* @param centers K 线中心 x 坐标数组
|
|
473
476
|
*/
|
|
474
477
|
setKLinePositions(
|
|
475
478
|
positions: number[] | null,
|
|
476
|
-
|
|
479
|
+
visibleRange: { start: number; end: number } | null,
|
|
477
480
|
kWidthPx?: number,
|
|
478
481
|
centers?: number[] | null,
|
|
479
482
|
) {
|
|
480
483
|
const nextWidth = kWidthPx ?? null
|
|
481
484
|
const nextCenters = centers ?? null
|
|
485
|
+
const nextRange = visibleRange
|
|
482
486
|
const unchanged =
|
|
483
487
|
this.framePositions === positions &&
|
|
484
488
|
this.frameCenters === nextCenters &&
|
|
485
|
-
this.frameKWidthPx === nextWidth
|
|
489
|
+
this.frameKWidthPx === nextWidth &&
|
|
490
|
+
this.frameVisibleRange?.start === nextRange?.start &&
|
|
491
|
+
this.frameVisibleRange?.end === nextRange?.end
|
|
486
492
|
|
|
487
493
|
this.framePositions = positions
|
|
488
494
|
this.frameCenters = nextCenters
|
|
489
495
|
this.frameKWidthPx = nextWidth
|
|
496
|
+
this.frameVisibleRange = nextRange
|
|
490
497
|
|
|
491
498
|
// 几何变化时标记 hover 待刷新;由 flushPendingHover 与 paint 同帧完成
|
|
492
499
|
if (!unchanged && this.lastClientPos && !this._state.readonly.isDragging.peek()) {
|
|
@@ -726,7 +733,7 @@ if (this.tooltipPositionMode === 'adaptive') {
|
|
|
726
733
|
return null
|
|
727
734
|
}
|
|
728
735
|
|
|
729
|
-
const scrollLeft = this.chart.
|
|
736
|
+
const scrollLeft = this.chart.kernel.viewport.readonly.scrollLeftLogical.peek()
|
|
730
737
|
const dpr = this.chart.getCurrentDpr()
|
|
731
738
|
|
|
732
739
|
return {
|
|
@@ -785,11 +792,11 @@ if (this.tooltipPositionMode === 'adaptive') {
|
|
|
785
792
|
private findNearestBar(ctx: HoverContext): NearestBar | null {
|
|
786
793
|
const kLinePositions = this.framePositions
|
|
787
794
|
const kWidthPx = this.frameKWidthPx
|
|
788
|
-
|
|
789
|
-
const visibleRange =
|
|
795
|
+
// 与 framePositions 同代的 seal range(viewport 已 clamp,此处仍用 seal 保证同帧一致)
|
|
796
|
+
const visibleRange = this.frameVisibleRange
|
|
790
797
|
if (!kLinePositions || !visibleRange || !kWidthPx) return null
|
|
791
798
|
|
|
792
|
-
const { worldX,
|
|
799
|
+
const { worldX, dpr, scrollLeft, plotWidth } = ctx
|
|
793
800
|
const kWidthLogical = kWidthPx / dpr
|
|
794
801
|
const positions = kLinePositions
|
|
795
802
|
|
|
@@ -993,6 +1000,7 @@ if (this.tooltipPositionMode === 'adaptive') {
|
|
|
993
1000
|
this.framePositions = null
|
|
994
1001
|
this.frameCenters = null
|
|
995
1002
|
this.frameKWidthPx = null
|
|
1003
|
+
this.frameVisibleRange = null
|
|
996
1004
|
this.lastHoverRenderKey = ''
|
|
997
1005
|
}
|
|
998
1006
|
|