Dépannage

Vérifications et corrections copiables pour les échecs d’intégration de graphique courants.

Ces modes de défaillance sont générés à partir des mêmes connaissances versionnées utilisées par diagnose_chart. Commencez par le symptôme qui correspond le mieux à l’application, appliquez la vérification diagnostique et conservez uniquement les corrections qui préservent le cycle de vie documenté et le contrat de propriété des typed-array.

Si aucune entrée ne correspond, inspectez le symbole pertinent dans la nested API reference ou demandez au serveur local MCP server d’exécuter un diagnostic ciblé. Réduisez la reproduction à renderMode: "main" et animated: false uniquement comme étape diagnostique, pas comme configuration de production automatique.

Index des symptômes

Le canvas est vide ou a une taille nulle

S’applique à : line, stock.

Symptôme. Le chart s’initialise sans erreur visible, mais aucun tracé n’apparaît ou le canvas mesure zéro pixel en hauteur.

Cause probable. L’hôte du canvas n’avait pas de hauteur concrète lorsque le renderer s’est initialisé.

Vérifier

  • Inspectez l’hôte et le canvas avec getBoundingClientRect(); largeur et hauteur doivent être supérieures à zéro.
  • Vérifiez si un ancêtre en flex ou grid a fait s’effondrer la ligne du chart.

Corriger

  • Donnez à l’hôte une hauteur concrète ou un min-height avant l’exécution d’initialize().
  • Laissez le canvas remplir cet hôte plutôt que de compter sur les dimensions intrinsèques du canvas.

Donner au chart un hôte mesurable

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

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

Les données sont installées avant que le chart soit prêt

S’applique à : line, stock.

Symptôme. Le premier jeu de données n’apparaît pas, tandis qu’une mise à jour ultérieure ou un remount fonctionne.

Cause probable. Une intégration vanilla a appelé une méthode de données avant la fin d’initialize(), ou a initialisé plusieurs fois le même canvas.

Vérifier

  • Vérifiez qu’initialize() est awaited exactement une fois pour chaque instance de chart.
  • Si un framework possède la vue, préférez l’adaptateur officiel afin que l’ordre de montage et démontage soit géré pour vous.

Corriger

  • Awaitez initialize() avant d’appeler setData() ou setMultiSeriesData().
  • Détruisez l’ancien chart avant de réutiliser son canvas pour une autre instance.

Utilisez le cycle de vie vanilla dans l’ordre

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

Le rendu par worker revient au thread principal

S’applique à : line, stock.

Symptôme. Un graphique fonctionne mais tombe sur un rendu dans le thread principal, ou la construction du worker échoue uniquement en production.

Cause probable. Le worker, OffscreenCanvas, transferControlToOffscreen, ou la Content Security Policy déployée ont empêché le renderer du worker de démarrer.

Vérifier

  • Utilisez setStatsCallback() temporairement et inspectez les statistiques.renderMode.
  • Vérifiez la console du navigateur et la directive worker-src de la Content Security Policy.

Corriger

  • Gardez renderMode réglé sur auto sauf si forcer un mode est nécessaire pour le diagnostic.
  • Autorisez l’URL du worker générée par l’application dans worker-src et conservez le fallback sur le thread principal opérationnel.

Confirmer le renderer résolu

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

Les typed arrays sont détachés après setData

S’applique à : line, stock.

Symptôme. Une typed array source montre byteLength 0 ou n’est plus lisible après que des données ont été transmises à un graphique utilisant un worker.

Cause probable. Les données massives du graphique sont transférées au renderer en mode worker ; les ArrayBuffers transférées quittent l’appelant.

Vérifier

  • Inspectez byteLength immédiatement avant et après l’appel des données massives.
  • Confirmez si un autre sous-système doit réellement conserver une copie lisible.

Corriger

  • Considérez l’appel de données massives comme un transfert de propriété.
  • Clonez uniquement les colonnes qu’un autre sous-système doit conserver ; évitez de dupliquer par défaut des ensembles de données de plusieurs millions de valeurs.

Conserver une copie intentionnelle au niveau de l’application

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

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

// retainedX and retainedY remain readable.

L’axe temporel est compressé, vide ou très éloigné dans le futur

S’applique à : line, stock.

Symptôme. Les graduations affichent des dates impossibles, la plage visible s’effondre ou le panoramique se comporte de façon imprévisible.

Cause probable. La colonne X utilisait des secondes ou des chaînes au lieu de millisecondes d’époque finies ordonnées, ou les observations n’étaient pas triées.

Vérifier

  • Inspectez les premiers et derniers timestamps et convertissez-les avec new Date(value).
  • Vérifiez que chaque timestamp est fini et non décroissant.

Corriger

  • Convertissez les secondes d’époque en millisecondes avant de créer le Float64Array.
  • Parsez les dates ISO une seule fois lors de l’ingestion et triez les observations complètes avant de les séparer en colonnes.

Normaliser les timestamps lors de l’ingestion

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

Les colonnes ont des longueurs différentes ou des valeurs numériques invalides

S’applique à : line, stock.

Symptôme. Le graphique rejette un jeu de données, tronque de manière inattendue ou affiche des valeurs sous de mauvais timestamps.

