Resolução de problemas

Verificações e correções copiáveis para falhas comuns na integração de gráficos.

Estes modos de falha são gerados a partir do mesmo conhecimento versionado usado por diagnose_chart. Comece pelo sintoma que mais se aproxima da aplicação, aplique a verificação de diagnóstico e mantenha apenas as correções que preservem o ciclo de vida documentado e o contrato de propriedade de typed-array.

Se nenhuma entrada corresponder, inspecione o símbolo relevante na referência de API aninhada ou peça ao servidor MCP local para executar um diagnóstico focado. Reduza a reprodução para renderMode: "main" e animated: false apenas como passo de diagnóstico, não como configuração de produção automática.

Índice de sintomas

O canvas está em branco ou tem tamanho zero

Aplica-se a: line, stock.

Sintoma. O gráfico inicializa sem erro visível, mas nenhum gráfico aparece ou o canvas mede zero pixels de altura.

Causa provável. O host do canvas não tinha uma altura concreta quando o renderizador inicializou.

Verificar

  • Inspecione o host e o canvas com getBoundingClientRect(); tanto a largura como a altura devem ser maiores que zero.
  • Verifique se um ancestral com flex ou grid colapsou a linha do gráfico.

Corrigir

  • Dê ao host uma altura concreta ou min-height antes de initialize() ser executado.
  • Faça com que o canvas preencha esse host em vez de confiar nas dimensões intrínsecas do canvas.

Dê ao gráfico um host mensurável

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

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

Os dados são instalados antes do gráfico estar pronto

Aplica-se a: line, stock.

Sintoma. O primeiro conjunto de dados não aparece, enquanto uma atualização posterior ou remount funciona.

Causa provável. Uma integração vanilla chamou um método de dados antes de initialize() completar, ou inicializou o mesmo canvas mais do que uma vez.

Verificar

  • Verifique que initialize() é aguardado exactamente uma vez por cada instância de gráfico.
  • Se um framework for o proprietário da vista, prefira o adaptador oficial para que a ordenação de montagem e desmontagem seja tratada por si.

Corrigir

  • Aguarde initialize() antes de chamar setData() ou setMultiSeriesData().
  • Destrua o gráfico antigo antes de reutilizar o seu canvas para outra instância.

Use o ciclo de vida vanilla por ordem

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();
}

A renderização via worker recua para o thread principal

Aplica-se a: line, stock.

Sintoma. Um gráfico funciona mas resolve-se para renderização no thread principal, ou a construção do worker falha apenas em produção.

Causa provável. O worker, OffscreenCanvas, transferControlToOffscreen, ou a Content Security Policy implantada impediram o arranque do renderer no worker.

Verificar

  • Use setStatsCallback() temporariamente e inspecione as estatísticas.renderMode.
  • Verifique a consola do navegador e a diretiva worker-src da Content Security Policy.

Corrigir

  • Mantenha renderMode definido como auto salvo se for necessário forçar um modo para diagnóstico.
  • Permita a URL do worker gerada pela aplicação em worker-src e mantenha o fallback para thread principal operacional.

Confirmar o renderer resolvido

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

Arrays tipados ficam desacoplados após setData

Aplica-se a: line, stock.

Sintoma. Um array tipado de origem tem byteLength 0 ou deixa de ser legível depois de os dados terem sido passados para um gráfico suportado por worker.

Causa provável. Dados em massa do gráfico são transferidos para o renderer em modo worker; ArrayBuffers transferidos saem do chamador.

Verificar

  • Inspecione byteLength imediatamente antes e depois da chamada de dados em massa.
  • Confirme se outro subsistema realmente precisa manter uma cópia legível.

Corrigir

  • Trate a chamada de dados em massa como uma transferência de propriedade.
  • Clone apenas as colunas que outro subsistema deve reter; evite duplicar conjuntos de dados com milhões de valores por defeito.

Reter uma cópia intencional pela aplicação

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

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

// retainedX and retainedY remain readable.

O eixo temporal está comprimido, vazio ou muito no futuro

Aplica-se a: line, stock.

Sintoma. Os ticks mostram datas implausíveis, o intervalo visível colapsa, ou o pan comporta-se de forma imprevisível.

Causa provável. A coluna X usou segundos ou strings em vez de milissegundos epoch finitos ordenados, ou as observações não estavam ordenadas.

Verificar

  • Inspecione os timestamps primeiro e último e converta-os com new Date(value).
  • Verifique que cada timestamp é finito e não decrescente.

Corrigir

  • Converta segundos epoch para milissegundos antes de criar o Float64Array.
  • Analise datas ISO uma vez durante a ingestão e ordene observações completas antes de as dividir em colunas.

Normalizar timestamps durante a ingestão

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

As colunas têm comprimentos diferentes ou valores numéricos inválidos

Aplica-se a: line, stock.

Sintoma. O gráfico rejeita um conjunto de dados, trunca inesperadamente, ou renderiza valores com timestamps incorretos.

Causa provável. Dados colunar foram criados a partir de filtros ou mappings separados, pelo que as colunas de tempo e valor deixaram de descrever as mesmas observações.

