Solución de problemas
Comprobaciones y correcciones copiables para fallos comunes en la integración de gráficos.
Estos modos de fallo se generan a partir del mismo conocimiento versionado usado por diagnose_chart. Comience por el síntoma que más se parezca a la aplicación, aplique la comprobación diagnóstica y conserve solo las correcciones que preserven el ciclo de vida documentado y el contrato de propiedad de typed-array.
Si ninguna entrada coincide, inspeccione el símbolo relevante en la referencia de API anidada o pida al servidor MCP local que ejecute un diagnóstico enfocado. Reduzca la reproducción a renderMode: "main" y animated: false solo como paso diagnóstico, no como configuración de producción automática.
Índice de síntomas
El lienzo está en blanco o tiene tamaño cero
Se aplica a: line, stock.
Síntoma. El gráfico se inicializa sin error visible, pero no aparece ninguna trama o el lienzo mide cero píxeles de alto.
Causa probable. El contenedor del lienzo no tenía una altura concreta cuando se inicializó el renderizador.
Comprobar
- Inspeccione el host y el lienzo con getBoundingClientRect(); tanto el ancho como la altura deben ser mayores que cero.
- Compruebe si un ancestro en flex o grid colapsó la fila del gráfico.
Corregir
- Dé al host una altura concreta o min-height antes de que initialize() se ejecute.
- Haga que el lienzo llene ese host en lugar de confiar en las dimensiones intrínsecas del lienzo.
Dé al gráfico un host medible
.chart-host {
position: relative;
width: 100%;
height: clamp(20rem, 55vh, 34rem);
}
.chart-host > canvas {
display: block;
width: 100%;
height: 100%;
}Los datos se instalan antes de que el gráfico esté listo
Se aplica a: line, stock.
Síntoma. El primer conjunto de datos no aparece, mientras que una actualización posterior o un remount funcionan.
Causa probable. Una integración vanilla llamó a un método de datos antes de que initialize() terminara, o inicializó el mismo lienzo más de una vez.
Comprobar
- Verifique que initialize() se espere exactamente una vez por cada instancia de gráfico.
- Si un framework gestiona la vista, prefiera el adaptador oficial para que el orden de montaje y desmontaje se gestione por usted.
Corregir
- Espere a initialize() antes de llamar a setData() o setMultiSeriesData().
- Destruya el gráfico antiguo antes de reutilizar su lienzo para otra instancia.
Use el ciclo de vida vanilla en orden
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();
}El renderizado en worker revierte al main thread
Aplica a: line, stock.
Síntoma. Un gráfico funciona pero se resuelve a renderizado en el main thread, o la construcción del worker falla solo en producción.
Causa probable. El worker, OffscreenCanvas, transferControlToOffscreen, o la Content Security Policy desplegada impidieron que el renderer en worker arrancara.
Comprobar
- Usa setStatsCallback() temporalmente e inspecciona las estadísticas.renderMode.
- Revisa la consola del navegador y la directiva worker-src de la Content Security Policy.
Solucionar
- Mantén renderMode en auto salvo que forzar un modo sea necesario para el diagnóstico.
- Permite la URL del worker generada por la aplicación en worker-src y conserva el fallback al main thread operativo.
Confirmar el renderer resuelto
chart.setStatsCallback((stats) => {
console.info("Sixtyfold renderer:", stats.renderMode);
chart.setStatsCallback(null);
});Los typed arrays quedan detached después de setData
Aplica a: line, stock.
Síntoma. Un typed array fuente tiene byteLength 0 o ya no puede leerse después de que los datos se pasaron a un gráfico respaldado por worker.
Causa probable. Los datos masivos del gráfico se transfieren al renderer en modo worker; los ArrayBuffers transferidos abandonan el llamador.
Comprobar
- Inspecciona byteLength inmediatamente antes y después de la llamada de datos masivos.
- Confirma si otro subsistema necesita genuinamente conservar una copia legible.
Solucionar
- Trata la llamada de datos masivos como una transferencia de propiedad.
- Clona solo las columnas que otro subsistema deba retener; evita duplicar conjuntos de datos de millones de valores por defecto.
Conservar una copia intencionada en la aplicación
const retainedX = sourceX.slice();
const retainedY = sourceY.slice();
chart.setData({
x: sourceX,
y: sourceY,
length: sourceX.length,
});
// retainedX and retainedY remain readable.El eje temporal está comprimido, vacío o muy en el futuro
Aplica a: line, stock.
Síntoma. Las marcas muestran fechas implausibles, el rango visible colapsa o el paneo se comporta de forma impredecible.
Causa probable. La columna X usó segundos o cadenas en lugar de milisegundos epoch finitos ordenados, o las observaciones no estaban ordenadas.
Comprobar
- Inspecciona las marcas temporales primera y última y conviértelas con new Date(value).
- Verifica que cada marca temporal sea finita y no decreciente.
Solucionar
- Convierte segundos epoch a milisegundos antes de crear el Float64Array.
- Parsea fechas ISO una vez durante la ingestión y ordena observaciones completas antes de dividirlas en columnas.
Normaliza las marcas temporales durante la ingestión
const timestamp = Float64Array.from(rows, (row) => {
const value = Date.parse(row.recordedAt);
if (!Number.isFinite(value)) throw new TypeError("Invalid recordedAt value");
return value;
});Las columnas tienen longitudes diferentes o valores numéricos inválidos
Aplica a: line, stock.
Síntoma. El gráfico rechaza un conjunto de datos, trunca inesperadamente o renderiza valores bajo marcas temporales equivocadas.
Causa probable. Los datos columnars se crearon a partir de filtros o mapeos separados, por lo que las columnas de tiempo y valor ya no describen las mismas observaciones.
Comprobar
- Compara la longitud de cada columna antes de llamar al gráfico.
- Valida la conversión numérica antes de transferir los buffers.
Corregir
- Filtrar y ordenar primero las observaciones completas de la fuente, luego dividirlas en columnas tipadas alineadas.
- Usar NaN explícito solo para una brecha de serie de líneas documentada; los valores OHLCV deben seguir siendo velas válidas.
Rechazar columnas desalineadas antes de la transferencia
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 o hidratación accede a APIs de gráficos exclusivas del navegador
Se aplica a: line, stock.
Síntoma. Un render en servidor falla porque window, document, Worker o HTMLCanvasElement no está disponible, o la hidratación produce un árbol diferente.
Causa probable. El gráfico interactivo del navegador se construyó durante la evaluación en servidor en lugar de después del montaje en cliente.
Comprobar
- Buscar la construcción del gráfico en el ámbito del módulo o dentro de un componente de servidor.
- Separar la generación de imagen en servidor mediante @sixtyfold/ssr de la interacción en navegador.
Corregir
- Usar el adaptador oficial del framework dentro de un componente propiedad del cliente.
- Renderizar un host estable durante SSR e inicializar el gráfico interactivo solo tras el montaje.
Mantener un gráfico Next.js detrás del límite del 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" }} />;
}Permanecen workers o memoria tras la navegación
Se aplica a: line, stock.
Síntoma. La memoria crece tras abrir repetidamente la vista, o los gráficos antiguos siguen renderizando tras la navegación.
Causa probable. Un gráfico vanilla o un worker temporal de datos no se destruyó cuando se eliminó la vista propietaria.
Comprobar
- Registrar una instantánea del heap del navegador antes y después de varios ciclos mount/unmount.
- Buscar instancias de gráficos retenidas, workers, observadores o arrays de fuente decodificados.
Corregir
- Llamar a destroy() exactamente una vez por cada instancia de gráfico vanilla.
- Terminar workers temporales fetch/decode tras su última transferencia; los adaptadores oficiales del framework destruyen automáticamente los workers del gráfico.
Liberar workers del gráfico y de ingestión
return () => {
dataWorker?.terminate();
chart.destroy();
};La interacción es lenta o pierde frames
Se aplica a: line, stock.
Síntoma. El zoom, paneo, decodificación inicial o las actualizaciones por streaming bloquean visiblemente o no alcanzan la tasa de frames objetivo.
Causa probable. El main thread está decodificando o remodelando demasiados datos, el gráfico resolvió al renderer de reserva, o la carga de trabajo visible incluye más pasadas de dibujo de las que el dispositivo objetivo puede sostener.
Comprobar
- Inspeccionar estadísticas.renderMode, frameTime, trabajo visible y disponibilidad de LOD durante la secuencia de interacción real.
- Perfilar fetch, decodificación, conversión, transferencia y renderizado por separado.
Corregir
- Usar renderMode auto, preparar arrays tipados columnarios en un worker de datos dedicado, transferir una vez y terminar ese worker de ingestión.
- Benchmark de navegadores representativos, DPRs, recuentos de series y gestos; 60 FPS es un objetivo de diseño y no una garantía independiente del dispositivo.
Medir la vía de renderizado activa
chart.setStatsCallback((stats) => {
console.table({
mode: stats.renderMode,
fps: stats.fps,
frameTime: stats.frameTime,
ready: stats.lodReady,
});
}, { intervalMs: 250 });El detalle de la línea cambia bruscamente tras estabilizar el zoom
Se aplica a: line.
Síntoma. La vista se anima suavemente pero la textura de la línea cambia de nuevo al final del gesto.
Causa probable. La densidad de presentación o el rebasing de la cuadrícula es demasiado agresivo para la carga de trabajo, o el código de la aplicación sustituye los datos o la apariencia tras finalizar la animación.
Comprobar
- Reproduce la misma secuencia de vista mientras observas la densidad de presentación, el delta de la cuadrícula y las visitas a las consultas.
- Comprueba si la aplicación llama a setData(), setAppearance() o restaura la vista después de que la animación se estabilice.
Solucionar
- Empieza con modo adaptativo, densidad 0.75, rebaseRatio 1.25 y quantizationStep 0.25.
- Usa setLODOptions() para ajustar un coeficiente a la vez; usa densidad 0.25 para rellenos de rango costosos o trabajo multiserie excepcionalmente grande.
Ajustar la presentación sin recrear el gráfico
chart.setLODOptions({
mode: "adaptive",
density: 0.75,
rebaseRatio: 1.25,
quantizationStep: 0.25,
});Una línea conecta a través de observaciones ausentes
Se aplica a: line.
Síntoma. Una línea cruza visualmente un intervalo donde no existe ninguna medición.
Causa probable. Las observaciones faltantes se eliminaron o se reemplazaron por cero en lugar de representarse como una brecha NaN alineada.
Comprobar
- Confirma que la columna X compartida incluye el intervalo faltante cuando se requiere una ruptura visible.
- Inspecciona si la ingesta convirtió valores null en cero.
Solucionar
- Mantén las posiciones X alineadas y codifica los valores de línea faltantes como Number.NaN.
- No sintetices velas OHLCV ni conectes cierres de mercado; el comportamiento en escala temporal de acciones se rige por las velas observadas.
Representar una brecha real en una serie de líneas
const data = {
x: new Float64Array([0, 1, 2, 3]),
y: new Float64Array([12, Number.NaN, Number.NaN, 18]),
length: 4,
};