故障排查

可复制的检查与修复,用于常见图表集成失败情况。

这些故障模式源自与 diagnose_chart 使用相同的版本化知识。请从最匹配应用程序的症状开始,执行诊断检查,并仅保留那些保留已记录生命周期和类型数组所有权约定的修复。

如果没有条目匹配,请检查嵌套 API 参考中的相关符号,或请求本地 MCP 服务器运行针对性诊断。仅将复现缩减为 renderMode: "main"animated: false 作为诊断步骤,而非自动的生产配置。

症状索引

画布为空白或尺寸为零

适用于:linestock

症状:图表初始化时未显示错误,但未出现绘图或画布高度测量为零。

可能原因:渲染器初始化时画布宿主没有具体高度。

检查

  • 使用 getBoundingClientRect() 检查宿主和画布;宽度和高度都必须大于零。
  • 检查是否有 flex 或 grid 祖先折叠了图表行。

修复

  • 在 initialize() 运行前为宿主设置具体的 height 或 min-height。
  • 让画布填充该宿主,而不要依赖画布的内在尺寸。

为图表提供可测量的宿主

.chart-host {
  position: relative;
  width: 100%;
  height: clamp(20rem, 55vh, 34rem);
}

.chart-host > canvas {
  display: block;
  width: 100%;
  height: 100%;
}

数据在图表就绪前被安装

适用于:linestock

症状:第一个数据集未出现,而后续更新或重新挂载有效。

可能原因:在 initialize() 完成前,原生集成调用了数据方法,或对同一画布初始化了多次。

检查

  • 确认对每个图表实例严格等待 initialize() 一次。
  • 如果框架管理视图,优先使用官方适配器,以便为你处理挂载与卸载顺序。

修复

  • 在调用 setData() 或 setMultiSeriesData() 之前等待 initialize()。
  • 在重用画布给另一个实例之前销毁旧图表。

按顺序使用原生生命周期

const chart = new LineChart(canvas, { renderMode: "auto" });
await chart.initialize();
chart.setData(data);

// Call this when the view is permanently removed.
export function disposeChart() {
  chart.destroy();
}

Worker 渲染回退到主线程

适用范围:linestock

症状。 图表可以工作,但解析为主线程渲染,或仅在生产环境中 worker 构造失败。

可能原因。 Worker、OffscreenCanvas、transferControlToOffscreen,或已部署的内容安全策略阻止了 worker 渲染器启动。

检查

  • 临时使用 setStatsCallback() 并检查统计信息。renderMode。
  • 检查浏览器控制台和 worker-src 内容安全策略指令。

修复

  • 除非为诊断需要强制模式,否则保持 renderMode 设置为 auto。
  • 在 worker-src 中允许应用生成的 worker URL,并保持主线程回退可用。

确认解析出的渲染器

chart.setStatsCallback((stats) => {
  console.info("Sixtyfold renderer:", stats.renderMode);
  chart.setStatsCallback(null);
});

在将数据传递给 Worker 支持的图表后,类型化数组在 setData 后变为 detached

适用范围:linestock

症状。 源类型化数组的 byteLength 为 0,或在数据传给 worker 支持的图表后无法再读取。

可能原因。 在 worker 模式下,批量图表数据被转移到渲染器;被转移的 ArrayBuffers 会离开调用方。

检查

  • 在批量数据调用前后立即检查 byteLength。
  • 确认是否有其他子系统确实需要保留可读副本。

修复

  • 将批量数据调用视为所有权转移。
  • 仅克隆其他子系统必须保留的列;默认避免复制数百万值的数据集。

保留一个有意的应用副本

const retainedX = sourceX.slice();
const retainedY = sourceY.slice();

chart.setData({
  x: sourceX,
  y: sourceY,
  length: sourceX.length,
});

// retainedX and retainedY remain readable.

时间轴被压缩、为空或在远未来

适用范围:linestock

症状。 刻度显示不合理的日期、可见范围塌缩或平移行为不可预测。

可能原因。 用作 X 列的值使用了秒或字符串而不是有序的有限纪元毫秒,或观测值未排序。

检查

  • 检查首尾时间戳并用 new Date(value) 进行转换。
  • 验证每个时间戳为有限且非递减。

修复

  • 在创建 Float64Array 之前将纪元秒转换为毫秒。
  • 在摄取时解析 ISO 日期一次,并在将完整观测拆分为列之前对其排序。

在摄取时规范化时间戳

const timestamp = Float64Array.from(rows, (row) => {
  const value = Date.parse(row.recordedAt);
  if (!Number.isFinite(value)) throw new TypeError("Invalid recordedAt value");
  return value;
});

列长度不同或包含无效数值

适用范围:linestock

症状。 图表拒绝数据集、意外截断或在错误时间戳下渲染值。

可能原因。 列式数据由独立的过滤或映射创建,因此时间列和值列不再描述相同的观测。

检查

  • 在调用图表之前比较每列的长度。
  • 在传输缓冲区之前验证数值转换。

