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 aproxime a la aplicación, aplique la comprobación diagnóstica y conserve únicamente las correcciones que preserven el ciclo de vida documentado y el contrato de propiedad de los typed arrays.
Si no coincide ninguna entrada, inspeccione el símbolo pertinente en la referencia de API anidada o pida al servidor MCP local que ejecute un diagnóstico dirigido. Reduzca la reproducción a renderMode: "main" y animated: false solo como paso de diagnóstico, no como configuración automática para producción.
Índice de síntomas
El canvas 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 traza o el canvas mide cero píxeles de alto.
Causa probable. El host del canvas no tenía una altura concreta cuando se inicializó el renderizador.
Compruebe
- Inspeccione el host y el canvas con getBoundingClientRect(); tanto el ancho como la altura deben ser mayores que cero.
- Compruebe si un ancestro con display:flex o display:grid colapsó la fila del gráfico.
Solución
- Asigne al contenedor una altura concreta o min-height antes de que se ejecute initialize().
- Haga que el lienzo rellene ese contenedor en lugar de confiar en las dimensiones intrínsecas del canvas.
Proporcione al gráfico un contenedor con dimensiones medibles
.chart-host {
position: relative;
width: 100%;
height: clamp(20rem, 55vh, 34rem);
}
.chart-host > canvas {
display: block;
width: 100%;
height: 100%;
}Los datos se establecen 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 volver a montar funciona.
Causa probable. Una integración genérica llamó a un método de datos antes de que initialize() finalizara, o inicializó el mismo canvas más de una vez.
Comprobar
- Verifique que initialize() se espere exactamente una vez por cada instancia de gráfico.
- Si un framework posee la vista, prefiera el adaptador oficial para que el orden de montaje y desmontaje se gestione por usted.
- La preparación del adaptador se dispara una vez tras la inicialización y el primer lote exitoso de props de datos, apariencia y viewport; no es una notificación de fotograma pintado.
Corregir
- Espere a initialize() antes de llamar a setData() o setMultiSeriesData().
- Destruya el gráfico antiguo antes de reutilizar su canvas para otra instancia.
- Para una instalación de la prop adapter rechazada, proporcione props corregidas para reintentar sin volver a montar. Los datos instalados correctamente no se transfieren de nuevo. Una falla terminal del renderer requiere volver a montar.
Use el ciclo de vida vanilla en el 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 recae al hilo principal
Se aplica a: line, stock.
Síntoma. Un gráfico funciona pero se realiza el render en el hilo principal, o la construcción del worker falla solo en producción.
Causa probable. El worker, OffscreenCanvas, transferControlToOffscreen o la Política de Seguridad de Contenidos desplegada impidieron que el renderer del worker se iniciara.
Comprobar
- Use setStatsCallback() temporalmente e inspeccione estadísticas.renderMode.
- Compruebe la consola del navegador y la directiva Content Security Policy ⟦SF_worker-src⟧.
Solución
- Mantenga renderMode en auto salvo que sea necesario forzar un modo para diagnósticos.
- Permita la URL del worker generada por la aplicación en ⟦SF_worker-src⟧ y mantenga operativo el fallback en el hilo principal.
Confirmar el renderer resuelto
chart.setStatsCallback((stats) => {
console.info("Sixtyfold renderer:", stats.renderMode);
chart.setStatsCallback(null);
});Los arrays tipados se desprenden después de setData
Se aplica a: line, stock.
Síntoma. Un array tipado de origen tiene byteLength 0 o ya no puede leerse después de que los datos se asignaron a un chart respaldado por worker.
Causa probable. Los datos masivos del gráfico se transfieren al renderizador en modo worker; las referencias transferidas ArrayBuffers dejan al llamante.
Comprobar
- Inspeccione byteLength inmediatamente antes y después de la llamada de datos masivos.
- Confirme si otro subsistema necesita realmente conservar una copia legible.
Corregir
- Trate la llamada de datos masivos como una transferencia de propiedad.
- Clone únicamente las columnas que otro subsistema deba conservar; evite duplicar por defecto conjuntos de datos con millones de valores.
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 situado muy en el futuro
Se aplica a: line, stock.
Síntoma. Las marcas de tiempo muestran fechas implausibles, el rango visible se colapsa o el desplazamiento (panning) se comporta de forma impredecible.
Causa probable. La columna X usó segundos o cadenas en lugar de milisegundos de época finitos y ordenables, o las observaciones no estaban ordenadas.
Comprobar
- Inspeccione las marcas de tiempo primera y última y conviértalas con new Date(value).
- Verifique que cada marca de tiempo sea finita. El streaming de cotizaciones requiere marcas de tiempo estrictamente crecientes dentro de cada lote y respecto a cada vela previamente encolada o añadida.
- Distinga el streaming de los datos estáticos StockChart.setData(): los datos estáticos pueden ser monótonamente ascendentes o descendentes y pueden contener marcas de tiempo iguales; el gráfico normaliza su orden.
Corregir
- Convertir segundos epoch a milisegundos antes de crear Float64Array.
- Analizar fechas ISO una vez durante la ingestión y ordenar observaciones completas antes de dividirlas en columnas.
- Desduplicar observaciones del flujo de acciones antes de anexarlas. Los métodos de anexado en streaming no actualizan una vela existente con la misma marca temporal.
Normalice las marcas de tiempo 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 no válidos
Se aplica a: line, stock.
Síntoma: el gráfico rechaza un conjunto de datos, se trunca inesperadamente o representa valores bajo marcas de tiempo incorrectas.
Causa probable: los datos columnarios se crearon a partir de filtros o mapeos independientes, por lo que las columnas de tiempo y de valor ya no describen las mismas observaciones.
Comprobar
- Comparar la longitud de cada columna antes de invocar el gráfico.
- Validar la conversión numérica antes de transferir los buffers.
- Stock addCandles requiere seis columnas de igual tamaño; addCandleBatches valida la lista completa de fragmentos antes de cualquier transferencia. Marcas de tiempo o longitudes de columna inválidas dejan los búferes del llamador adjuntos.
Corregir
- Filtre y ordene primero las observaciones de origen completas y después divídalas en columnas tipadas alineadas.
- Use NaN explícito sólo para una discontinuidad documentada en series de líneas; los valores OHLCV deben seguir siendo velas válidas.
Rechace 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 acceden a APIs de gráficos exclusivas del navegador
Aplica a: line, stock.
Síntoma: una renderización en servidor lanza un error porque window, document, Worker o HTMLCanvasElement no están disponibles, o la hidratación produce un árbol diferente.
Causa probable. El gráfico interactivo para navegador se construyó durante la evaluación en el servidor en lugar de después de que el cliente se montara.
Comprobar
- Localice la construcción del gráfico en el ámbito del módulo o dentro de un componente del servidor.
- Separe la generación de imágenes en el servidor mediante @sixtyfold/ssr de la interacción en el navegador.
Corrección
- Use el adaptador oficial del framework dentro de un componente propiedad del cliente.
- Renderice un host estable durante SSR e inicialice el gráfico interactivo solo después del montaje.
Mantenga 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" }} />;
}Quedan workers o memoria después de la navegación
Se aplica a: line, stock.
Síntoma. La memoria aumenta tras abrir repetidamente la vista, o los gráficos antiguos siguen renderizándose después de la navegación.
Causa probable. Un gráfico vanilla o un worker temporal de datos no se destruyó cuando se eliminó la vista que lo poseía.
Comprobar
- Haga un volcado de heap del navegador antes y después de varios ciclos de montaje/desmontaje.
- Busque instancias de gráfico retenidas, workers, observadores o matrices de origen decodificadas.
Corregir
- Llame a destroy() exactamente una vez por cada instancia de gráfico vanilla.
- Finalice los workers de fetch/decodificación temporales tras su última transferencia; los adaptadores oficiales del framework destruyen automáticamente los workers del gráfico.
Liberar workers de gráfico e ingestión
return () => {
dataWorker?.terminate();
chart.destroy();
};La interacción es lenta o se pierden fotogramas
Se aplica a: line, stock.
Síntoma. Al hacer zoom, desplazar, decodificar inicialmente o actualizar por streaming, se producen bloqueos visibles o no se alcanza la tasa de fotogramas objetivo.
Causa probable. El hilo principal está decodificando o remodelando demasiados datos, el gráfico resolvió al renderizador de reserva, o la carga de trabajo visible incluye más pasadas de dibujo de las que el dispositivo objetivo puede mantener.
Comprobar
- Inspeccione estadísticas.renderMode, frameTime, trabajo visible y preparación de LOD durante la secuencia de interacción real.
- Perfilar la obtención, decodificación, conversión, transferencia y renderizado por separado.
Corregir
- Use renderMode auto, prepare arrays tipados columnarios en un worker de datos dedicado, asigne los datos a la gráfica una sola vez mediante transferencia y termine ese worker de ingestión.
- Evalúe navegadores representativos, DPR, recuentos de series y gestos; 60 FPS es un objetivo de diseño y no una garantía independiente del dispositivo.
Mida la ruta 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 una vez que el zoom se estabiliza
Se aplica a: line.
Síntoma. La ventana gráfica (viewport) se anima con suavidad, pero la textura de la línea cambia nuevamente al final del gesto.
Causa probable. La densidad de presentación o el rebasedo de la cuadrícula (grid rebasing) son demasiado agresivos para la carga de trabajo, o el código de la aplicación reemplaza los datos o la apariencia tras completarse la animación.
Comprobar
- Reproduzca la misma secuencia de la ventana gráfica mientras observa la densidad de presentación, el delta de la cuadrícula y las visitas de consulta.
- Compruebe si la aplicación llama a setData(), setAppearance() o restaura el viewport después de que la animación termine.
Solución
- Empiece en modo adaptable, densidad 0.75, rebaseRatio 1.25 y quantizationStep 0.25.
- Use setLODOptions() para ajustar un coeficiente a la vez; utilice densidad 0.25 para rellenos de rango costosos o trabajos multiserie excepcionalmente grandes.
Ajuste 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 faltantes
Se aplica a: line.
Síntoma. Una línea cruza visualmente un intervalo donde no existe ninguna medición.
Causa probable. Observaciones faltantes fueron eliminadas o reemplazadas por cero en lugar de representarse como una brecha alineada NaN.
Comprobar
- Confirme que la columna X compartida incluye el intervalo faltante cuando se requiere una ruptura visible.
- Inspeccione si la ingesta convirtió valores null a cero.
Corregir
- Mantener alineadas las posiciones X y codificar valores de línea faltantes como Number.NaN.
- No sintetizar velas OHLCV ni unir cierres de mercado; el comportamiento temporal de acciones está determinado 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,
};