@363045841yyt/klinechart-core 0.8.1-alpha.3 → 0.8.1
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 +30 -4
- package/dist/controllers/createChartController.js.map +1 -1
- package/dist/controllers/types.d.ts +9 -2
- package/dist/controllers/types.d.ts.map +1 -1
- package/dist/data-fetchers/baostock.js +3 -3
- package/dist/data-fetchers/baostock.js.map +1 -1
- package/dist/data-fetchers/dataBuffer.d.ts +6 -1
- package/dist/data-fetchers/dataBuffer.d.ts.map +1 -1
- package/dist/data-fetchers/dataBuffer.js +88 -47
- package/dist/data-fetchers/dataBuffer.js.map +1 -1
- package/dist/data-fetchers/index.d.ts +1 -0
- package/dist/data-fetchers/index.d.ts.map +1 -1
- package/dist/data-fetchers/index.js +1 -0
- package/dist/data-fetchers/index.js.map +1 -1
- package/dist/data-fetchers/router.d.ts.map +1 -1
- package/dist/data-fetchers/router.js +3 -0
- package/dist/data-fetchers/router.js.map +1 -1
- package/dist/data-fetchers/tradingview.d.ts +3 -0
- package/dist/data-fetchers/tradingview.d.ts.map +1 -0
- package/dist/data-fetchers/tradingview.js +45 -0
- package/dist/data-fetchers/tradingview.js.map +1 -0
- package/dist/engine/chart.d.ts +34 -351
- package/dist/engine/chart.d.ts.map +1 -1
- package/dist/engine/chart.js +246 -1716
- package/dist/engine/chart.js.map +1 -1
- package/dist/engine/chartContext.d.ts +24 -0
- package/dist/engine/chartContext.d.ts.map +1 -0
- package/dist/engine/chartContext.js +19 -0
- package/dist/engine/chartContext.js.map +1 -0
- package/dist/engine/chartTypes.d.ts +77 -0
- package/dist/engine/chartTypes.d.ts.map +1 -0
- package/dist/engine/chartTypes.js +2 -0
- package/dist/engine/chartTypes.js.map +1 -0
- package/dist/engine/controller/interaction.d.ts +1 -0
- package/dist/engine/controller/interaction.d.ts.map +1 -1
- package/dist/engine/controller/interaction.js +9 -2
- package/dist/engine/controller/interaction.js.map +1 -1
- package/dist/engine/data/chartDataManager.d.ts +102 -0
- package/dist/engine/data/chartDataManager.d.ts.map +1 -0
- package/dist/engine/data/chartDataManager.js +590 -0
- package/dist/engine/data/chartDataManager.js.map +1 -0
- package/dist/engine/indicators/chartIndicatorManager.d.ts +102 -0
- package/dist/engine/indicators/chartIndicatorManager.d.ts.map +1 -0
- package/dist/engine/indicators/chartIndicatorManager.js +437 -0
- package/dist/engine/indicators/chartIndicatorManager.js.map +1 -0
- package/dist/engine/layout/chartPaneLayout.d.ts +53 -0
- package/dist/engine/layout/chartPaneLayout.d.ts.map +1 -0
- package/dist/engine/layout/chartPaneLayout.js +388 -0
- package/dist/engine/layout/chartPaneLayout.js.map +1 -0
- package/dist/engine/render/chartRenderer.d.ts +86 -0
- package/dist/engine/render/chartRenderer.d.ts.map +1 -0
- package/dist/engine/render/chartRenderer.js +438 -0
- package/dist/engine/render/chartRenderer.js.map +1 -0
- package/dist/engine/renderers/Indicator/mainIndicatorLegend.d.ts.map +1 -1
- package/dist/engine/renderers/Indicator/mainIndicatorLegend.js +73 -7
- package/dist/engine/renderers/Indicator/mainIndicatorLegend.js.map +1 -1
- package/dist/engine/renderers/comparisonLine.d.ts.map +1 -1
- package/dist/engine/renderers/comparisonLine.js +25 -11
- package/dist/engine/renderers/comparisonLine.js.map +1 -1
- package/dist/engine/subPaneManager.d.ts +27 -6
- package/dist/engine/subPaneManager.d.ts.map +1 -1
- package/dist/engine/subPaneManager.js +54 -56
- package/dist/engine/subPaneManager.js.map +1 -1
- package/dist/engine/utils/chartZoomController.d.ts +33 -0
- package/dist/engine/utils/chartZoomController.d.ts.map +1 -0
- package/dist/engine/utils/chartZoomController.js +66 -0
- package/dist/engine/utils/chartZoomController.js.map +1 -0
- package/dist/engine/viewport/chartViewportManager.d.ts +72 -0
- package/dist/engine/viewport/chartViewportManager.d.ts.map +1 -0
- package/dist/engine/viewport/chartViewportManager.js +249 -0
- package/dist/engine/viewport/chartViewportManager.js.map +1 -0
- package/dist/engine/viewport/viewport.js +1 -1
- package/dist/engine/viewport/viewport.js.map +1 -1
- package/dist/plugin/types.d.ts +1 -0
- package/dist/plugin/types.d.ts.map +1 -1
- package/dist/plugin/types.js.map +1 -1
- package/dist/tokens/theme-china.d.ts.map +1 -1
- package/dist/tokens/theme-china.js +0 -4
- package/dist/tokens/theme-china.js.map +1 -1
- package/dist/tokens/theme-dark.d.ts.map +1 -1
- package/dist/tokens/theme-dark.js +0 -4
- package/dist/tokens/theme-dark.js.map +1 -1
- package/dist/tokens/theme-light.d.ts.map +1 -1
- package/dist/tokens/theme-light.js +1 -5
- package/dist/tokens/theme-light.js.map +1 -1
- package/dist/tokens/types.d.ts +0 -4
- package/dist/tokens/types.d.ts.map +1 -1
- package/dist/types/price.d.ts +2 -0
- package/dist/types/price.d.ts.map +1 -1
- package/dist/types/price.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.d.ts.map +1 -1
- package/dist/version.js +1 -1
- package/dist/version.js.map +1 -1
- package/package.json +1 -1
- package/src/controllers/createChartController.ts +49 -13
- package/src/controllers/types.ts +9 -2
- package/src/data-fetchers/__tests__/dataBuffer.test.ts +77 -0
- package/src/data-fetchers/baostock.ts +3 -3
- package/src/data-fetchers/dataBuffer.ts +70 -22
- package/src/data-fetchers/index.ts +1 -0
- package/src/data-fetchers/router.ts +3 -0
- package/src/data-fetchers/tradingview.ts +48 -0
- package/src/engine/__tests__/subPaneManager.test.ts +154 -0
- package/src/engine/chart.ts +260 -2103
- package/src/engine/chartContext.ts +34 -0
- package/src/engine/chartTypes.ts +88 -0
- package/src/engine/controller/__tests__/interaction.dpr.test.ts +1 -0
- package/src/engine/controller/interaction.ts +10 -2
- package/src/engine/data/chartDataManager.ts +691 -0
- package/src/engine/indicators/__tests__/chartIndicatorManager.test.ts +103 -0
- package/src/engine/indicators/chartIndicatorManager.ts +566 -0
- package/src/engine/layout/chartPaneLayout.ts +474 -0
- package/src/engine/render/chartRenderer.ts +579 -0
- package/src/engine/renderers/Indicator/mainIndicatorLegend.ts +99 -13
- package/src/engine/renderers/comparisonLine.ts +25 -11
- package/src/engine/subPaneManager.ts +75 -59
- package/src/engine/utils/chartZoomController.ts +104 -0
- package/src/engine/viewport/chartViewportManager.ts +310 -0
- package/src/engine/viewport/viewport.ts +1 -1
- package/src/plugin/types.ts +1 -0
- package/src/tokens/__tests__/__snapshots__/baseline.test.ts.snap +1 -9
- package/src/tokens/theme-china.ts +0 -4
- package/src/tokens/theme-dark.ts +0 -4
- package/src/tokens/theme-light.ts +2 -6
- package/src/tokens/types.ts +0 -4
- package/src/types/price.ts +2 -0
- package/src/version.ts +1 -1
- package/src/engine/chart.d.ts +0 -619
|
@@ -0,0 +1,310 @@
|
|
|
1
|
+
import type { ChartDom, Viewport, ViewportState } from '../chartTypes'
|
|
2
|
+
import type { VisibleRange, UpdateLevel } from '../layout/pane'
|
|
3
|
+
import { createSignal, type Signal } from '../../reactivity/signal'
|
|
4
|
+
|
|
5
|
+
export interface ViewportDependencies {
|
|
6
|
+
getDom: () => ChartDom
|
|
7
|
+
getBottomAxisHeight: () => number
|
|
8
|
+
getLeftLoadBufferWidth: () => number
|
|
9
|
+
getZoomLevel: () => number
|
|
10
|
+
getLastVisibleRange: () => VisibleRange
|
|
11
|
+
getKWidth: () => number
|
|
12
|
+
getKGap: () => number
|
|
13
|
+
scheduleDraw: (level?: UpdateLevel) => void
|
|
14
|
+
onResizeCompleted: () => void
|
|
15
|
+
resizeSharedWebGLSurface: (plotWidth: number, plotHeight: number, dpr: number) => void
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export class ChartViewportManager {
|
|
19
|
+
private deps: ViewportDependencies
|
|
20
|
+
|
|
21
|
+
/** 精确 DPR(来自 ResizeObserver 的 devicePixelContentBoxSize) */
|
|
22
|
+
private preciseDpr = 0
|
|
23
|
+
|
|
24
|
+
/** 统一监听容器尺寸与 DPR 变化 */
|
|
25
|
+
private resizeObserver?: ResizeObserver
|
|
26
|
+
|
|
27
|
+
/** scroll 事件处理器引用(用于 cleanup) */
|
|
28
|
+
private onScroll?: () => void
|
|
29
|
+
|
|
30
|
+
/** 最近一次观测到的容器尺寸 */
|
|
31
|
+
private observedSize = { width: 0, height: 0 }
|
|
32
|
+
|
|
33
|
+
/** 缓存的 scrollLeft(通过 scroll 事件同步,避免每帧读取 DOM 触发强制回流) */
|
|
34
|
+
private cachedScrollLeft = 0
|
|
35
|
+
|
|
36
|
+
/** 待写入 DOM 的 scrollLeft(在 RAF 回调中应用,确保 Vue 已完成 DOM 更新) */
|
|
37
|
+
private _pendingScrollLeft: number | null = null
|
|
38
|
+
|
|
39
|
+
/** 内部视口状态 */
|
|
40
|
+
private _internalViewport: Viewport | null = null
|
|
41
|
+
|
|
42
|
+
/** 视口状态信号 */
|
|
43
|
+
private _viewportSignal = createSignal<ViewportState>({
|
|
44
|
+
zoomLevel: 1,
|
|
45
|
+
plotWidth: 0,
|
|
46
|
+
plotHeight: 0,
|
|
47
|
+
dpr: 1,
|
|
48
|
+
visibleFrom: 0,
|
|
49
|
+
visibleTo: 0,
|
|
50
|
+
kWidth: 0,
|
|
51
|
+
kGap: 1,
|
|
52
|
+
})
|
|
53
|
+
|
|
54
|
+
constructor(deps: ViewportDependencies) {
|
|
55
|
+
this.deps = deps
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/** 视口状态信号 */
|
|
59
|
+
get viewportSignal(): Signal<ViewportState> {
|
|
60
|
+
return this._viewportSignal
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/** 获取缓存的 scrollLeft(避免读取 DOM 触发强制回流) */
|
|
64
|
+
getCachedScrollLeft(): number {
|
|
65
|
+
return this.cachedScrollLeft
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/** 获取逻辑 scrollLeft(减去左侧加载缓冲宽度,可为负值) */
|
|
69
|
+
getLogicalScrollLeft(): number {
|
|
70
|
+
return this.cachedScrollLeft - this.deps.getLeftLoadBufferWidth()
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/** 获取当前视口 */
|
|
74
|
+
getViewport(): Viewport | null {
|
|
75
|
+
return this._internalViewport
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/** 获取有效 DPR */
|
|
79
|
+
getEffectiveDpr(): number {
|
|
80
|
+
let dpr = this.preciseDpr > 0
|
|
81
|
+
? this.preciseDpr
|
|
82
|
+
: Math.round((window.devicePixelRatio || 1) * 64) / 64
|
|
83
|
+
if (dpr < 1) dpr = 1
|
|
84
|
+
return dpr
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/** 获取观测到的容器尺寸 */
|
|
88
|
+
getObservedSize(): { width: number; height: number } {
|
|
89
|
+
return this.observedSize
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/** 设置滚动位置(缓存 + 待写入) */
|
|
93
|
+
setScrollLeft(v: number): void {
|
|
94
|
+
this.cachedScrollLeft = v
|
|
95
|
+
this._pendingScrollLeft = v
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/** 仅设置缓存 scrollLeft(由 DataManager 内部使用) */
|
|
99
|
+
setCachedScrollLeft(v: number): void {
|
|
100
|
+
this.cachedScrollLeft = v
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/** 仅设置待写入 scrollLeft(由 DataManager 内部使用) */
|
|
104
|
+
setPendingScrollLeft(v: number): void {
|
|
105
|
+
this._pendingScrollLeft = v
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/** 在 RAF 回调中应用待写入的 scrollLeft */
|
|
109
|
+
applyPendingScrollLeft(container: HTMLElement): void {
|
|
110
|
+
if (this._pendingScrollLeft !== null) {
|
|
111
|
+
container.scrollLeft = this._pendingScrollLeft
|
|
112
|
+
this.cachedScrollLeft = container.scrollLeft
|
|
113
|
+
this._pendingScrollLeft = null
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/** 初始化 ResizeObserver 和 scroll 监听 */
|
|
118
|
+
init(): void {
|
|
119
|
+
if (typeof ResizeObserver === 'undefined') return
|
|
120
|
+
|
|
121
|
+
const target = this.deps.getDom().container
|
|
122
|
+
if (!target) return
|
|
123
|
+
|
|
124
|
+
// 初始化 scrollLeft 缓存
|
|
125
|
+
this.cachedScrollLeft = target.scrollLeft
|
|
126
|
+
this.onScroll = () => { this.cachedScrollLeft = target.scrollLeft }
|
|
127
|
+
target.addEventListener('scroll', this.onScroll, { passive: true })
|
|
128
|
+
|
|
129
|
+
this.resizeObserver = new ResizeObserver((entries) => {
|
|
130
|
+
const entry = entries[0]
|
|
131
|
+
if (!entry) return
|
|
132
|
+
|
|
133
|
+
const prevWidth = this.observedSize.width
|
|
134
|
+
const prevHeight = this.observedSize.height
|
|
135
|
+
const prevDpr = this.preciseDpr
|
|
136
|
+
|
|
137
|
+
this.updateObservedMetrics(entry)
|
|
138
|
+
|
|
139
|
+
const widthChanged = this.observedSize.width !== prevWidth
|
|
140
|
+
const heightChanged = this.observedSize.height !== prevHeight
|
|
141
|
+
const dprChanged = this.preciseDpr !== prevDpr
|
|
142
|
+
if ((import.meta as any).env?.MODE !== 'production') {
|
|
143
|
+
console.log(
|
|
144
|
+
`[Chart] resize observer: ` +
|
|
145
|
+
`size ${prevWidth}x${prevHeight} -> ${this.observedSize.width}x${this.observedSize.height} ` +
|
|
146
|
+
`dpr ${prevDpr} -> ${this.preciseDpr} ` +
|
|
147
|
+
`changed: ${widthChanged || heightChanged ? 'size' : ''}${widthChanged || heightChanged && dprChanged ? '+' : ''}${dprChanged ? 'dpr' : ''}`
|
|
148
|
+
)
|
|
149
|
+
}
|
|
150
|
+
if (widthChanged || heightChanged || dprChanged) {
|
|
151
|
+
this.deps.onResizeCompleted()
|
|
152
|
+
}
|
|
153
|
+
})
|
|
154
|
+
|
|
155
|
+
try {
|
|
156
|
+
this.resizeObserver.observe(target, { box: 'device-pixel-content-box' as ResizeObserverBoxOptions })
|
|
157
|
+
} catch {
|
|
158
|
+
this.resizeObserver.observe(target)
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/** 销毁 */
|
|
163
|
+
destroy(): void {
|
|
164
|
+
this.resizeObserver?.disconnect()
|
|
165
|
+
this.resizeObserver = undefined
|
|
166
|
+
this.preciseDpr = 0
|
|
167
|
+
this.observedSize = { width: 0, height: 0 }
|
|
168
|
+
|
|
169
|
+
if (this.onScroll) {
|
|
170
|
+
this.deps.getDom().container?.removeEventListener('scroll', this.onScroll)
|
|
171
|
+
this.onScroll = undefined
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
this._internalViewport = null
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* 计算视口
|
|
179
|
+
*/
|
|
180
|
+
computeViewport(): Viewport | null {
|
|
181
|
+
const container = this.deps.getDom().container
|
|
182
|
+
if (!container) return null
|
|
183
|
+
|
|
184
|
+
const observedWidth = this.observedSize.width
|
|
185
|
+
const observedHeight = this.observedSize.height
|
|
186
|
+
const viewWidth = observedWidth > 0
|
|
187
|
+
? observedWidth
|
|
188
|
+
: Math.max(1, Math.round(container.clientWidth))
|
|
189
|
+
const viewHeight = observedHeight > 0
|
|
190
|
+
? observedHeight
|
|
191
|
+
: Math.max(1, Math.round(container.clientHeight))
|
|
192
|
+
|
|
193
|
+
const plotWidth = Math.round(viewWidth)
|
|
194
|
+
const plotHeight = Math.round(viewHeight - this.deps.getBottomAxisHeight())
|
|
195
|
+
|
|
196
|
+
let dpr = this.getEffectiveDpr()
|
|
197
|
+
|
|
198
|
+
const MAX_CANVAS_PIXELS = 16 * 1024 * 1024
|
|
199
|
+
const requestedPixels = viewWidth * dpr * (viewHeight * dpr)
|
|
200
|
+
if (requestedPixels > MAX_CANVAS_PIXELS) {
|
|
201
|
+
dpr = Math.sqrt(MAX_CANVAS_PIXELS / (viewWidth * viewHeight))
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
// 对齐 scrollLeft,消除 translate 亚像素偏移
|
|
205
|
+
const scrollLeft = Math.round(this.getLogicalScrollLeft() * dpr) / dpr
|
|
206
|
+
|
|
207
|
+
const dom = this.deps.getDom()
|
|
208
|
+
|
|
209
|
+
const canvasLayerWidth = `${viewWidth}px`
|
|
210
|
+
if (dom.canvasLayer.style.width !== canvasLayerWidth) {
|
|
211
|
+
dom.canvasLayer.style.width = canvasLayerWidth
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
const canvasLayerHeight = `${viewHeight}px`
|
|
215
|
+
if (dom.canvasLayer.style.height !== canvasLayerHeight) {
|
|
216
|
+
dom.canvasLayer.style.height = canvasLayerHeight
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
const xAxisWidth = Math.round(plotWidth * dpr)
|
|
220
|
+
if (dom.xAxisCanvas.width !== xAxisWidth) {
|
|
221
|
+
dom.xAxisCanvas.width = xAxisWidth
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
const xAxisHeight = Math.round(this.deps.getBottomAxisHeight() * dpr)
|
|
225
|
+
if (dom.xAxisCanvas.height !== xAxisHeight) {
|
|
226
|
+
dom.xAxisCanvas.height = xAxisHeight
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
const xAxisCssWidth = `${xAxisWidth / dpr}px`
|
|
230
|
+
if (dom.xAxisCanvas.style.width !== xAxisCssWidth) {
|
|
231
|
+
dom.xAxisCanvas.style.width = xAxisCssWidth
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
const xAxisCssHeight = `${xAxisHeight / dpr}px`
|
|
235
|
+
if (dom.xAxisCanvas.style.height !== xAxisCssHeight) {
|
|
236
|
+
dom.xAxisCanvas.style.height = xAxisCssHeight
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
this.deps.resizeSharedWebGLSurface(plotWidth, plotHeight, dpr)
|
|
240
|
+
|
|
241
|
+
const vp: Viewport = {
|
|
242
|
+
viewWidth,
|
|
243
|
+
viewHeight,
|
|
244
|
+
plotWidth,
|
|
245
|
+
plotHeight,
|
|
246
|
+
scrollLeft,
|
|
247
|
+
dpr,
|
|
248
|
+
}
|
|
249
|
+
const prevViewport = this._internalViewport
|
|
250
|
+
const viewportChanged = !prevViewport
|
|
251
|
+
|| prevViewport.viewWidth !== vp.viewWidth
|
|
252
|
+
|| prevViewport.viewHeight !== vp.viewHeight
|
|
253
|
+
|| prevViewport.plotWidth !== vp.plotWidth
|
|
254
|
+
|| prevViewport.plotHeight !== vp.plotHeight
|
|
255
|
+
|| prevViewport.scrollLeft !== vp.scrollLeft
|
|
256
|
+
|| prevViewport.dpr !== vp.dpr
|
|
257
|
+
|
|
258
|
+
this._internalViewport = vp
|
|
259
|
+
if (viewportChanged) {
|
|
260
|
+
const current = this._viewportSignal.peek()
|
|
261
|
+
this._viewportSignal.set({
|
|
262
|
+
zoomLevel: current.zoomLevel,
|
|
263
|
+
plotWidth: vp.plotWidth,
|
|
264
|
+
plotHeight: vp.plotHeight,
|
|
265
|
+
dpr: vp.dpr > 0 ? vp.dpr : current.dpr,
|
|
266
|
+
visibleFrom: current.visibleFrom,
|
|
267
|
+
visibleTo: current.visibleTo,
|
|
268
|
+
kWidth: current.kWidth,
|
|
269
|
+
kGap: current.kGap,
|
|
270
|
+
})
|
|
271
|
+
}
|
|
272
|
+
return vp
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
/**
|
|
276
|
+
* 更新 viewport signal(用于滚动事件/缩放后的信号同步)
|
|
277
|
+
*/
|
|
278
|
+
updateViewportSignal(): void {
|
|
279
|
+
const vp = this._internalViewport
|
|
280
|
+
if (!vp) return
|
|
281
|
+
|
|
282
|
+
this._viewportSignal.set({
|
|
283
|
+
zoomLevel: this.deps.getZoomLevel(),
|
|
284
|
+
plotWidth: vp.plotWidth,
|
|
285
|
+
plotHeight: vp.plotHeight,
|
|
286
|
+
dpr: vp.dpr,
|
|
287
|
+
visibleFrom: this.deps.getLastVisibleRange().start,
|
|
288
|
+
visibleTo: this.deps.getLastVisibleRange().end,
|
|
289
|
+
kWidth: this.deps.getKWidth(),
|
|
290
|
+
kGap: this.deps.getKGap(),
|
|
291
|
+
})
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
private updateObservedMetrics(entry: ResizeObserverEntry) {
|
|
295
|
+
const cssWidth = Math.max(1, Math.round(entry.contentRect.width))
|
|
296
|
+
const cssHeight = Math.max(1, Math.round(entry.contentRect.height))
|
|
297
|
+
this.observedSize.width = cssWidth
|
|
298
|
+
this.observedSize.height = cssHeight
|
|
299
|
+
|
|
300
|
+
const pixelSize = entry.devicePixelContentBoxSize?.[0]
|
|
301
|
+
const cssSize = entry.contentBoxSize?.[0]
|
|
302
|
+
if (!pixelSize || !cssSize || cssSize.inlineSize <= 0) {
|
|
303
|
+
this.preciseDpr = 0
|
|
304
|
+
return
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
const raw = pixelSize.inlineSize / cssSize.inlineSize
|
|
308
|
+
this.preciseDpr = Math.round(raw * 64) / 64
|
|
309
|
+
}
|
|
310
|
+
}
|
|
@@ -31,7 +31,7 @@ export function getVisibleRange(
|
|
|
31
31
|
const viewWidthPx = viewWidth * dpr
|
|
32
32
|
|
|
33
33
|
// 计算可见范围(物理像素空间整数运算)
|
|
34
|
-
const start = Math.
|
|
34
|
+
const start = Math.floor((scrollLeftPx - startXPx) / unitPx) - 1
|
|
35
35
|
const end = Math.min(totalDataCount, Math.ceil((scrollLeftPx + viewWidthPx - startXPx) / unitPx) + 1)
|
|
36
36
|
|
|
37
37
|
return { start, end }
|
package/src/plugin/types.ts
CHANGED
|
@@ -275,6 +275,7 @@ export interface RenderContext {
|
|
|
275
275
|
data: unknown[]
|
|
276
276
|
comparisonData?: ReadonlyMap<string, ReadonlyArray<KLineData>>
|
|
277
277
|
comparisonSymbols?: ReadonlyArray<import('../controllers/types').SymbolSpec>
|
|
278
|
+
comparisonColors?: ReadonlyMap<string, string>
|
|
278
279
|
range: { start: number; end: number }
|
|
279
280
|
scrollLeft: number
|
|
280
281
|
kWidth: number
|
|
@@ -57,10 +57,6 @@ exports[`theme baseline — dark > CSS declaration block (snapshot) 1`] = `
|
|
|
57
57
|
--klc-color-text-tertiary: hsl(210, 6%, 60%);
|
|
58
58
|
--klc-color-text-weak: hsl(210, 5%, 45%);
|
|
59
59
|
--klc-color-text-white: rgba(255, 255, 255, 0.95);
|
|
60
|
-
--klc-color-price-up-light: rgba(255, 80, 100, 0.85);
|
|
61
|
-
--klc-color-price-up-tick: hsl(0, 70%, 60%);
|
|
62
|
-
--klc-color-price-down-light: rgba(60, 200, 160, 0.85);
|
|
63
|
-
--klc-color-price-down-tick: hsl(150, 50%, 65%);
|
|
64
60
|
--klc-color-price-last-price: rgba(230, 100, 115, 0.95);
|
|
65
61
|
--klc-color-tag-bg-white: rgb(40, 40, 55);
|
|
66
62
|
--klc-color-tag-bg-light-gray: rgba(50, 50, 65, 0.92);
|
|
@@ -254,11 +250,7 @@ exports[`theme baseline — light > CSS declaration block (snapshot) 1`] = `
|
|
|
254
250
|
--klc-color-text-tertiary: hsl(210, 8%, 50%);
|
|
255
251
|
--klc-color-text-weak: hsl(210, 7%, 65%);
|
|
256
252
|
--klc-color-text-white: rgba(255, 255, 255, 0.92);
|
|
257
|
-
--klc-color-price-
|
|
258
|
-
--klc-color-price-up-tick: hsl(0, 60%, 50%);
|
|
259
|
-
--klc-color-price-down-light: rgba(3, 123, 102, 0.92);
|
|
260
|
-
--klc-color-price-down-tick: hsl(150, 30%, 60%);
|
|
261
|
-
--klc-color-price-last-price: rgba(196, 74, 86, 0.95);
|
|
253
|
+
--klc-color-price-last-price: rgba(230, 100, 115, 0.95);
|
|
262
254
|
--klc-color-tag-bg-white: rgb(255, 255, 255);
|
|
263
255
|
--klc-color-tag-bg-light-gray: rgba(255, 255, 255, 0.92);
|
|
264
256
|
--klc-color-tag-bg-pure-white: #ffffff;
|
|
@@ -56,10 +56,6 @@ export function withAsiaMarketColors(theme: Theme): Theme {
|
|
|
56
56
|
// ── Nested: price accents ──
|
|
57
57
|
price: {
|
|
58
58
|
...theme.colors.price,
|
|
59
|
-
upLight: theme.colors.price.downLight,
|
|
60
|
-
downLight: theme.colors.price.upLight,
|
|
61
|
-
upTick: theme.colors.price.downTick,
|
|
62
|
-
downTick: theme.colors.price.upTick,
|
|
63
59
|
},
|
|
64
60
|
|
|
65
61
|
// ── Nested: MACD histogram bars ──
|
package/src/tokens/theme-dark.ts
CHANGED
|
@@ -96,10 +96,6 @@ export const darkTheme: Theme = {
|
|
|
96
96
|
white: 'rgba(255, 255, 255, 0.95)',
|
|
97
97
|
},
|
|
98
98
|
price: {
|
|
99
|
-
upLight: 'rgba(255, 80, 100, 0.85)',
|
|
100
|
-
upTick: 'hsl(0, 70%, 60%)',
|
|
101
|
-
downLight: 'rgba(60, 200, 160, 0.85)',
|
|
102
|
-
downTick: 'hsl(150, 50%, 65%)',
|
|
103
99
|
lastPrice: 'rgba(230, 100, 115, 0.95)',
|
|
104
100
|
},
|
|
105
101
|
tagBg: {
|
|
@@ -101,12 +101,8 @@ export const lightTheme: Theme = {
|
|
|
101
101
|
weak: 'hsl(210, 7%, 65%)',
|
|
102
102
|
white: 'rgba(255, 255, 255, 0.92)',
|
|
103
103
|
},
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
upTick: 'hsl(0, 60%, 50%)',
|
|
107
|
-
downLight: 'rgba(3, 123, 102, 0.92)',
|
|
108
|
-
downTick: 'hsl(150, 30%, 60%)',
|
|
109
|
-
lastPrice: 'rgba(196, 74, 86, 0.95)',
|
|
104
|
+
price: {
|
|
105
|
+
lastPrice: 'rgba(230, 100, 115, 0.95)',
|
|
110
106
|
},
|
|
111
107
|
tagBg: {
|
|
112
108
|
white: 'rgb(255, 255, 255)',
|
package/src/tokens/types.ts
CHANGED
|
@@ -85,10 +85,6 @@ export interface TextColors {
|
|
|
85
85
|
* (candleUpBody / candleDownBody); this group covers extras.
|
|
86
86
|
*/
|
|
87
87
|
export interface PriceColors {
|
|
88
|
-
readonly upLight: ColorValue
|
|
89
|
-
readonly upTick: ColorValue
|
|
90
|
-
readonly downLight: ColorValue
|
|
91
|
-
readonly downTick: ColorValue
|
|
92
88
|
readonly lastPrice: ColorValue
|
|
93
89
|
}
|
|
94
90
|
|
package/src/types/price.ts
CHANGED
package/src/version.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const VERSION = "0.8.1
|
|
1
|
+
export const VERSION = "0.8.1"
|