修复

  • 先对完整源观测值进行过滤和排序,然后将它们拆分为对齐的类型化列。
  • 仅在文档化的折线序列间隙使用显式 NaN;OHLCV 值必须保持为有效的蜡烛数据。

在传输前拒绝未对齐的列

const lengths = [timestamp.length, open.length, high.length, low.length, close.length, volume.length];
if (!lengths.every((length) => length === lengths[0])) {
  throw new RangeError(`OHLCV columns are misaligned: ${lengths.join(", ")}`);
}

SSR 或水合访问仅浏览器可用的图表 API

适用于:linestock

症状。 服务器渲染抛出错误,原因是 window、document、Worker 或 HTMLCanvasElement 不可用,或水合产生不同的 DOM 树。

可能原因。 交互式浏览器图表在服务器评估期间构造,而不是在客户端挂载后构造。

检查

  • 查找在模块范围或服务器组件内部的图表构造。
  • 通过 @sixtyfold/ssr 将服务器图像生成与浏览器交互分离。

修复

  • 在客户端拥有的组件内使用官方框架适配器。
  • 在 SSR 期间渲染稳定宿主,仅在挂载后初始化交互式图表。

将 Next.js 图表保留在客户端边界之后

"use client";

import type { MultiSeriesData, TimeSeriesData } from "@sixtyfold/core";
import { SixtyfoldLineChart } from "@sixtyfold/react/line";

type LineData = TimeSeriesData | MultiSeriesData;

export function ChartPanel({ data }: { data: LineData }) {
  return <SixtyfoldLineChart data={data} options={{ renderMode: "auto" }} />;
}

Worker 或内存在导航后仍然存在

适用于:linestock

症状。 在反复打开视图后内存增长,或导航后旧图表继续渲染。

可能原因。 原生图表或临时数据 Worker 在其所属视图被移除时未被销毁。

检查

  • 在若干次 mount/unmount 循环前后记录浏览器堆快照。
  • 查找被保留的图表实例、Worker、观察者或已解码的源数组。

修复

  • 对每个原生图表实例恰好调用一次 destroy()。
  • 在最终传输后终止临时 fetch/decode Worker;官方框架适配器会自动销毁图表 Worker。

释放图表和摄取(ingestion)Worker

return () => {
  dataWorker?.terminate();
  chart.destroy();
};

交互变慢或丢帧

适用于:linestock

症状。 缩放、平移、初始解码或流更新明显阻塞或未达到目标帧率。

可能原因。 主线程正在解码或重塑过多数据,图表降级到回退渲染器,或可见工作负载包含目标设备无法维持的更多绘制传递。

检查

  • 检查统计信息:renderMode、frameTime、可见工作量和交互序列期间的 LOD 就绪情况。
  • 分别分析抓取、解码、转换、传输和渲染的性能剖面。

修复

  • 使用 renderMode 自动模式,在专用数据 Worker 中准备列式类型化数组,进行一次传输,然后终止该摄取 Worker。
  • 对具代表性的浏览器、DPR、序列数量和手势进行基准测试;60 FPS 是设计目标,而非与设备无关的保证。

测量活动渲染路径

chart.setStatsCallback((stats) => {
  console.table({
    mode: stats.renderMode,
    fps: stats.fps,
    frameTime: stats.frameTime,
    ready: stats.lodReady,
  });
}, { intervalMs: 250 });

缩放稳定后线条细节突变

适用于:line

症状。 视口平滑动画,但手势结束时线条纹理再次发生变化。

可能原因。 呈现密度或网格重基线对工作负载过于激进,或应用代码在动画结束后替换了数据或外观。

检查

  • 在观察呈现密度、网格增量和查询访问的同时重放相同的视口序列。
  • 检查应用端是否在动画稳定后调用了 setData()、setAppearance() 或恢复视口。

修复

  • 从自适应模式开始,密度设为 0.75,rebaseRatio 设为 1.25,quantizationStep 设为 0.25。
  • 使用 setLODOptions() 一次调整一个系数;对于开销大的范围填充或异常大的多序列工作,使用密度 0.25。

在不重建图表的情况下调整呈现

chart.setLODOptions({
  mode: "adaptive",
  density: 0.75,
  rebaseRatio: 1.25,
  quantizationStep: 0.25,
});

折线跨越缺失观测点相连

适用于:line

症状。 折线在没有测量值的区间上视觉上穿过。

可能原因。 缺失观测被删除或被替换为零,而不是表示为对齐的 NaN 间隙。

检查

  • 当需要可见断裂时,确认共享的 X 列包含该缺失区间。
  • 检查摄取过程是否将 null 值转换为零。

修复

  • 保持对齐的 X 位置,并将缺失的折线值编码为 Number.NaN。
  • 不要合成 OHLCV 蜡烛或连接市场休市;股票时间尺度行为由观测到的蜡烛驱动。

表示真实的折线序列间隙

const data = {
  x: new Float64Array([0, 1, 2, 3]),
  y: new Float64Array([12, Number.NaN, Number.NaN, 18]),
  length: 4,
};