실시간 차트 갱신시 화면떨림 문제 해결
This commit is contained in:
@@ -532,7 +532,10 @@ export class ChartManager {
|
||||
}
|
||||
|
||||
// ─── Indicators ─────────────────────────────────────────────────────────
|
||||
async addIndicator(config: IndicatorConfig): Promise<void> {
|
||||
async addIndicator(
|
||||
config: IndicatorConfig,
|
||||
options?: { skipLayout?: boolean },
|
||||
): Promise<void> {
|
||||
config = enrichIndicatorConfig(config);
|
||||
if (this.indicators.has(config.id) || this.rawBars.length === 0) return;
|
||||
const dataGenAtStart = this._dataGeneration;
|
||||
@@ -664,12 +667,50 @@ export class ChartManager {
|
||||
} catch (e) {
|
||||
console.error(`[Indicator] ${config.type} error:`, e);
|
||||
} finally {
|
||||
if (this._activeIndicatorPaneIndices().size > 0) {
|
||||
if (
|
||||
!options?.skipLayout
|
||||
&& this._activeIndicatorPaneIndices().size > 0
|
||||
) {
|
||||
this.resetPaneHeights(this._lastLayoutAvailableHeight);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** 여러 지표 추가 후 pane 레이아웃을 한 번만 갱신 */
|
||||
async addIndicatorsBatch(configs: IndicatorConfig[]): Promise<void> {
|
||||
for (const config of configs) {
|
||||
await this.addIndicator(config, { skipLayout: true });
|
||||
}
|
||||
if (configs.length > 0 && this._activeIndicatorPaneIndices().size > 0) {
|
||||
this.resetPaneHeights(this._lastLayoutAvailableHeight);
|
||||
this._notifyPaneLayout();
|
||||
}
|
||||
}
|
||||
|
||||
private _detachIndicatorEntry(id: string): boolean {
|
||||
const entry = this.indicators.get(id);
|
||||
if (!entry) return false;
|
||||
if (entry.seriesList.length > 0) {
|
||||
for (const pl of entry.hlineRefs) {
|
||||
try { entry.seriesList[0].removePriceLine(pl); } catch { /* ok */ }
|
||||
}
|
||||
if (entry.fillPrimitive) {
|
||||
try { entry.seriesList[0].detachPrimitive(entry.fillPrimitive); } catch { /* ok */ }
|
||||
}
|
||||
}
|
||||
if (entry.cloudPlugin) {
|
||||
for (const s of entry.seriesList) {
|
||||
try { s.detachPrimitive(entry.cloudPlugin); } catch { /* ok */ }
|
||||
}
|
||||
}
|
||||
detachBbBandFill(entry);
|
||||
for (const s of entry.seriesList) { try { this.chart.removeSeries(s); } catch { /* ok */ } }
|
||||
this.patternMarkers = this.patternMarkers.filter(p => p.id !== id);
|
||||
this._reapplyAllPatternMarkers();
|
||||
this.indicators.delete(id);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 순서 변경 전용 재로드 — 메인 차트·볼륨 시리즈는 유지하고 보조지표만 제거 후 재추가.
|
||||
* reloadAll 과 달리 setData()를 호출하지 않으므로 메인 캔들 차트가 깜빡이지 않는다.
|
||||
@@ -708,31 +749,32 @@ export class ChartManager {
|
||||
}
|
||||
|
||||
removeIndicator(id: string): void {
|
||||
const entry = this.indicators.get(id);
|
||||
if (!entry) return;
|
||||
// hlines price line 및 fill primitive 정리
|
||||
if (entry.seriesList.length > 0) {
|
||||
for (const pl of entry.hlineRefs) {
|
||||
try { entry.seriesList[0].removePriceLine(pl); } catch { /* ok */ }
|
||||
}
|
||||
if (entry.fillPrimitive) {
|
||||
try { entry.seriesList[0].detachPrimitive(entry.fillPrimitive); } catch { /* ok */ }
|
||||
}
|
||||
this.removeIndicators([id]);
|
||||
}
|
||||
|
||||
/** 여러 지표 제거 후 pane 레이아웃을 한 번만 갱신 (줌·스크롤 유지에 유리) */
|
||||
removeIndicators(ids: string[]): void {
|
||||
let any = false;
|
||||
for (const id of ids) {
|
||||
if (this._detachIndicatorEntry(id)) any = true;
|
||||
}
|
||||
if (entry.cloudPlugin) {
|
||||
for (const s of entry.seriesList) {
|
||||
try { s.detachPrimitive(entry.cloudPlugin); } catch { /* ok */ }
|
||||
}
|
||||
}
|
||||
for (const s of entry.seriesList) { try { this.chart.removeSeries(s); } catch { /* ok */ } }
|
||||
this.patternMarkers = this.patternMarkers.filter(p => p.id !== id);
|
||||
this._reapplyAllPatternMarkers();
|
||||
this.indicators.delete(id);
|
||||
this._removeExtraSubPanes();
|
||||
if (!any) return;
|
||||
this._trimTrailingEmptySubPanes();
|
||||
this.resetPaneHeights(this._lastLayoutAvailableHeight);
|
||||
this._notifyPaneLayout();
|
||||
}
|
||||
|
||||
/** 파라미터 변경된 지표만 제거 후 재추가 (전체 보조지표 재로드 회피) */
|
||||
async refreshIndicators(configs: IndicatorConfig[]): Promise<void> {
|
||||
if (configs.length === 0) return;
|
||||
let any = false;
|
||||
for (const c of configs) {
|
||||
if (this._detachIndicatorEntry(c.id)) any = true;
|
||||
}
|
||||
if (any) this._trimTrailingEmptySubPanes();
|
||||
await this.addIndicatorsBatch(configs);
|
||||
}
|
||||
|
||||
/** 메인 시리즈 마커 플러그인 해제 (setData 등 시리즈 교체 전 호출) */
|
||||
private _disposeMainMarkersPlugin(): void {
|
||||
if (!this.mainMarkersPlugin) return;
|
||||
@@ -2254,7 +2296,7 @@ export class ChartManager {
|
||||
return indices;
|
||||
}
|
||||
|
||||
/** pane 2+ 빈 sub-pane 제거 — 제거·재로드 후 고아 pane 이 높이를 나눠 가져가는 것 방지 */
|
||||
/** pane 2+ 빈 sub-pane 제거 — reloadIndicatorsOnly 전용 (모든 지표 시리즈 제거 후) */
|
||||
private _removeExtraSubPanes(): void {
|
||||
for (;;) {
|
||||
const panes = this.chart.panes();
|
||||
@@ -2268,6 +2310,21 @@ export class ChartManager {
|
||||
}
|
||||
}
|
||||
|
||||
/** 활성 지표가 없는 맨 끝 sub-pane 만 제거 — 단일 지표 제거 시 나머지 pane 유지 */
|
||||
private _trimTrailingEmptySubPanes(): void {
|
||||
for (;;) {
|
||||
const panes = this.chart.panes();
|
||||
if (panes.length <= 2) return;
|
||||
const last = panes.length - 1;
|
||||
if (this._activeIndicatorPaneIndices().has(last)) return;
|
||||
try {
|
||||
this.chart.removePane(last);
|
||||
} catch {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** chart-container 기준 Y(px) → pane index (0=캔들, 1=거래량, 2+=보조지표) */
|
||||
getPaneIndexAtChartY(chartY: number): number {
|
||||
const layouts = this.getPaneLayouts();
|
||||
@@ -2578,12 +2635,17 @@ export class ChartManager {
|
||||
|
||||
/** 실제 시리즈·구름이 붙은 보조지표 수 (부분 로드·중단 감지용) */
|
||||
getLoadedIndicatorCount(): number {
|
||||
let n = 0;
|
||||
return this.getLoadedIndicatorIds().size;
|
||||
}
|
||||
|
||||
/** 차트에 실제로 그려진 보조지표 id (부분 로드·증분 복구용) */
|
||||
getLoadedIndicatorIds(): Set<string> {
|
||||
const ids = new Set<string>();
|
||||
for (const entry of this.indicators.values()) {
|
||||
if (entry.config?.hidden === true) continue;
|
||||
if (entry.cloudPlugin || entry.seriesList.length > 0) n += 1;
|
||||
if (entry.cloudPlugin || entry.seriesList.length > 0) ids.add(entry.id);
|
||||
}
|
||||
return n;
|
||||
return ids;
|
||||
}
|
||||
|
||||
/** 현재 로드된 rawBars 개수 반환 (데이터 로드 여부 확인용) */
|
||||
|
||||
Reference in New Issue
Block a user