Verificar

  • Compare o comprimento de cada coluna antes de chamar o gráfico.
  • Valide a conversão numérica antes de transferir buffers.

Correção

  • Filtrar e ordenar as observações de origem completas primeiro e depois dividi‑las em colunas tipadas alinhadas.
  • Usar NaN explícito apenas para uma lacuna de série de linhas documentada; valores OHLCV devem permanecer candles válidos.

Rejeitar colunas desalinhadas antes da transferência

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 ou hidratação acede a APIs de gráficos disponíveis apenas no navegador

Aplica‑se a: line, stock.

Sintoma. Uma renderização no servidor lança porque window, document, Worker ou HTMLCanvasElement não estão disponíveis, ou a hidratação produz uma árvore diferente.

Causa provável. O gráfico interativo do navegador foi construído durante a avaliação no servidor em vez de após o cliente montar.

Verificar

  • Encontrar a construção do gráfico no âmbito do módulo ou dentro de um componente de servidor.
  • Separar a geração de imagem no servidor através de @sixtyfold/ssr da interação no navegador.

Correção

  • Usar o adaptador oficial do framework dentro de um componente gerido pelo cliente.
  • Renderizar um anfitrião estável durante SSR e inicializar o gráfico interativo apenas após a montagem.

Manter um gráfico Next.js atrás da fronteira do cliente

"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" }} />;
}

Workers ou memória permanecem após navegação

Aplica‑se a: line, stock.

Sintoma. A memória cresce após abrir repetidamente a vista, ou gráficos antigos continuam a renderizar após a navegação.

Causa provável. Um gráfico vanilla ou um worker de dados temporário não foi destruído quando a vista proprietária foi removida.

Verificar

  • Registar um instantâneo do heap do navegador antes e depois de vários ciclos mount/unmount.
  • Procurar instâncias de gráfico retidas, workers, observers ou arrays de origem decodificados.

Correção

  • Chamar destroy() exactamente uma vez para cada instância de gráfico vanilla.
  • Terminar workers temporários fetch/decode após a sua transferência final; os adaptadores oficiais do framework destroem automaticamente workers de gráfico.

Liberar workers de gráfico e de ingestão

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

A interação está lenta ou perde frames

Aplica‑se a: line, stock.

Sintoma. Zoom, pan, decodificação inicial ou atualizações por streaming bloqueiam visivelmente ou falham em atingir a taxa de frames alvo.

Causa provável. O thread principal está a decodificar ou remodelar demasiados dados, o gráfico recaiu para o renderer de reserva, ou a carga de trabalho visível inclui mais passes de desenho do que o dispositivo alvo consegue suportar.

Verificar

  • Inspecionar stats.renderMode, frameTime, trabalho visível e prontidão de LOD durante a sequência real de interação.
  • Perfilizar fetch, decode, conversão, transferência e rendering separadamente.

Correção

  • Usar renderMode auto, preparar arrays tipados colunares num worker de dados dedicado, transferir uma vez e terminar esse worker de ingestão.
  • Benchmarking de navegadores representativos, DPRs, contagens de séries e gestos; 60 FPS é um objetivo de desenho e não uma garantia independente do dispositivo.

Medir o caminho de rendering activo

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

O detalhe da linha muda abruptamente após o zoom assentar

Aplica‑se a: line.

Sintoma. O viewport anima suavemente mas a textura da linha muda novamente no fim do gesto.

Provável causa. Densidade de apresentação ou rebase de grelha demasiado agressivo para a carga de trabalho, ou o código da aplicação substitui dados ou aparência após a conclusão da animação.

Verificar

  • Reproduzir a mesma sequência de viewport enquanto observa a densidade de apresentação, o delta da grelha e as visitas a queries.
  • Verifique se existe no lado da aplicação setData(), setAppearance() ou restauro do viewport após a animação estabilizar.

Corrigir

  • Comece com modo adaptativo, densidade 0.75, rebaseRatio 1.25 e quantizationStep 0.25.
  • Use setLODOptions() para ajustar um coeficiente de cada vez; use densidade 0.25 para preenchimentos de intervalo dispendiosos ou trabalho multi-série excepcionalmente grande.

Ajustar apresentação sem recriar o gráfico

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

Uma linha liga através de observações em falta

Aplica-se a: line.

Sintoma. Uma linha cruza visualmente um intervalo onde não existe nenhuma medição.

Provável causa. Observações em falta foram removidas ou substituídas por zero em vez de serem representadas como uma lacuna NaN alinhada.

Verificar

  • Confirmar que a coluna X partilhada inclui o intervalo em falta quando é necessária uma quebra visível.
  • Inspecione se a ingestão converteu valores null para zero.

Corrigir

  • Mantenha posições X alinhadas e codifique valores de linha em falta como Number.NaN.
  • Não sintetize candles OHLCV nem ligue encerramentos de mercado; o comportamento em escala de tempo de ações é conduzido por candles observados.

Representar uma lacuna real numa série temporal

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