fix(web): 分析/自动刷新后保留 K 线视窗位置
拆分手动分析与自动刷新拉数路径;全量重建用 logical 优先恢复视窗, 增量 recent 用 scroll+barDelta;避免 barSpacing 重锚与重复冻结导致往右跳。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
+29
-34
@@ -339,7 +339,7 @@ $(document).ready(function() {
|
||||
startAStockStatusUpdater();
|
||||
// A 股:metadata 完成后再拉数(下方不再重复 updateChart)
|
||||
setTimeout(function() {
|
||||
updateChart();
|
||||
analyzeChart({ reason: 'astock-init' });
|
||||
}, 300);
|
||||
});
|
||||
}
|
||||
@@ -378,10 +378,10 @@ $(document).ready(function() {
|
||||
// 尝试加载更多交易对
|
||||
loadSymbols();
|
||||
|
||||
// 初始化图表:默认加密货币延迟拉取;若首屏为 A 股则在 chart_metadata 完成后再 updateChart
|
||||
// 初始化图表:默认加密货币延迟拉取;若首屏为 A 股则在 chart_metadata 完成后再 analyze
|
||||
if (initialDataSource !== 'a_stock') {
|
||||
setTimeout(function() {
|
||||
updateChart();
|
||||
analyzeChart({ reason: 'crypto-init' });
|
||||
}, 500);
|
||||
}
|
||||
|
||||
@@ -533,30 +533,35 @@ function startAutoRefresh() {
|
||||
|
||||
// 启动定时器
|
||||
autoRefreshTick = 0;
|
||||
|
||||
// 进入实时模式:结束时间=现在,立刻走自动刷新全量(视窗由 autoRefreshChart 内 freeze 冻结)
|
||||
updateEndTimeToNow();
|
||||
autoRefreshChart({
|
||||
mode: 'full',
|
||||
incremental: false,
|
||||
forceFullRebuild: true,
|
||||
reason: 'auto-refresh-start'
|
||||
});
|
||||
|
||||
autoRefreshTimer = setInterval(function() {
|
||||
// 更新结束时间显示(仅 UI)
|
||||
// 自动刷新专用:结束时间推进到现在
|
||||
updateEndTimeToNow();
|
||||
|
||||
autoRefreshTick += 1;
|
||||
const now = Date.now();
|
||||
const lastFull = window._lastFullAnalyzeAt || 0;
|
||||
const needFullAnalyze = !lastFull || (now - lastFull >= AUTO_FULL_ANALYZE_MS);
|
||||
// 常态:/api/klines/recent 合并尾部 K;满 1 分钟:全量 /api/analyze 刷新缠论
|
||||
if (needFullAnalyze) {
|
||||
console.log('自动刷新 → 全量缠论 analyze(距上次', lastFull ? Math.round((now - lastFull) / 1000) + 's' : '首次', ')');
|
||||
updateChart({
|
||||
fromAutoRefresh: true,
|
||||
fullAnalyze: true,
|
||||
incremental: true
|
||||
});
|
||||
const tf = $('#timeframe').val() || '4h';
|
||||
const needFull = !lastFull || (now - lastFull >= AUTO_FULL_ANALYZE_MS) ||
|
||||
(typeof isLiveBaselineStale === 'function' && isLiveBaselineStale(tf));
|
||||
|
||||
if (needFull) {
|
||||
console.log('自动刷新 tick → live 全量');
|
||||
autoRefreshChart({ mode: 'full', incremental: false, forceFullRebuild: true });
|
||||
} else {
|
||||
updateChart({
|
||||
fromAutoRefresh: true,
|
||||
incremental: true
|
||||
});
|
||||
console.log('自动刷新 tick → recent 尾部');
|
||||
autoRefreshChart({ mode: 'recent' });
|
||||
}
|
||||
|
||||
// 更新下次刷新时间
|
||||
nextRefreshTime = new Date(Date.now() + intervalMs);
|
||||
updateNextRefreshTimeDisplay();
|
||||
}, intervalMs);
|
||||
@@ -798,21 +803,7 @@ function refreshChart(data, options) {
|
||||
// 自动刷新:增量更新,避免每次销毁/重建 Lightweight Charts
|
||||
if (preferIncremental && chartsReady) {
|
||||
try {
|
||||
// 数据到达后再冻结视窗(比请求发出时更准;避免用到过期 scroll)
|
||||
if (tvWidget.mainChart && typeof captureChartViewState === 'function') {
|
||||
try {
|
||||
window._preserveViewOnRefresh = captureChartViewState(tvWidget.mainChart);
|
||||
const prev = currentData && (
|
||||
($('#subSubPeriodKline').is(':checked') && currentData.sub_sub_kline_data) ||
|
||||
($('#elementPeriodKline').is(':checked') && currentData.element_kline_data) ||
|
||||
currentData.kline_data
|
||||
);
|
||||
window._preserveViewBarCount = Array.isArray(prev) ? prev.length : 0;
|
||||
} catch (e) {
|
||||
window._preserveViewOnRefresh = null;
|
||||
window._preserveViewBarCount = 0;
|
||||
}
|
||||
}
|
||||
// 视窗已在请求发出时 freezeChartViewportBeforeRequest 冻结,勿在此重拍(会弄错 barCount)
|
||||
updateTradingViewData({ tailOnly: !!options.skipTables });
|
||||
// recent-tail 刷新结构未变,跳过表格重绘以提速
|
||||
if (!options.skipTables) {
|
||||
@@ -829,7 +820,11 @@ function refreshChart(data, options) {
|
||||
|
||||
// 保存当前缩放(barSpacing)和滚动位置(scrollPosition)到 window
|
||||
// tvWidget 会在 initTradingView 内被重建,所以必须存到 window 上
|
||||
if (!window._pendingRestoreView && tvWidget && tvWidget.mainChart) {
|
||||
// 全量重建:优先用请求前冻结的视窗(分析按钮在请求发出时已 capture)
|
||||
if (window._preserveViewOnRefresh) {
|
||||
window._pendingRestoreView = window._preserveViewOnRefresh;
|
||||
console.log('📌 全量重建:使用请求前冻结视窗');
|
||||
} else if (!window._pendingRestoreView && tvWidget && tvWidget.mainChart) {
|
||||
try {
|
||||
window._pendingRestoreView = captureChartViewState(tvWidget.mainChart);
|
||||
console.log('📌 保存图表视图:', JSON.stringify(window._pendingRestoreView));
|
||||
|
||||
Reference in New Issue
Block a user