fix(web): 分析/自动刷新后保留 K 线视窗位置
拆分手动分析与自动刷新拉数路径;全量重建用 logical 优先恢复视窗, 增量 recent 用 scroll+barDelta;避免 barSpacing 重锚与重复冻结导致往右跳。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -106,6 +106,32 @@ function syncFxBoxVerticalOverlay(mainChart, mainChartContainer) {
|
||||
}
|
||||
mainChartContainer.appendChild(canvas);
|
||||
}
|
||||
var lastSig = '';
|
||||
var watchRaf = null;
|
||||
var cleaned = false;
|
||||
var redrawPending = false;
|
||||
var quant = function (v) {
|
||||
if (v == null || !isFinite(Number(v))) return 'n';
|
||||
return String(Math.round(Number(v)));
|
||||
};
|
||||
// LWC 4 无 priceScale 订阅:采样坐标变化(含增量 setData 后自动缩放)
|
||||
var sampleSig = function () {
|
||||
var boxes = window._fxBoxVerticals || [];
|
||||
var series = getMainPriceSeries();
|
||||
if (!series || !boxes.length) return '0';
|
||||
var ts = mainChart.timeScale();
|
||||
var a = boxes[0];
|
||||
var b = boxes[boxes.length - 1];
|
||||
return [
|
||||
boxes.length,
|
||||
quant(ts.timeToCoordinate(a.time)),
|
||||
quant(series.priceToCoordinate(a.hi)),
|
||||
quant(series.priceToCoordinate(a.lo)),
|
||||
quant(ts.timeToCoordinate(b.time)),
|
||||
quant(series.priceToCoordinate(b.hi)),
|
||||
quant(series.priceToCoordinate(b.lo))
|
||||
].join('|');
|
||||
};
|
||||
var redraw = function () {
|
||||
var boxes = window._fxBoxVerticals || [];
|
||||
var series = getMainPriceSeries();
|
||||
@@ -119,7 +145,10 @@ function syncFxBoxVerticalOverlay(mainChart, mainChartContainer) {
|
||||
if (!ctx2) return;
|
||||
ctx2.setTransform(dpr, 0, 0, dpr, 0, 0);
|
||||
ctx2.clearRect(0, 0, rect.width, rect.height);
|
||||
if (!series || !boxes.length) return;
|
||||
if (!series || !boxes.length) {
|
||||
lastSig = sampleSig();
|
||||
return;
|
||||
}
|
||||
var ts = mainChart.timeScale();
|
||||
for (var i = 0; i < boxes.length; i++) {
|
||||
var box = boxes[i];
|
||||
@@ -136,25 +165,48 @@ function syncFxBoxVerticalOverlay(mainChart, mainChartContainer) {
|
||||
ctx2.stroke();
|
||||
}
|
||||
ctx2.setLineDash([]);
|
||||
lastSig = sampleSig();
|
||||
};
|
||||
var onRange = function () { requestAnimationFrame(redraw); };
|
||||
try { mainChart.timeScale().subscribeVisibleLogicalRangeChange(onRange); } catch (e) {}
|
||||
try { mainChart.timeScale().subscribeVisibleTimeRangeChange(onRange); } catch (e) {}
|
||||
var scheduleRedraw = function () {
|
||||
if (cleaned || redrawPending) return;
|
||||
redrawPending = true;
|
||||
requestAnimationFrame(function () {
|
||||
redrawPending = false;
|
||||
if (!cleaned) redraw();
|
||||
});
|
||||
};
|
||||
var watch = function () {
|
||||
if (cleaned) return;
|
||||
watchRaf = requestAnimationFrame(watch);
|
||||
var sig = sampleSig();
|
||||
if (sig !== lastSig) scheduleRedraw();
|
||||
};
|
||||
try { mainChart.timeScale().subscribeVisibleLogicalRangeChange(scheduleRedraw); } catch (e) {}
|
||||
try { mainChart.timeScale().subscribeVisibleTimeRangeChange(scheduleRedraw); } catch (e) {}
|
||||
var ro = null;
|
||||
if (typeof ResizeObserver !== 'undefined') {
|
||||
ro = new ResizeObserver(onRange);
|
||||
ro = new ResizeObserver(scheduleRedraw);
|
||||
ro.observe(mainChartContainer);
|
||||
}
|
||||
window._redrawFxBoxVerticalOverlay = scheduleRedraw;
|
||||
window._fxBoxOverlayCleanup = function () {
|
||||
try { mainChart.timeScale().unsubscribeVisibleLogicalRangeChange(onRange); } catch (e) {}
|
||||
try { mainChart.timeScale().unsubscribeVisibleTimeRangeChange(onRange); } catch (e) {}
|
||||
if (cleaned) return;
|
||||
cleaned = true;
|
||||
if (watchRaf != null) {
|
||||
try { cancelAnimationFrame(watchRaf); } catch (e) {}
|
||||
watchRaf = null;
|
||||
}
|
||||
window._redrawFxBoxVerticalOverlay = null;
|
||||
try { mainChart.timeScale().unsubscribeVisibleLogicalRangeChange(scheduleRedraw); } catch (e) {}
|
||||
try { mainChart.timeScale().unsubscribeVisibleTimeRangeChange(scheduleRedraw); } catch (e) {}
|
||||
if (ro) try { ro.disconnect(); } catch (e) {}
|
||||
try { if (canvas && canvas.parentNode) canvas.parentNode.removeChild(canvas); } catch (e) {}
|
||||
};
|
||||
if (!window._tvInitCleanups) window._tvInitCleanups = [];
|
||||
window._tvInitCleanups.push(window._fxBoxOverlayCleanup);
|
||||
requestAnimationFrame(redraw);
|
||||
setTimeout(redraw, 50);
|
||||
scheduleRedraw();
|
||||
setTimeout(scheduleRedraw, 50);
|
||||
watchRaf = requestAnimationFrame(watch);
|
||||
}
|
||||
|
||||
function chartTvRenderOverlays(ctx) {
|
||||
@@ -2137,28 +2189,11 @@ function chartTvRenderOverlays(ctx) {
|
||||
});
|
||||
safeOverlayLineSetData(bottomSeries, [{ time: startTs, value: boxLow }, { time: endTs, value: boxLow }]);
|
||||
|
||||
const leftSeries = mainChart.addLineSeries({
|
||||
color: boxColor,
|
||||
lineWidth: 1,
|
||||
lineStyle: 2,
|
||||
lastValueVisible: false,
|
||||
priceLineVisible: false,
|
||||
crosshairMarkerVisible: false,
|
||||
});
|
||||
safeOverlayLineSetData(leftSeries, [{ time: startTs, value: boxLow }, { time: startTs, value: boxHigh }]);
|
||||
|
||||
const rightSeries = mainChart.addLineSeries({
|
||||
color: boxColor,
|
||||
lineWidth: 1,
|
||||
lineStyle: 2,
|
||||
lastValueVisible: false,
|
||||
priceLineVisible: false,
|
||||
crosshairMarkerVisible: false,
|
||||
});
|
||||
safeOverlayLineSetData(rightSeries, [{ time: endTs, value: boxLow }, { time: endTs, value: boxHigh }]);
|
||||
pushFxBoxVertical(startTs, boxLow, boxHigh, boxColor);
|
||||
pushFxBoxVertical(endTs, boxLow, boxHigh, boxColor);
|
||||
|
||||
if (!tvWidget.series.subSubKlcFxBoxSeries) tvWidget.series.subSubKlcFxBoxSeries = [];
|
||||
tvWidget.series.subSubKlcFxBoxSeries.push(topSeries, bottomSeries, leftSeries, rightSeries);
|
||||
tvWidget.series.subSubKlcFxBoxSeries.push(topSeries, bottomSeries);
|
||||
}
|
||||
}
|
||||
} catch (e) { console.error('绘制次次周期KLC分型标记出错:', e); }
|
||||
@@ -2461,4 +2496,11 @@ function chartTvRenderOverlays(ctx) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// KLC 分型框竖边:canvas 真竖线(LWC 折线做不到不斜)
|
||||
try {
|
||||
syncFxBoxVerticalOverlay(mainChart, mainChartContainer);
|
||||
} catch (e) {
|
||||
console.warn('分型竖边 overlay 失败:', e);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user