Cause probable. Les données colonnaires ont été créées à partir de filtres ou de mappings séparés, si bien que les colonnes temps et valeurs ne décrivent plus les mêmes observations.

Vérifier

  • Comparez la longueur de chaque colonne avant d’appeler le graphique.
  • Validez la conversion numérique avant de transférer les buffers.

Correction

  • Filtrer et trier d’abord l’ensemble complet des observations sources, puis scinder en colonnes typées alignées.
  • N’utiliser NaN explicite que pour un intervalle de séries chronologiques documenté ; les valeurs OHLCV doivent rester des chandeliers valides.

Rejeter les colonnes désalignées avant le transfert

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 hydratation accède à des APIs de chart navigateur uniquement

S’applique à : line, stock.

Symptôme. Un rendu côté serveur lève une erreur parce que window, document, Worker ou HTMLCanvasElement est indisponible, ou l’hydratation produit un arbre différent.

Cause probable. Le chart interactif du navigateur a été construit pendant l’évaluation serveur au lieu d’après le montage côté client.

Vérifier

  • Trouver la construction du chart au niveau du module ou à l’intérieur d’un composant serveur.
  • Séparer la génération d’image côté serveur via @sixtyfold/ssr de l’interaction navigateur.

Correction

  • Utiliser l’adaptateur officiel du framework à l’intérieur d’un composant appartenant au client.
  • Rendre un hôte stable pendant la SSR et n’initialiser le chart interactif qu’après le montage.

Garder un chart Next.js derrière la frontière client

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

Les workers ou la mémoire persistent après la navigation

S’applique à : line, stock.

Symptôme. La mémoire augmente après plusieurs ouvertures de la vue, ou d’anciens charts continuent de rendre après navigation.

Cause probable. Un chart vanilla ou un worker de données temporaire n’a pas été détruit lorsque la vue propriétaire a été supprimée.

Vérifier

  • Enregistrer un snapshot du tas du navigateur avant et après plusieurs cycles mount/unmount.
  • Rechercher des instances de chart retenues, des workers, des observers ou des tableaux sources décodés.

Correction

  • Appeler destroy() exactement une fois pour chaque instance de chart vanilla.
  • Terminer les workers temporaires fetch/decode après leur dernier transfert ; les adaptateurs officiels des frameworks détruisent automatiquement les workers de chart.

Libérer les workers de chart et d’ingestion

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

L’interaction est lente ou perd des images

S’applique à : line, stock.

Symptôme. Le zoom, le panoramique, le décodage initial ou les mises à jour en streaming bloquent visiblement ou manquent le débit cible d’images.

Cause probable. Le thread principal décode ou remodèle trop de données, le chart est tombé sur le renderer de secours, ou la charge visible inclut plus de passes de dessin que le dispositif cible ne peut soutenir.

Vérifier

  • Inspecter les stats.renderMode, frameTime, le travail visible et la disponibilité du LOD pendant la séquence d’interaction réelle.
  • Profiler fetch, decode, conversion, transfert et rendu séparément.

Correction

  • Utiliser renderMode auto, préparer des tableaux typés colonnaires dans un data worker dédié, transférer une fois, puis terminer ce worker d’ingestion.
  • Mesurer les navigateurs représentatifs, DPR, nombres de séries et gestes ; 60 FPS est un objectif de conception et non une garantie indépendante du dispositif.

Mesurer le chemin de rendu actif

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

Le niveau de détail de la ligne change brusquement après stabilisation du zoom

S’applique à : line.

Symptôme. Le viewport s’anime en douceur mais la texture de la ligne change à nouveau à la fin du geste.

Cause probable. La densité de présentation ou le rebasing de la grille est trop agressif pour la charge de travail, ou le code de l’application remplace les données ou l’apparence après la fin de l’animation.

Vérifier

  • Rejouez la même séquence de fenêtres d’affichage en observant la densité de présentation, le delta de grille et les visites de requêtes.
  • Vérifiez la présence d’appels côté application setData(), setAppearance() ou d’une restauration du viewport après stabilisation de l’animation.

Corriger

  • Commencez en mode adaptatif, densité 0.75, rebaseRatio 1.25 et quantizationStep 0.25.
  • Utilisez setLODOptions() pour ajuster un coefficient à la fois ; utilisez une densité de 0.25 pour des remplissages de plage coûteux ou des traitements multi-séries exceptionnellement volumineux.

Ajuster la présentation sans recréer le graphique

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

Une ligne traverse des observations manquantes

S’applique à : line.

Symptôme. Une ligne traverse visuellement un intervalle où aucune mesure n’existe.

Cause probable. Des observations manquantes ont été supprimées ou remplacées par zéro au lieu d’être représentées comme un gap NaN aligné.

Vérifier

  • Confirmez que la colonne X partagée inclut l’intervalle manquant lorsqu’une rupture visible est requise.
  • Inspectez si l’ingestion a converti des valeurs nulles en zéro.

Corriger

  • Conservez des positions X alignées et encodez les valeurs de ligne manquantes comme Number.NaN.
  • Ne synthétisez pas de chandeliers OHLCV ni ne reliez des fermetures de marché ; le comportement temporel des actions est dicté par les chandeliers observés.

Représentez un véritable gap de série temporelle

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