Animation

Les animations de VSeed prennent en charge les étapes d'apparition, de boucle, d'ambiance, de mise à jour et de sortie. Ce guide regroupe des exemples pour les graphiques à barres, lignes/aires, secteurs, radar et nuage de points afin de comparer les configurations selon les types de graphiques.

Animation des graphiques barres / colonnes

Tous les exemples reposent sur le thème grand écran (sombre) et proposent une liste déroulante pour changer de thème. Les thèmes sont chargés depuis : https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/theme.json

1. Animation d'apparition

L'animation d'apparition est le mouvement joué lorsque le graphique entre pour la première fois dans la vue. Pour les séries barres/colonnes, l'effet d'apparition prend actuellement en charge growth.

1.1 growth (croissance)

import { useEffect, useState } from 'react'
import { VSeedRender } from '@components'
import { registerTokenThemes } from '@visactor/vseed'

const THEME_URL = 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/theme.json'
const DATASET = [
  {
    10001: 'Sales',
    10002: 30,
    10003: 'Tn97A7q0XVDq',
    30001: '2023',
    HqlYadRdHJ5c: 'Labels',
    Tn97A7q0XVDq: 30,
    '7ti8XuX4kcY1': '2023',
  },
  {
    10001: 'Sales',
    10002: 24,
    10003: 'Tn97A7q0XVDq',
    30001: '2022',
    HqlYadRdHJ5c: 'Labels',
    Tn97A7q0XVDq: 24,
    '7ti8XuX4kcY1': '2022',
  },
  {
    10001: 'Sales',
    10002: 20,
    10003: 'Tn97A7q0XVDq',
    30001: '2023',
    HqlYadRdHJ5c: 'Tables',
    Tn97A7q0XVDq: 20,
    '7ti8XuX4kcY1': '2023',
  },
  {
    10001: 'Sales',
    10002: 10,
    10003: 'Tn97A7q0XVDq',
    30001: '2022',
    HqlYadRdHJ5c: 'Tables',
    Tn97A7q0XVDq: 10,
    '7ti8XuX4kcY1': '2022',
  },
  {
    10001: 'Sales',
    10002: 40,
    10003: 'Tn97A7q0XVDq',
    30001: '2023',
    HqlYadRdHJ5c: 'Storage',
    Tn97A7q0XVDq: 40,
    '7ti8XuX4kcY1': '2023',
  },
  {
    10001: 'Sales',
    10002: 20,
    10003: 'Tn97A7q0XVDq',
    30001: '2022',
    HqlYadRdHJ5c: 'Storage',
    Tn97A7q0XVDq: 20,
    '7ti8XuX4kcY1': '2022',
  },
  {
    10001: 'Sales',
    10002: 10,
    10003: 'Tn97A7q0XVDq',
    30001: '2023',
    HqlYadRdHJ5c: 'Furn',
    Tn97A7q0XVDq: 10,
    '7ti8XuX4kcY1': '2023',
  },
  {
    10001: 'Sales',
    10002: 10,
    10003: 'Tn97A7q0XVDq',
    30001: '2022',
    HqlYadRdHJ5c: 'Furn',
    Tn97A7q0XVDq: 10,
    '7ti8XuX4kcY1': '2022',
  },
  {
    10001: 'Sales',
    10002: 50,
    10003: 'Tn97A7q0XVDq',
    30001: '2023',
    HqlYadRdHJ5c: 'Art',
    Tn97A7q0XVDq: 50,
    '7ti8XuX4kcY1': '2023',
  },
  {
    10001: 'Sales',
    10002: 30,
    10003: 'Tn97A7q0XVDq',
    30001: '2022',
    HqlYadRdHJ5c: 'Art',
    Tn97A7q0XVDq: 30,
    '7ti8XuX4kcY1': '2022',
  },
]

const DIMENSIONS = [
  { id: 'HqlYadRdHJ5c', alias: 'Category' },
  { id: '7ti8XuX4kcY1', alias: 'Year' },
]
const MEASURES = [{ id: 'Tn97A7q0XVDq', alias: 'Sales' }]

const useLargeScreenThemes = () => {
  const [themes, setThemes] = useState({})
  const [themeKey, setThemeKey] = useState('')
  useEffect(() => {
    fetch(THEME_URL)
      .then((r) => r.json())
      .then((remote) => {
        const registry = {}
        Object.entries(remote).forEach(([key, value]) => {
          const colors = value?.colors || []
          registry[key] = {
            baseTheme: 'dark',
            colorScheme: colors.length >= 2 ? colors : [colors[0] || '#00D5FF', '#227BFF'],
            linearColorScheme: [colors[0] || '#003B73', colors[1] || '#00D5FF'],
            textPrimary: '#EAF7FF',
            textSecondary: '#8FC7FF',
            borderColor: 'rgba(82, 191, 255, 0.28)',
            surfaceColor: '#071A33',
            tooltipBackgroundColor: 'rgba(5, 18, 38, 0.92)',
            axisGridColor: 'rgba(82, 191, 255, 0.18)',
            accentColor: colors[0] || '#00D5FF',
          }
        })
        registerTokenThemes(registry, { ensureRegisterAll: false })
        setThemes(remote)
        const firstKey = Object.keys(remote)[0] || ''
        setThemeKey(firstKey)
      })
      .catch(() => {})
  }, [])
  return { themes, themeKey, setThemeKey }
}

const ThemeSelect = ({ themes, themeKey, onChange }) => (
  <select
    value={themeKey}
    onChange={(e) => onChange(e.target.value)}
    style={{
      marginBottom: 12,
      color: '#EAF7FF',
      background: 'rgba(0, 213, 255, 0.18)',
      border: '1px solid rgba(0, 213, 255, 0.45)',
      padding: '6px 12px',
    }}
  >
    {Object.entries(themes).map(([key, value]) => (
      <option key={key} value={key} style={{ color: '#000' }}>
        {value?.name || key}
      </option>
    ))}
  </select>
)

export default () => {
  const { themes, themeKey, setThemeKey } = useLargeScreenThemes()
  if (!themeKey) return null
  const vseedConfig = {
    theme: themeKey,
    chartType: 'columnParallel',
    dataset: DATASET,
    dimensions: DIMENSIONS,
    measures: MEASURES,
    label: { enable: false },
    animation: {
      enable: true,
      params: {
        appear: { enable: true, effects: ['growth'], ease: 'circInOut', duration: 1 },
      },
    },
  }
  return (
    <div style={{ padding: 16, background: '#061A33' }}>
      <ThemeSelect themes={themes} themeKey={themeKey} onChange={setThemeKey} />
      <VSeedRender vseed={vseedConfig} />
    </div>
  )
}

2. Animation en boucle

L'animation en boucle est le mouvement continu après le rendu initial, utile pour mettre en évidence ou maintenir un rythme dynamique.

2.1 highlight (mise en évidence de groupe) + atmosphere (effet d'ambiance)

import { useEffect, useState } from 'react'
import { VSeedRender } from '@components'
import { registerTokenThemes } from '@visactor/vseed'

const THEME_URL = 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/theme.json'
const DATASET = [
  {
    10001: 'Sales',
    10002: 30,
    10003: 'Tn97A7q0XVDq',
    30001: '2023',
    HqlYadRdHJ5c: 'Labels',
    Tn97A7q0XVDq: 30,
    '7ti8XuX4kcY1': '2023',
  },
  {
    10001: 'Sales',
    10002: 24,
    10003: 'Tn97A7q0XVDq',
    30001: '2022',
    HqlYadRdHJ5c: 'Labels',
    Tn97A7q0XVDq: 24,
    '7ti8XuX4kcY1': '2022',
  },
  {
    10001: 'Sales',
    10002: 20,
    10003: 'Tn97A7q0XVDq',
    30001: '2023',
    HqlYadRdHJ5c: 'Tables',
    Tn97A7q0XVDq: 20,
    '7ti8XuX4kcY1': '2023',
  },
  {
    10001: 'Sales',
    10002: 10,
    10003: 'Tn97A7q0XVDq',
    30001: '2022',
    HqlYadRdHJ5c: 'Tables',
    Tn97A7q0XVDq: 10,
    '7ti8XuX4kcY1': '2022',
  },
  {
    10001: 'Sales',
    10002: 40,
    10003: 'Tn97A7q0XVDq',
    30001: '2023',
    HqlYadRdHJ5c: 'Storage',
    Tn97A7q0XVDq: 40,
    '7ti8XuX4kcY1': '2023',
  },
  {
    10001: 'Sales',
    10002: 20,
    10003: 'Tn97A7q0XVDq',
    30001: '2022',
    HqlYadRdHJ5c: 'Storage',
    Tn97A7q0XVDq: 20,
    '7ti8XuX4kcY1': '2022',
  },
  {
    10001: 'Sales',
    10002: 10,
    10003: 'Tn97A7q0XVDq',
    30001: '2023',
    HqlYadRdHJ5c: 'Furn',
    Tn97A7q0XVDq: 10,
    '7ti8XuX4kcY1': '2023',
  },
  {
    10001: 'Sales',
    10002: 10,
    10003: 'Tn97A7q0XVDq',
    30001: '2022',
    HqlYadRdHJ5c: 'Furn',
    Tn97A7q0XVDq: 10,
    '7ti8XuX4kcY1': '2022',
  },
  {
    10001: 'Sales',
    10002: 50,
    10003: 'Tn97A7q0XVDq',
    30001: '2023',
    HqlYadRdHJ5c: 'Art',
    Tn97A7q0XVDq: 50,
    '7ti8XuX4kcY1': '2023',
  },
  {
    10001: 'Sales',
    10002: 30,
    10003: 'Tn97A7q0XVDq',
    30001: '2022',
    HqlYadRdHJ5c: 'Art',
    Tn97A7q0XVDq: 30,
    '7ti8XuX4kcY1': '2022',
  },
]

export default () => {
  const [themes, setThemes] = useState({})
  const [themeKey, setThemeKey] = useState('')
  useEffect(() => {
    fetch('https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/theme.json')
      .then((r) => r.json())
      .then((remote) => {
        const registry = {}
        Object.entries(remote).forEach(([key, value]) => {
          const colors = value?.colors || []
          registry[key] = {
            baseTheme: 'dark',
            colorScheme: colors.length >= 2 ? colors : [colors[0] || '#00D5FF', '#227BFF'],
            linearColorScheme: [colors[0] || '#003B73', colors[1] || '#00D5FF'],
            textPrimary: '#EAF7FF',
            textSecondary: '#8FC7FF',
            borderColor: 'rgba(82, 191, 255, 0.28)',
            surfaceColor: '#071A33',
            tooltipBackgroundColor: 'rgba(5, 18, 38, 0.92)',
            axisGridColor: 'rgba(82, 191, 255, 0.18)',
            accentColor: colors[0] || '#00D5FF',
          }
        })
        registerTokenThemes(registry, { ensureRegisterAll: false })
        setThemes(remote)
        setThemeKey(Object.keys(remote)[0] || '')
      })
      .catch(() => {})
  }, [])
  if (!themeKey) return null
  const vseedConfig = {
    theme: themeKey,
    chartType: 'columnParallel',
    dataset: DATASET,
    dimensions: [
      { id: 'HqlYadRdHJ5c', alias: 'Category' },
      { id: '7ti8XuX4kcY1', alias: 'Year' },
    ],
    measures: [{ id: 'Tn97A7q0XVDq', alias: 'Sales' }],
    label: { enable: false },
    animation: {
      enable: true,
      params: {
        appear: { enable: true, effects: ['growth'], ease: 'linear', duration: 1 },
        loop: {
          enable: true,
          interval: 3,
          loop: { enable: true, effects: ['highLight'], ease: 'linear', duration: 1 },
          atmosphere: { effect: 'breath', ease: 'linear', color: '#4A90E2' },
        },
      },
    },
  }
  return (
    <div style={{ padding: 16, background: '#061A33' }}>
      <select
        value={themeKey}
        onChange={(e) => setThemeKey(e.target.value)}
        style={{
          marginBottom: 12,
          color: '#EAF7FF',
          background: 'rgba(0, 213, 255, 0.18)',
          border: '1px solid rgba(0, 213, 255, 0.45)',
          padding: '6px 12px',
        }}
      >
        {Object.entries(themes).map(([key, value]) => (
          <option key={key} value={key} style={{ color: '#000' }}>
            {value?.name || key}
          </option>
        ))}
      </select>
      <VSeedRender vseed={vseedConfig} />
    </div>
  )
}

2.2 growth (boucle) + atmosphere (effet d'ambiance)

import { useEffect, useState } from 'react'
import { VSeedRender } from '@components'
import { registerTokenThemes } from '@visactor/vseed'

const DATASET = [
  {
    10001: 'Sales',
    10002: 30,
    10003: 'Tn97A7q0XVDq',
    30001: '2023',
    HqlYadRdHJ5c: 'Labels',
    Tn97A7q0XVDq: 30,
    '7ti8XuX4kcY1': '2023',
  },
  {
    10001: 'Sales',
    10002: 24,
    10003: 'Tn97A7q0XVDq',
    30001: '2022',
    HqlYadRdHJ5c: 'Labels',
    Tn97A7q0XVDq: 24,
    '7ti8XuX4kcY1': '2022',
  },
  {
    10001: 'Sales',
    10002: 20,
    10003: 'Tn97A7q0XVDq',
    30001: '2023',
    HqlYadRdHJ5c: 'Tables',
    Tn97A7q0XVDq: 20,
    '7ti8XuX4kcY1': '2023',
  },
  {
    10001: 'Sales',
    10002: 10,
    10003: 'Tn97A7q0XVDq',
    30001: '2022',
    HqlYadRdHJ5c: 'Tables',
    Tn97A7q0XVDq: 10,
    '7ti8XuX4kcY1': '2022',
  },
  {
    10001: 'Sales',
    10002: 40,
    10003: 'Tn97A7q0XVDq',
    30001: '2023',
    HqlYadRdHJ5c: 'Storage',
    Tn97A7q0XVDq: 40,
    '7ti8XuX4kcY1': '2023',
  },
  {
    10001: 'Sales',
    10002: 20,
    10003: 'Tn97A7q0XVDq',
    30001: '2022',
    HqlYadRdHJ5c: 'Storage',
    Tn97A7q0XVDq: 20,
    '7ti8XuX4kcY1': '2022',
  },
  {
    10001: 'Sales',
    10002: 10,
    10003: 'Tn97A7q0XVDq',
    30001: '2023',
    HqlYadRdHJ5c: 'Furn',
    Tn97A7q0XVDq: 10,
    '7ti8XuX4kcY1': '2023',
  },
  {
    10001: 'Sales',
    10002: 10,
    10003: 'Tn97A7q0XVDq',
    30001: '2022',
    HqlYadRdHJ5c: 'Furn',
    Tn97A7q0XVDq: 10,
    '7ti8XuX4kcY1': '2022',
  },
  {
    10001: 'Sales',
    10002: 50,
    10003: 'Tn97A7q0XVDq',
    30001: '2023',
    HqlYadRdHJ5c: 'Art',
    Tn97A7q0XVDq: 50,
    '7ti8XuX4kcY1': '2023',
  },
  {
    10001: 'Sales',
    10002: 30,
    10003: 'Tn97A7q0XVDq',
    30001: '2022',
    HqlYadRdHJ5c: 'Art',
    Tn97A7q0XVDq: 30,
    '7ti8XuX4kcY1': '2022',
  },
]

export default () => {
  const [themes, setThemes] = useState({})
  const [themeKey, setThemeKey] = useState('')
  useEffect(() => {
    fetch('https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/theme.json')
      .then((r) => r.json())
      .then((remote) => {
        const registry = {}
        Object.entries(remote).forEach(([key, value]) => {
          const colors = value?.colors || []
          registry[key] = {
            baseTheme: 'dark',
            colorScheme: colors.length >= 2 ? colors : [colors[0] || '#00D5FF', '#227BFF'],
            linearColorScheme: [colors[0] || '#003B73', colors[1] || '#00D5FF'],
            textPrimary: '#EAF7FF',
            textSecondary: '#8FC7FF',
            borderColor: 'rgba(82, 191, 255, 0.28)',
            surfaceColor: '#071A33',
            tooltipBackgroundColor: 'rgba(5, 18, 38, 0.92)',
            axisGridColor: 'rgba(82, 191, 255, 0.18)',
            accentColor: colors[0] || '#00D5FF',
          }
        })
        registerTokenThemes(registry, { ensureRegisterAll: false })
        setThemes(remote)
        setThemeKey(Object.keys(remote)[0] || '')
      })
      .catch(() => {})
  }, [])
  if (!themeKey) return null
  const vseedConfig = {
    theme: themeKey,
    chartType: 'barParallel',
    dataset: DATASET,
    dimensions: [
      { id: 'HqlYadRdHJ5c', alias: 'Category' },
      { id: '7ti8XuX4kcY1', alias: 'Year' },
    ],
    measures: [{ id: 'Tn97A7q0XVDq', alias: 'Sales' }],
    label: { enable: false },
    animation: {
      enable: true,
      params: {
        appear: { enable: true, effects: ['growth'], ease: 'linear', duration: 1 },
        loop: {
          enable: true,
          interval: 2,
          loop: { enable: true, effects: ['growth'], ease: 'linear', duration: 1 },
          atmo: { effect: 'breath', ease: 'linear', color: '#4A90E2' },
        },
      },
    },
  }
  return (
    <div style={{ padding: 16, background: '#061A33' }}>
      <select
        value={themeKey}
        onChange={(e) => setThemeKey(e.target.value)}
        style={{
          marginBottom: 12,
          color: '#EAF7FF',
          background: 'rgba(0, 213, 255, 0.18)',
          border: '1px solid rgba(0, 213, 255, 0.45)',
          padding: '6px 12px',
        }}
      >
        {Object.entries(themes).map(([key, value]) => (
          <option key={key} value={key} style={{ color: '#000' }}>
            {value?.name || key}
          </option>
        ))}
      </select>
      <VSeedRender vseed={vseedConfig} />
    </div>
  )
}

2.3 moveIn (boucle) + atmosphere (effet d'ambiance)

import { useEffect, useState } from 'react'
import { VSeedRender } from '@components'
import { registerTokenThemes } from '@visactor/vseed'

const DATASET = [
  {
    10001: 'Sales',
    10002: 30,
    10003: 'Tn97A7q0XVDq',
    30001: '2023',
    HqlYadRdHJ5c: 'Labels',
    Tn97A7q0XVDq: 30,
    '7ti8XuX4kcY1': '2023',
  },
  {
    10001: 'Sales',
    10002: 24,
    10003: 'Tn97A7q0XVDq',
    30001: '2022',
    HqlYadRdHJ5c: 'Labels',
    Tn97A7q0XVDq: 24,
    '7ti8XuX4kcY1': '2022',
  },
  {
    10001: 'Sales',
    10002: 20,
    10003: 'Tn97A7q0XVDq',
    30001: '2023',
    HqlYadRdHJ5c: 'Tables',
    Tn97A7q0XVDq: 20,
    '7ti8XuX4kcY1': '2023',
  },
  {
    10001: 'Sales',
    10002: 10,
    10003: 'Tn97A7q0XVDq',
    30001: '2022',
    HqlYadRdHJ5c: 'Tables',
    Tn97A7q0XVDq: 10,
    '7ti8XuX4kcY1': '2022',
  },
  {
    10001: 'Sales',
    10002: 40,
    10003: 'Tn97A7q0XVDq',
    30001: '2023',
    HqlYadRdHJ5c: 'Storage',
    Tn97A7q0XVDq: 40,
    '7ti8XuX4kcY1': '2023',
  },
  {
    10001: 'Sales',
    10002: 20,
    10003: 'Tn97A7q0XVDq',
    30001: '2022',
    HqlYadRdHJ5c: 'Storage',
    Tn97A7q0XVDq: 20,
    '7ti8XuX4kcY1': '2022',
  },
  {
    10001: 'Sales',
    10002: 10,
    10003: 'Tn97A7q0XVDq',
    30001: '2023',
    HqlYadRdHJ5c: 'Furn',
    Tn97A7q0XVDq: 10,
    '7ti8XuX4kcY1': '2023',
  },
  {
    10001: 'Sales',
    10002: 10,
    10003: 'Tn97A7q0XVDq',
    30001: '2022',
    HqlYadRdHJ5c: 'Furn',
    Tn97A7q0XVDq: 10,
    '7ti8XuX4kcY1': '2022',
  },
  {
    10001: 'Sales',
    10002: 50,
    10003: 'Tn97A7q0XVDq',
    30001: '2023',
    HqlYadRdHJ5c: 'Art',
    Tn97A7q0XVDq: 50,
    '7ti8XuX4kcY1': '2023',
  },
  {
    10001: 'Sales',
    10002: 30,
    10003: 'Tn97A7q0XVDq',
    30001: '2022',
    HqlYadRdHJ5c: 'Art',
    Tn97A7q0XVDq: 30,
    '7ti8XuX4kcY1': '2022',
  },
]

export default () => {
  const [themes, setThemes] = useState({})
  const [themeKey, setThemeKey] = useState('')
  useEffect(() => {
    fetch('https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/theme.json')
      .then((r) => r.json())
      .then((remote) => {
        const registry = {}
        Object.entries(remote).forEach(([key, value]) => {
          const colors = value?.colors || []
          registry[key] = {
            baseTheme: 'dark',
            colorScheme: colors.length >= 2 ? colors : [colors[0] || '#00D5FF', '#227BFF'],
            linearColorScheme: [colors[0] || '#003B73', colors[1] || '#00D5FF'],
            textPrimary: '#EAF7FF',
            textSecondary: '#8FC7FF',
            borderColor: 'rgba(82, 191, 255, 0.28)',
            surfaceColor: '#071A33',
            tooltipBackgroundColor: 'rgba(5, 18, 38, 0.92)',
            axisGridColor: 'rgba(82, 191, 255, 0.18)',
            accentColor: colors[0] || '#00D5FF',
          }
        })
        registerTokenThemes(registry, { ensureRegisterAll: false })
        setThemes(remote)
        setThemeKey(Object.keys(remote)[0] || '')
      })
      .catch(() => {})
  }, [])
  if (!themeKey) return null
  const vseedConfig = {
    theme: themeKey,
    chartType: 'columnParallel',
    dataset: DATASET,
    dimensions: [
      { id: 'HqlYadRdHJ5c', alias: 'Category' },
      { id: '7ti8XuX4kcY1', alias: 'Year' },
    ],
    measures: [{ id: 'Tn97A7q0XVDq', alias: 'Sales' }],
    label: { enable: false },
    animation: {
      enable: true,
      params: {
        appear: { enable: true, effects: ['growth'], ease: 'linear', duration: 1 },
        loop: {
          enable: true,
          interval: 2,
          loop: { enable: true, effects: ['moveIn'], ease: 'linear', duration: 1 },
          atmo: { effect: 'breath', ease: 'linear', color: '#4A90E2' },
        },
      },
    },
  }
  return (
    <div style={{ padding: 16, background: '#061A33' }}>
      <select
        value={themeKey}
        onChange={(e) => setThemeKey(e.target.value)}
        style={{
          marginBottom: 12,
          color: '#EAF7FF',
          background: 'rgba(0, 213, 255, 0.18)',
          border: '1px solid rgba(0, 213, 255, 0.45)',
          padding: '6px 12px',
        }}
      >
        {Object.entries(themes).map(([key, value]) => (
          <option key={key} value={key} style={{ color: '#000' }}>
            {value?.name || key}
          </option>
        ))}
      </select>
      <VSeedRender vseed={vseedConfig} />
    </div>
  )
}

Animation des graphiques ligne / aire

Tous les exemples reposent sur le thème grand écran (sombre) et proposent une liste déroulante pour changer de thème. Les thèmes sont chargés depuis : https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/theme.json

1. Animation d'apparition

L'animation d'apparition introduit visuellement le graphique lorsqu'il entre pour la première fois dans la page.

1.1 load (chargement)

import { useEffect, useState } from 'react'
import { VSeedRender } from '@components'
import { registerTokenThemes, Builder } from '@visactor/vseed'

const THEME_URL = 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/theme.json'
const DATASET = [
  {
    10001: 'Sales',
    10002: 30,
    10003: 'Tn97A7q0XVDq',
    30001: '2023',
    HqlYadRdHJ5c: 'Labels',
    Tn97A7q0XVDq: 30,
    '7ti8XuX4kcY1': '2023',
  },
  {
    10001: 'Sales',
    10002: 24,
    10003: 'Tn97A7q0XVDq',
    30001: '2022',
    HqlYadRdHJ5c: 'Labels',
    Tn97A7q0XVDq: 24,
    '7ti8XuX4kcY1': '2022',
  },
  {
    10001: 'Sales',
    10002: 20,
    10003: 'Tn97A7q0XVDq',
    30001: '2023',
    HqlYadRdHJ5c: 'Tables',
    Tn97A7q0XVDq: 20,
    '7ti8XuX4kcY1': '2023',
  },
  {
    10001: 'Sales',
    10002: 10,
    10003: 'Tn97A7q0XVDq',
    30001: '2022',
    HqlYadRdHJ5c: 'Tables',
    Tn97A7q0XVDq: 10,
    '7ti8XuX4kcY1': '2022',
  },
  {
    10001: 'Sales',
    10002: 40,
    10003: 'Tn97A7q0XVDq',
    30001: '2023',
    HqlYadRdHJ5c: 'Storage',
    Tn97A7q0XVDq: 40,
    '7ti8XuX4kcY1': '2023',
  },
  {
    10001: 'Sales',
    10002: 20,
    10003: 'Tn97A7q0XVDq',
    30001: '2022',
    HqlYadRdHJ5c: 'Storage',
    Tn97A7q0XVDq: 20,
    '7ti8XuX4kcY1': '2022',
  },
  {
    10001: 'Sales',
    10002: 10,
    10003: 'Tn97A7q0XVDq',
    30001: '2023',
    HqlYadRdHJ5c: 'Furn',
    Tn97A7q0XVDq: 10,
    '7ti8XuX4kcY1': '2023',
  },
  {
    10001: 'Sales',
    10002: 10,
    10003: 'Tn97A7q0XVDq',
    30001: '2022',
    HqlYadRdHJ5c: 'Furn',
    Tn97A7q0XVDq: 10,
    '7ti8XuX4kcY1': '2022',
  },
  {
    10001: 'Sales',
    10002: 50,
    10003: 'Tn97A7q0XVDq',
    30001: '2023',
    HqlYadRdHJ5c: 'Art',
    Tn97A7q0XVDq: 50,
    '7ti8XuX4kcY1': '2023',
  },
  {
    10001: 'Sales',
    10002: 30,
    10003: 'Tn97A7q0XVDq',
    30001: '2022',
    HqlYadRdHJ5c: 'Art',
    Tn97A7q0XVDq: 30,
    '7ti8XuX4kcY1': '2022',
  },
]

const DIMENSIONS = [
  { id: 'HqlYadRdHJ5c', alias: 'Category' },
  { id: '7ti8XuX4kcY1', alias: 'Year' },
]
const MEASURES = [{ id: 'Tn97A7q0XVDq', alias: 'Sales' }]

const useLargeScreenThemes = () => {
  const [themes, setThemes] = useState({})
  const [themeKey, setThemeKey] = useState('')
  useEffect(() => {
    Builder.updateSpec('line', (spec) => ({
      ...spec,
      point: { ...spec.point, style: { ...(spec.point && spec.point.style), lineWidth: 0, stroke: null } },
    }))
    Builder.updateSpec('area', (spec) => ({
      ...spec,
      point: { ...spec.point, style: { ...(spec.point && spec.point.style), lineWidth: 0, stroke: null } },
    }))
    Builder.updateSpec('areaPercent', (spec) => ({
      ...spec,
      point: { ...spec.point, style: { ...(spec.point && spec.point.style), lineWidth: 0, stroke: null } },
    }))
    fetch(THEME_URL)
      .then((r) => r.json())
      .then((remote) => {
        const registry = {}
        Object.entries(remote).forEach(([key, value]) => {
          const colors = value?.colors || []
          registry[key] = {
            baseTheme: 'dark',
            colorScheme: colors.length >= 2 ? colors : [colors[0] || '#00D5FF', '#227BFF'],
            linearColorScheme: [colors[0] || '#003B73', colors[1] || '#00D5FF'],
            textPrimary: '#EAF7FF',
            textSecondary: '#8FC7FF',
            borderColor: 'rgba(82, 191, 255, 0.28)',
            surfaceColor: '#071A33',
            tooltipBackgroundColor: 'rgba(5, 18, 38, 0.92)',
            axisGridColor: 'rgba(82, 191, 255, 0.18)',
            accentColor: colors[0] || '#00D5FF',
          }
        })
        registerTokenThemes(registry, { ensureRegisterAll: false })
        setThemes(remote)
        setThemeKey(Object.keys(remote)[0] || '')
      })
      .catch(() => {})
  }, [])
  return { themes, themeKey, setThemeKey }
}

const ThemeSelect = ({ themes, themeKey, onChange }) => (
  <select
    value={themeKey}
    onChange={(e) => onChange(e.target.value)}
    style={{
      marginBottom: 12,
      color: '#EAF7FF',
      background: 'rgba(0, 213, 255, 0.18)',
      border: '1px solid rgba(0, 213, 255, 0.45)',
      padding: '6px 12px',
    }}
  >
    {Object.entries(themes).map(([key, value]) => (
      <option key={key} value={key} style={{ color: '#000' }}>
        {value?.name || key}
      </option>
    ))}
  </select>
)

export default () => {
  const { themes, themeKey, setThemeKey } = useLargeScreenThemes()
  if (!themeKey) return null
  const vseedConfig = {
    theme: themeKey,
    chartType: 'line',
    dataset: DATASET,
    dimensions: DIMENSIONS,
    measures: MEASURES,
    label: { enable: false },
    animation: {
      enable: true,
      params: { appear: { enable: true, effects: ['load'], ease: 'circInOut', duration: 1 } },
    },
  }
  return (
    <div style={{ padding: 16, background: '#061A33' }}>
      <ThemeSelect themes={themes} themeKey={themeKey} onChange={setThemeKey} />
      <VSeedRender vseed={vseedConfig} />
    </div>
  )
}

1.2 growth (croissance)

import { useEffect, useState } from 'react'
import { VSeedRender } from '@components'
import { registerTokenThemes, Builder } from '@visactor/vseed'

const DATASET = [
  {
    10001: 'Sales',
    10002: 30,
    10003: 'Tn97A7q0XVDq',
    30001: '2023',
    HqlYadRdHJ5c: 'Labels',
    Tn97A7q0XVDq: 30,
    '7ti8XuX4kcY1': '2023',
  },
  {
    10001: 'Sales',
    10002: 24,
    10003: 'Tn97A7q0XVDq',
    30001: '2022',
    HqlYadRdHJ5c: 'Labels',
    Tn97A7q0XVDq: 24,
    '7ti8XuX4kcY1': '2022',
  },
  {
    10001: 'Sales',
    10002: 20,
    10003: 'Tn97A7q0XVDq',
    30001: '2023',
    HqlYadRdHJ5c: 'Tables',
    Tn97A7q0XVDq: 20,
    '7ti8XuX4kcY1': '2023',
  },
  {
    10001: 'Sales',
    10002: 10,
    10003: 'Tn97A7q0XVDq',
    30001: '2022',
    HqlYadRdHJ5c: 'Tables',
    Tn97A7q0XVDq: 10,
    '7ti8XuX4kcY1': '2022',
  },
  {
    10001: 'Sales',
    10002: 40,
    10003: 'Tn97A7q0XVDq',
    30001: '2023',
    HqlYadRdHJ5c: 'Storage',
    Tn97A7q0XVDq: 40,
    '7ti8XuX4kcY1': '2023',
  },
  {
    10001: 'Sales',
    10002: 20,
    10003: 'Tn97A7q0XVDq',
    30001: '2022',
    HqlYadRdHJ5c: 'Storage',
    Tn97A7q0XVDq: 20,
    '7ti8XuX4kcY1': '2022',
  },
  {
    10001: 'Sales',
    10002: 10,
    10003: 'Tn97A7q0XVDq',
    30001: '2023',
    HqlYadRdHJ5c: 'Furn',
    Tn97A7q0XVDq: 10,
    '7ti8XuX4kcY1': '2023',
  },
  {
    10001: 'Sales',
    10002: 10,
    10003: 'Tn97A7q0XVDq',
    30001: '2022',
    HqlYadRdHJ5c: 'Furn',
    Tn97A7q0XVDq: 10,
    '7ti8XuX4kcY1': '2022',
  },
  {
    10001: 'Sales',
    10002: 50,
    10003: 'Tn97A7q0XVDq',
    30001: '2023',
    HqlYadRdHJ5c: 'Art',
    Tn97A7q0XVDq: 50,
    '7ti8XuX4kcY1': '2023',
  },
  {
    10001: 'Sales',
    10002: 30,
    10003: 'Tn97A7q0XVDq',
    30001: '2022',
    HqlYadRdHJ5c: 'Art',
    Tn97A7q0XVDq: 30,
    '7ti8XuX4kcY1': '2022',
  },
]

export default () => {
  const [themes, setThemes] = useState({})
  const [themeKey, setThemeKey] = useState('')
  useEffect(() => {
    Builder.updateSpec('line', (spec) => ({
      ...spec,
      point: { ...spec.point, style: { ...(spec.point && spec.point.style), lineWidth: 0, stroke: null } },
    }))
    Builder.updateSpec('area', (spec) => ({
      ...spec,
      point: { ...spec.point, style: { ...(spec.point && spec.point.style), lineWidth: 0, stroke: null } },
    }))
    Builder.updateSpec('areaPercent', (spec) => ({
      ...spec,
      point: { ...spec.point, style: { ...(spec.point && spec.point.style), lineWidth: 0, stroke: null } },
    }))
    fetch('https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/theme.json')
      .then((r) => r.json())
      .then((remote) => {
        const registry = {}
        Object.entries(remote).forEach(([key, value]) => {
          const colors = value?.colors || []
          registry[key] = {
            baseTheme: 'dark',
            colorScheme: colors.length >= 2 ? colors : [colors[0] || '#00D5FF', '#227BFF'],
            linearColorScheme: [colors[0] || '#003B73', colors[1] || '#00D5FF'],
            textPrimary: '#EAF7FF',
            textSecondary: '#8FC7FF',
            borderColor: 'rgba(82, 191, 255, 0.28)',
            surfaceColor: '#071A33',
            tooltipBackgroundColor: 'rgba(5, 18, 38, 0.92)',
            axisGridColor: 'rgba(82, 191, 255, 0.18)',
            accentColor: colors[0] || '#00D5FF',
          }
        })
        registerTokenThemes(registry, { ensureRegisterAll: false })
        setThemes(remote)
        setThemeKey(Object.keys(remote)[0] || '')
      })
      .catch(() => {})
  }, [])
  if (!themeKey) return null
  const vseedConfig = {
    theme: themeKey,
    chartType: 'area',
    dataset: DATASET,
    dimensions: [
      { id: 'HqlYadRdHJ5c', alias: 'Category' },
      { id: '7ti8XuX4kcY1', alias: 'Year' },
    ],
    measures: [{ id: 'Tn97A7q0XVDq', alias: 'Sales' }],
    label: { enable: false },
    animation: {
      enable: true,
      params: { appear: { enable: true, effects: ['growth'], ease: 'linear', duration: 1 } },
    },
  }
  return (
    <div style={{ padding: 16, background: '#061A33' }}>
      <select
        value={themeKey}
        onChange={(e) => setThemeKey(e.target.value)}
        style={{
          marginBottom: 12,
          color: '#EAF7FF',
          background: 'rgba(0, 213, 255, 0.18)',
          border: '1px solid rgba(0, 213, 255, 0.45)',
          padding: '6px 12px',
        }}
      >
        {Object.entries(themes).map(([key, value]) => (
          <option key={key} value={key} style={{ color: '#000' }}>
            {value?.name || key}
          </option>
        ))}
      </select>
      <VSeedRender vseed={vseedConfig} />
    </div>
  )
}

2. Animation en boucle

L'animation en boucle maintient le graphique dans un mouvement cyclique pour les affichages de longue durée.

2.1 load (boucle) + atmosphere (breath)

import { useEffect, useState } from 'react'
import { VSeedRender } from '@components'
import { registerTokenThemes, Builder } from '@visactor/vseed'

const DATASET = [
  {
    10001: 'Sales',
    10002: 30,
    10003: 'Tn97A7q0XVDq',
    30001: '2023',
    HqlYadRdHJ5c: 'Labels',
    Tn97A7q0XVDq: 30,
    '7ti8XuX4kcY1': '2023',
  },
  {
    10001: 'Sales',
    10002: 24,
    10003: 'Tn97A7q0XVDq',
    30001: '2022',
    HqlYadRdHJ5c: 'Labels',
    Tn97A7q0XVDq: 24,
    '7ti8XuX4kcY1': '2022',
  },
  {
    10001: 'Sales',
    10002: 20,
    10003: 'Tn97A7q0XVDq',
    30001: '2023',
    HqlYadRdHJ5c: 'Tables',
    Tn97A7q0XVDq: 20,
    '7ti8XuX4kcY1': '2023',
  },
  {
    10001: 'Sales',
    10002: 10,
    10003: 'Tn97A7q0XVDq',
    30001: '2022',
    HqlYadRdHJ5c: 'Tables',
    Tn97A7q0XVDq: 10,
    '7ti8XuX4kcY1': '2022',
  },
  {
    10001: 'Sales',
    10002: 40,
    10003: 'Tn97A7q0XVDq',
    30001: '2023',
    HqlYadRdHJ5c: 'Storage',
    Tn97A7q0XVDq: 40,
    '7ti8XuX4kcY1': '2023',
  },
  {
    10001: 'Sales',
    10002: 20,
    10003: 'Tn97A7q0XVDq',
    30001: '2022',
    HqlYadRdHJ5c: 'Storage',
    Tn97A7q0XVDq: 20,
    '7ti8XuX4kcY1': '2022',
  },
  {
    10001: 'Sales',
    10002: 10,
    10003: 'Tn97A7q0XVDq',
    30001: '2023',
    HqlYadRdHJ5c: 'Furn',
    Tn97A7q0XVDq: 10,
    '7ti8XuX4kcY1': '2023',
  },
  {
    10001: 'Sales',
    10002: 10,
    10003: 'Tn97A7q0XVDq',
    30001: '2022',
    HqlYadRdHJ5c: 'Furn',
    Tn97A7q0XVDq: 10,
    '7ti8XuX4kcY1': '2022',
  },
  {
    10001: 'Sales',
    10002: 50,
    10003: 'Tn97A7q0XVDq',
    30001: '2023',
    HqlYadRdHJ5c: 'Art',
    Tn97A7q0XVDq: 50,
    '7ti8XuX4kcY1': '2023',
  },
  {
    10001: 'Sales',
    10002: 30,
    10003: 'Tn97A7q0XVDq',
    30001: '2022',
    HqlYadRdHJ5c: 'Art',
    Tn97A7q0XVDq: 30,
    '7ti8XuX4kcY1': '2022',
  },
]

export default () => {
  const [themes, setThemes] = useState({})
  const [themeKey, setThemeKey] = useState('')
  useEffect(() => {
    Builder.updateSpec('line', (spec) => ({
      ...spec,
      point: { ...spec.point, style: { ...(spec.point && spec.point.style), lineWidth: 0, stroke: null } },
    }))
    Builder.updateSpec('area', (spec) => ({
      ...spec,
      point: { ...spec.point, style: { ...(spec.point && spec.point.style), lineWidth: 0, stroke: null } },
    }))
    Builder.updateSpec('areaPercent', (spec) => ({
      ...spec,
      point: { ...spec.point, style: { ...(spec.point && spec.point.style), lineWidth: 0, stroke: null } },
    }))
    fetch('https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/theme.json')
      .then((r) => r.json())
      .then((remote) => {
        const registry = {}
        Object.entries(remote).forEach(([key, value]) => {
          const colors = value?.colors || []
          registry[key] = {
            baseTheme: 'dark',
            colorScheme: colors.length >= 2 ? colors : [colors[0] || '#00D5FF', '#227BFF'],
            linearColorScheme: [colors[0] || '#003B73', colors[1] || '#00D5FF'],
            textPrimary: '#EAF7FF',
            textSecondary: '#8FC7FF',
            borderColor: 'rgba(82, 191, 255, 0.28)',
            surfaceColor: '#071A33',
            tooltipBackgroundColor: 'rgba(5, 18, 38, 0.92)',
            axisGridColor: 'rgba(82, 191, 255, 0.18)',
            accentColor: colors[0] || '#00D5FF',
          }
        })
        registerTokenThemes(registry, { ensureRegisterAll: false })
        setThemes(remote)
        setThemeKey(Object.keys(remote)[0] || '')
      })
      .catch(() => {})
  }, [])
  if (!themeKey) return null
  const vseedConfig = {
    theme: themeKey,
    chartType: 'line',
    dataset: DATASET,
    dimensions: [
      { id: 'HqlYadRdHJ5c', alias: 'Category' },
      { id: '7ti8XuX4kcY1', alias: 'Year' },
    ],
    measures: [{ id: 'Tn97A7q0XVDq', alias: 'Sales' }],
    label: { enable: false },
    animation: {
      enable: true,
      params: {
        appear: { enable: true, effects: ['load'], ease: 'linear', duration: 1 },
        loop: {
          enable: true,
          interval: 2,
          loop: { enable: true, effects: ['load'], ease: 'linear', duration: 1 },
          atmosphere: { effect: 'breath', ease: 'linear' },
        },
      },
    },
  }
  return (
    <div style={{ padding: 16, background: '#061A33' }}>
      <select
        value={themeKey}
        onChange={(e) => setThemeKey(e.target.value)}
        style={{
          marginBottom: 12,
          color: '#EAF7FF',
          background: 'rgba(0, 213, 255, 0.18)',
          border: '1px solid rgba(0, 213, 255, 0.45)',
          padding: '6px 12px',
        }}
      >
        {Object.entries(themes).map(([key, value]) => (
          <option key={key} value={key} style={{ color: '#000' }}>
            {value?.name || key}
          </option>
        ))}
      </select>
      <VSeedRender vseed={vseedConfig} />
    </div>
  )
}

2.2 load (boucle) + atmosphere (ripple)

import { useEffect, useState } from 'react'
import { VSeedRender } from '@components'
import { registerTokenThemes, Builder } from '@visactor/vseed'

const DATASET = [
  {
    10001: 'Sales',
    10002: 30,
    10003: 'Tn97A7q0XVDq',
    30001: '2023',
    HqlYadRdHJ5c: 'Labels',
    Tn97A7q0XVDq: 30,
    '7ti8XuX4kcY1': '2023',
  },
  {
    10001: 'Sales',
    10002: 24,
    10003: 'Tn97A7q0XVDq',
    30001: '2022',
    HqlYadRdHJ5c: 'Labels',
    Tn97A7q0XVDq: 24,
    '7ti8XuX4kcY1': '2022',
  },
  {
    10001: 'Sales',
    10002: 20,
    10003: 'Tn97A7q0XVDq',
    30001: '2023',
    HqlYadRdHJ5c: 'Tables',
    Tn97A7q0XVDq: 20,
    '7ti8XuX4kcY1': '2023',
  },
  {
    10001: 'Sales',
    10002: 10,
    10003: 'Tn97A7q0XVDq',
    30001: '2022',
    HqlYadRdHJ5c: 'Tables',
    Tn97A7q0XVDq: 10,
    '7ti8XuX4kcY1': '2022',
  },
  {
    10001: 'Sales',
    10002: 40,
    10003: 'Tn97A7q0XVDq',
    30001: '2023',
    HqlYadRdHJ5c: 'Storage',
    Tn97A7q0XVDq: 40,
    '7ti8XuX4kcY1': '2023',
  },
  {
    10001: 'Sales',
    10002: 20,
    10003: 'Tn97A7q0XVDq',
    30001: '2022',
    HqlYadRdHJ5c: 'Storage',
    Tn97A7q0XVDq: 20,
    '7ti8XuX4kcY1': '2022',
  },
  {
    10001: 'Sales',
    10002: 10,
    10003: 'Tn97A7q0XVDq',
    30001: '2023',
    HqlYadRdHJ5c: 'Furn',
    Tn97A7q0XVDq: 10,
    '7ti8XuX4kcY1': '2023',
  },
  {
    10001: 'Sales',
    10002: 10,
    10003: 'Tn97A7q0XVDq',
    30001: '2022',
    HqlYadRdHJ5c: 'Furn',
    Tn97A7q0XVDq: 10,
    '7ti8XuX4kcY1': '2022',
  },
  {
    10001: 'Sales',
    10002: 50,
    10003: 'Tn97A7q0XVDq',
    30001: '2023',
    HqlYadRdHJ5c: 'Art',
    Tn97A7q0XVDq: 50,
    '7ti8XuX4kcY1': '2023',
  },
  {
    10001: 'Sales',
    10002: 30,
    10003: 'Tn97A7q0XVDq',
    30001: '2022',
    HqlYadRdHJ5c: 'Art',
    Tn97A7q0XVDq: 30,
    '7ti8XuX4kcY1': '2022',
  },
]

export default () => {
  const [themes, setThemes] = useState({})
  const [themeKey, setThemeKey] = useState('')
  useEffect(() => {
    Builder.updateSpec('line', (spec) => ({
      ...spec,
      point: { ...spec.point, style: { ...(spec.point && spec.point.style), lineWidth: 0, stroke: null } },
    }))
    Builder.updateSpec('area', (spec) => ({
      ...spec,
      point: { ...spec.point, style: { ...(spec.point && spec.point.style), lineWidth: 0, stroke: null } },
    }))
    Builder.updateSpec('areaPercent', (spec) => ({
      ...spec,
      point: { ...spec.point, style: { ...(spec.point && spec.point.style), lineWidth: 0, stroke: null } },
    }))
    fetch('https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/theme.json')
      .then((r) => r.json())
      .then((remote) => {
        const registry = {}
        Object.entries(remote).forEach(([key, value]) => {
          const colors = value?.colors || []
          registry[key] = {
            baseTheme: 'dark',
            colorScheme: colors.length >= 2 ? colors : [colors[0] || '#00D5FF', '#227BFF'],
            linearColorScheme: [colors[0] || '#003B73', colors[1] || '#00D5FF'],
            textPrimary: '#EAF7FF',
            textSecondary: '#8FC7FF',
            borderColor: 'rgba(82, 191, 255, 0.28)',
            surfaceColor: '#071A33',
            tooltipBackgroundColor: 'rgba(5, 18, 38, 0.92)',
            axisGridColor: 'rgba(82, 191, 255, 0.18)',
            accentColor: colors[0] || '#00D5FF',
          }
        })
        registerTokenThemes(registry, { ensureRegisterAll: false })
        setThemes(remote)
        setThemeKey(Object.keys(remote)[0] || '')
      })
      .catch(() => {})
  }, [])
  if (!themeKey) return null
  const vseedConfig = {
    theme: themeKey,
    chartType: 'areaPercent',
    dataset: DATASET,
    dimensions: [
      { id: 'HqlYadRdHJ5c', alias: 'Category' },
      { id: '7ti8XuX4kcY1', alias: 'Year' },
    ],
    measures: [{ id: 'Tn97A7q0XVDq', alias: 'Sales' }],
    label: { enable: false },
    animation: {
      enable: true,
      params: {
        appear: { enable: true, effects: ['growth'], ease: 'linear', duration: 1 },
        loop: {
          enable: true,
          interval: 3,
          loop: { enable: true, effects: ['load'], ease: 'circInOut', duration: 1 },
          atmosphere: { effect: 'ripple', ease: 'linear' },
        },
      },
    },
  }
  return (
    <div style={{ padding: 16, background: '#061A33' }}>
      <select
        value={themeKey}
        onChange={(e) => setThemeKey(e.target.value)}
        style={{
          marginBottom: 12,
          color: '#EAF7FF',
          background: 'rgba(0, 213, 255, 0.18)',
          border: '1px solid rgba(0, 213, 255, 0.45)',
          padding: '6px 12px',
        }}
      >
        {Object.entries(themes).map(([key, value]) => (
          <option key={key} value={key} style={{ color: '#000' }}>
            {value?.name || key}
          </option>
        ))}
      </select>
      <VSeedRender vseed={vseedConfig} />
    </div>
  )
}

Animation des graphiques secteurs / anneau / rose

Tous les exemples reposent sur le thème grand écran (sombre) et proposent une liste déroulante pour changer de thème. Les thèmes sont chargés depuis : https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/theme.json

1. Animation d'apparition

L'animation d'apparition contrôle la première entrée des secteurs dans la vue.

1.1 radial (depuis le centre)

import { useEffect, useState } from 'react'
import { VSeedRender } from '@components'
import { registerTokenThemes } from '@visactor/vseed'

const THEME_URL = 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/theme.json'
const DATASET = [
  {
    10001: 'Sales',
    10002: 30,
    10003: 'Tn97A7q0XVDq',
    30001: '2023',
    HqlYadRdHJ5c: 'Labels',
    Tn97A7q0XVDq: 30,
    '7ti8XuX4kcY1': '2023',
  },
  {
    10001: 'Sales',
    10002: 24,
    10003: 'Tn97A7q0XVDq',
    30001: '2022',
    HqlYadRdHJ5c: 'Labels',
    Tn97A7q0XVDq: 24,
    '7ti8XuX4kcY1': '2022',
  },
  {
    10001: 'Sales',
    10002: 20,
    10003: 'Tn97A7q0XVDq',
    30001: '2023',
    HqlYadRdHJ5c: 'Tables',
    Tn97A7q0XVDq: 20,
    '7ti8XuX4kcY1': '2023',
  },
  {
    10001: 'Sales',
    10002: 10,
    10003: 'Tn97A7q0XVDq',
    30001: '2022',
    HqlYadRdHJ5c: 'Tables',
    Tn97A7q0XVDq: 10,
    '7ti8XuX4kcY1': '2022',
  },
  {
    10001: 'Sales',
    10002: 40,
    10003: 'Tn97A7q0XVDq',
    30001: '2023',
    HqlYadRdHJ5c: 'Storage',
    Tn97A7q0XVDq: 40,
    '7ti8XuX4kcY1': '2023',
  },
  {
    10001: 'Sales',
    10002: 20,
    10003: 'Tn97A7q0XVDq',
    30001: '2022',
    HqlYadRdHJ5c: 'Storage',
    Tn97A7q0XVDq: 20,
    '7ti8XuX4kcY1': '2022',
  },
  {
    10001: 'Sales',
    10002: 10,
    10003: 'Tn97A7q0XVDq',
    30001: '2023',
    HqlYadRdHJ5c: 'Furn',
    Tn97A7q0XVDq: 10,
    '7ti8XuX4kcY1': '2023',
  },
  {
    10001: 'Sales',
    10002: 10,
    10003: 'Tn97A7q0XVDq',
    30001: '2022',
    HqlYadRdHJ5c: 'Furn',
    Tn97A7q0XVDq: 10,
    '7ti8XuX4kcY1': '2022',
  },
  {
    10001: 'Sales',
    10002: 50,
    10003: 'Tn97A7q0XVDq',
    30001: '2023',
    HqlYadRdHJ5c: 'Art',
    Tn97A7q0XVDq: 50,
    '7ti8XuX4kcY1': '2023',
  },
  {
    10001: 'Sales',
    10002: 30,
    10003: 'Tn97A7q0XVDq',
    30001: '2022',
    HqlYadRdHJ5c: 'Art',
    Tn97A7q0XVDq: 30,
    '7ti8XuX4kcY1': '2022',
  },
]

const DIMENSIONS = [{ id: 'HqlYadRdHJ5c', alias: 'Category' }]
const MEASURES = [{ id: 'Tn97A7q0XVDq', alias: 'Sales' }]

const useLargeScreenThemes = () => {
  const [themes, setThemes] = useState({})
  const [themeKey, setThemeKey] = useState('')
  useEffect(() => {
    fetch(THEME_URL)
      .then((r) => r.json())
      .then((remote) => {
        const registry = {}
        Object.entries(remote).forEach(([key, value]) => {
          const colors = value?.colors || []
          registry[key] = {
            baseTheme: 'dark',
            colorScheme: colors.length >= 2 ? colors : [colors[0] || '#00D5FF', '#227BFF'],
            linearColorScheme: [colors[0] || '#003B73', colors[1] || '#00D5FF'],
            textPrimary: '#EAF7FF',
            textSecondary: '#8FC7FF',
            borderColor: 'rgba(82, 191, 255, 0.28)',
            surfaceColor: '#071A33',
            tooltipBackgroundColor: 'rgba(5, 18, 38, 0.92)',
            axisGridColor: 'rgba(82, 191, 255, 0.18)',
            accentColor: colors[0] || '#00D5FF',
          }
        })
        registerTokenThemes(registry, { ensureRegisterAll: false })
        setThemes(remote)
        setThemeKey(Object.keys(remote)[0] || '')
      })
      .catch(() => {})
  }, [])
  return { themes, themeKey, setThemeKey }
}

const ThemeSelect = ({ themes, themeKey, onChange }) => (
  <select
    value={themeKey}
    onChange={(e) => onChange(e.target.value)}
    style={{
      marginBottom: 12,
      color: '#EAF7FF',
      background: 'rgba(0, 213, 255, 0.18)',
      border: '1px solid rgba(0, 213, 255, 0.45)',
      padding: '6px 12px',
    }}
  >
    {Object.entries(themes).map(([key, value]) => (
      <option key={key} value={key} style={{ color: '#000' }}>
        {value?.name || key}
      </option>
    ))}
  </select>
)

export default () => {
  const { themes, themeKey, setThemeKey } = useLargeScreenThemes()
  if (!themeKey) return null
  const vseedConfig = {
    theme: themeKey,
    chartType: 'pie',
    dataset: DATASET,
    dimensions: DIMENSIONS,
    measures: MEASURES,
    label: { enable: false },
    animation: {
      enable: true,
      params: { appear: { enable: true, effects: ['radial'], ease: 'circInOut', duration: 1 } },
    },
  }
  return (
    <div style={{ padding: 16, background: '#061A33' }}>
      <ThemeSelect themes={themes} themeKey={themeKey} onChange={setThemeKey} />
      <VSeedRender vseed={vseedConfig} />
    </div>
  )
}

1.2 scale (mise à l'échelle)

import { useEffect, useState } from 'react'
import { VSeedRender } from '@components'
import { registerTokenThemes } from '@visactor/vseed'

const DATASET = [
  {
    10001: 'Sales',
    10002: 30,
    10003: 'Tn97A7q0XVDq',
    30001: '2023',
    HqlYadRdHJ5c: 'Labels',
    Tn97A7q0XVDq: 30,
    '7ti8XuX4kcY1': '2023',
  },
  {
    10001: 'Sales',
    10002: 24,
    10003: 'Tn97A7q0XVDq',
    30001: '2022',
    HqlYadRdHJ5c: 'Labels',
    Tn97A7q0XVDq: 24,
    '7ti8XuX4kcY1': '2022',
  },
  {
    10001: 'Sales',
    10002: 20,
    10003: 'Tn97A7q0XVDq',
    30001: '2023',
    HqlYadRdHJ5c: 'Tables',
    Tn97A7q0XVDq: 20,
    '7ti8XuX4kcY1': '2023',
  },
  {
    10001: 'Sales',
    10002: 10,
    10003: 'Tn97A7q0XVDq',
    30001: '2022',
    HqlYadRdHJ5c: 'Tables',
    Tn97A7q0XVDq: 10,
    '7ti8XuX4kcY1': '2022',
  },
  {
    10001: 'Sales',
    10002: 40,
    10003: 'Tn97A7q0XVDq',
    30001: '2023',
    HqlYadRdHJ5c: 'Storage',
    Tn97A7q0XVDq: 40,
    '7ti8XuX4kcY1': '2023',
  },
  {
    10001: 'Sales',
    10002: 20,
    10003: 'Tn97A7q0XVDq',
    30001: '2022',
    HqlYadRdHJ5c: 'Storage',
    Tn97A7q0XVDq: 20,
    '7ti8XuX4kcY1': '2022',
  },
  {
    10001: 'Sales',
    10002: 10,
    10003: 'Tn97A7q0XVDq',
    30001: '2023',
    HqlYadRdHJ5c: 'Furn',
    Tn97A7q0XVDq: 10,
    '7ti8XuX4kcY1': '2023',
  },
  {
    10001: 'Sales',
    10002: 10,
    10003: 'Tn97A7q0XVDq',
    30001: '2022',
    HqlYadRdHJ5c: 'Furn',
    Tn97A7q0XVDq: 10,
    '7ti8XuX4kcY1': '2022',
  },
  {
    10001: 'Sales',
    10002: 50,
    10003: 'Tn97A7q0XVDq',
    30001: '2023',
    HqlYadRdHJ5c: 'Art',
    Tn97A7q0XVDq: 50,
    '7ti8XuX4kcY1': '2023',
  },
  {
    10001: 'Sales',
    10002: 30,
    10003: 'Tn97A7q0XVDq',
    30001: '2022',
    HqlYadRdHJ5c: 'Art',
    Tn97A7q0XVDq: 30,
    '7ti8XuX4kcY1': '2022',
  },
]

export default () => {
  const [themes, setThemes] = useState({})
  const [themeKey, setThemeKey] = useState('')
  useEffect(() => {
    fetch('https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/theme.json')
      .then((r) => r.json())
      .then((remote) => {
        const registry = {}
        Object.entries(remote).forEach(([key, value]) => {
          const colors = value?.colors || []
          registry[key] = {
            baseTheme: 'dark',
            colorScheme: colors.length >= 2 ? colors : [colors[0] || '#00D5FF', '#227BFF'],
            linearColorScheme: [colors[0] || '#003B73', colors[1] || '#00D5FF'],
            textPrimary: '#EAF7FF',
            textSecondary: '#8FC7FF',
            borderColor: 'rgba(82, 191, 255, 0.28)',
            surfaceColor: '#071A33',
            tooltipBackgroundColor: 'rgba(5, 18, 38, 0.92)',
            axisGridColor: 'rgba(82, 191, 255, 0.18)',
            accentColor: colors[0] || '#00D5FF',
          }
        })
        registerTokenThemes(registry, { ensureRegisterAll: false })
        setThemes(remote)
        setThemeKey(Object.keys(remote)[0] || '')
      })
      .catch(() => {})
  }, [])
  if (!themeKey) return null
  const vseedConfig = {
    theme: themeKey,
    chartType: 'donut',
    dataset: DATASET,
    dimensions: [{ id: 'HqlYadRdHJ5c', alias: 'Category' }],
    measures: [{ id: 'Tn97A7q0XVDq', alias: 'Sales' }],
    label: { enable: false },
    animation: {
      enable: true,
      params: { appear: { enable: true, effects: ['scale'], ease: 'circInOut', duration: 1 } },
    },
  }
  return (
    <div style={{ padding: 16, background: '#061A33' }}>
      <select
        value={themeKey}
        onChange={(e) => setThemeKey(e.target.value)}
        style={{
          marginBottom: 12,
          color: '#EAF7FF',
          background: 'rgba(0, 213, 255, 0.18)',
          border: '1px solid rgba(0, 213, 255, 0.45)',
          padding: '6px 12px',
        }}
      >
        {Object.entries(themes).map(([key, value]) => (
          <option key={key} value={key} style={{ color: '#000' }}>
            {value?.name || key}
          </option>
        ))}
      </select>
      <VSeedRender vseed={vseedConfig} />
    </div>
  )
}

2. Animation en boucle

L'animation en boucle continue de mettre certains secteurs en évidence de façon périodique.

2.1 enlarge (modifier la taille)

import { useEffect, useState } from 'react'
import { VSeedRender } from '@components'
import { registerTokenThemes } from '@visactor/vseed'

const DATASET = [
  {
    10001: 'Sales',
    10002: 30,
    10003: 'Tn97A7q0XVDq',
    30001: '2023',
    HqlYadRdHJ5c: 'Labels',
    Tn97A7q0XVDq: 30,
    '7ti8XuX4kcY1': '2023',
  },
  {
    10001: 'Sales',
    10002: 24,
    10003: 'Tn97A7q0XVDq',
    30001: '2022',
    HqlYadRdHJ5c: 'Labels',
    Tn97A7q0XVDq: 24,
    '7ti8XuX4kcY1': '2022',
  },
  {
    10001: 'Sales',
    10002: 20,
    10003: 'Tn97A7q0XVDq',
    30001: '2023',
    HqlYadRdHJ5c: 'Tables',
    Tn97A7q0XVDq: 20,
    '7ti8XuX4kcY1': '2023',
  },
  {
    10001: 'Sales',
    10002: 10,
    10003: 'Tn97A7q0XVDq',
    30001: '2022',
    HqlYadRdHJ5c: 'Tables',
    Tn97A7q0XVDq: 10,
    '7ti8XuX4kcY1': '2022',
  },
  {
    10001: 'Sales',
    10002: 40,
    10003: 'Tn97A7q0XVDq',
    30001: '2023',
    HqlYadRdHJ5c: 'Storage',
    Tn97A7q0XVDq: 40,
    '7ti8XuX4kcY1': '2023',
  },
  {
    10001: 'Sales',
    10002: 20,
    10003: 'Tn97A7q0XVDq',
    30001: '2022',
    HqlYadRdHJ5c: 'Storage',
    Tn97A7q0XVDq: 20,
    '7ti8XuX4kcY1': '2022',
  },
  {
    10001: 'Sales',
    10002: 10,
    10003: 'Tn97A7q0XVDq',
    30001: '2023',
    HqlYadRdHJ5c: 'Furn',
    Tn97A7q0XVDq: 10,
    '7ti8XuX4kcY1': '2023',
  },
  {
    10001: 'Sales',
    10002: 10,
    10003: 'Tn97A7q0XVDq',
    30001: '2022',
    HqlYadRdHJ5c: 'Furn',
    Tn97A7q0XVDq: 10,
    '7ti8XuX4kcY1': '2022',
  },
  {
    10001: 'Sales',
    10002: 50,
    10003: 'Tn97A7q0XVDq',
    30001: '2023',
    HqlYadRdHJ5c: 'Art',
    Tn97A7q0XVDq: 50,
    '7ti8XuX4kcY1': '2023',
  },
  {
    10001: 'Sales',
    10002: 30,
    10003: 'Tn97A7q0XVDq',
    30001: '2022',
    HqlYadRdHJ5c: 'Art',
    Tn97A7q0XVDq: 30,
    '7ti8XuX4kcY1': '2022',
  },
]

export default () => {
  const [themes, setThemes] = useState({})
  const [themeKey, setThemeKey] = useState('')
  useEffect(() => {
    fetch('https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/theme.json')
      .then((r) => r.json())
      .then((remote) => {
        const registry = {}
        Object.entries(remote).forEach(([key, value]) => {
          const colors = value?.colors || []
          registry[key] = {
            baseTheme: 'dark',
            colorScheme: colors.length >= 2 ? colors : [colors[0] || '#00D5FF', '#227BFF'],
            linearColorScheme: [colors[0] || '#003B73', colors[1] || '#00D5FF'],
            textPrimary: '#EAF7FF',
            textSecondary: '#8FC7FF',
            borderColor: 'rgba(82, 191, 255, 0.28)',
            surfaceColor: '#071A33',
            tooltipBackgroundColor: 'rgba(5, 18, 38, 0.92)',
            axisGridColor: 'rgba(82, 191, 255, 0.18)',
            accentColor: colors[0] || '#00D5FF',
          }
        })
        registerTokenThemes(registry, { ensureRegisterAll: false })
        setThemes(remote)
        setThemeKey(Object.keys(remote)[0] || '')
      })
      .catch(() => {})
  }, [])
  if (!themeKey) return null
  const vseedConfig = {
    theme: themeKey,
    chartType: 'pie',
    dataset: DATASET,
    dimensions: [{ id: 'HqlYadRdHJ5c', alias: 'Category' }],
    measures: [{ id: 'Tn97A7q0XVDq', alias: 'Sales' }],
    label: { enable: false },
    animation: {
      enable: true,
      params: {
        appear: { enable: true, effects: ['scale'], ease: 'linear', duration: 1 },
        loop: { enable: true, interval: 2, loop: { enable: true, effects: ['enlarge'], ease: 'linear', duration: 1 } },
      },
    },
  }
  return (
    <div style={{ padding: 16, background: '#061A33' }}>
      <select
        value={themeKey}
        onChange={(e) => setThemeKey(e.target.value)}
        style={{
          marginBottom: 12,
          color: '#EAF7FF',
          background: 'rgba(0, 213, 255, 0.18)',
          border: '1px solid rgba(0, 213, 255, 0.45)',
          padding: '6px 12px',
        }}
      >
        {Object.entries(themes).map(([key, value]) => (
          <option key={key} value={key} style={{ color: '#000' }}>
            {value?.name || key}
          </option>
        ))}
      </select>
      <VSeedRender vseed={vseedConfig} />
    </div>
  )
}

2.2 relocate (déplacer le centre)

import { useEffect, useState } from 'react'
import { VSeedRender } from '@components'
import { registerTokenThemes } from '@visactor/vseed'

const DATASET = [
  {
    10001: 'Sales',
    10002: 30,
    10003: 'Tn97A7q0XVDq',
    30001: '2023',
    HqlYadRdHJ5c: 'Labels',
    Tn97A7q0XVDq: 30,
    '7ti8XuX4kcY1': '2023',
  },
  {
    10001: 'Sales',
    10002: 24,
    10003: 'Tn97A7q0XVDq',
    30001: '2022',
    HqlYadRdHJ5c: 'Labels',
    Tn97A7q0XVDq: 24,
    '7ti8XuX4kcY1': '2022',
  },
  {
    10001: 'Sales',
    10002: 20,
    10003: 'Tn97A7q0XVDq',
    30001: '2023',
    HqlYadRdHJ5c: 'Tables',
    Tn97A7q0XVDq: 20,
    '7ti8XuX4kcY1': '2023',
  },
  {
    10001: 'Sales',
    10002: 10,
    10003: 'Tn97A7q0XVDq',
    30001: '2022',
    HqlYadRdHJ5c: 'Tables',
    Tn97A7q0XVDq: 10,
    '7ti8XuX4kcY1': '2022',
  },
  {
    10001: 'Sales',
    10002: 40,
    10003: 'Tn97A7q0XVDq',
    30001: '2023',
    HqlYadRdHJ5c: 'Storage',
    Tn97A7q0XVDq: 40,
    '7ti8XuX4kcY1': '2023',
  },
  {
    10001: 'Sales',
    10002: 20,
    10003: 'Tn97A7q0XVDq',
    30001: '2022',
    HqlYadRdHJ5c: 'Storage',
    Tn97A7q0XVDq: 20,
    '7ti8XuX4kcY1': '2022',
  },
  {
    10001: 'Sales',
    10002: 10,
    10003: 'Tn97A7q0XVDq',
    30001: '2023',
    HqlYadRdHJ5c: 'Furn',
    Tn97A7q0XVDq: 10,
    '7ti8XuX4kcY1': '2023',
  },
  {
    10001: 'Sales',
    10002: 10,
    10003: 'Tn97A7q0XVDq',
    30001: '2022',
    HqlYadRdHJ5c: 'Furn',
    Tn97A7q0XVDq: 10,
    '7ti8XuX4kcY1': '2022',
  },
  {
    10001: 'Sales',
    10002: 50,
    10003: 'Tn97A7q0XVDq',
    30001: '2023',
    HqlYadRdHJ5c: 'Art',
    Tn97A7q0XVDq: 50,
    '7ti8XuX4kcY1': '2023',
  },
  {
    10001: 'Sales',
    10002: 30,
    10003: 'Tn97A7q0XVDq',
    30001: '2022',
    HqlYadRdHJ5c: 'Art',
    Tn97A7q0XVDq: 30,
    '7ti8XuX4kcY1': '2022',
  },
]

export default () => {
  const [themes, setThemes] = useState({})
  const [themeKey, setThemeKey] = useState('')
  useEffect(() => {
    fetch('https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/theme.json')
      .then((r) => r.json())
      .then((remote) => {
        const registry = {}
        Object.entries(remote).forEach(([key, value]) => {
          const colors = value?.colors || []
          registry[key] = {
            baseTheme: 'dark',
            colorScheme: colors.length >= 2 ? colors : [colors[0] || '#00D5FF', '#227BFF'],
            linearColorScheme: [colors[0] || '#003B73', colors[1] || '#00D5FF'],
            textPrimary: '#EAF7FF',
            textSecondary: '#8FC7FF',
            borderColor: 'rgba(82, 191, 255, 0.28)',
            surfaceColor: '#071A33',
            tooltipBackgroundColor: 'rgba(5, 18, 38, 0.92)',
            axisGridColor: 'rgba(82, 191, 255, 0.18)',
            accentColor: colors[0] || '#00D5FF',
          }
        })
        registerTokenThemes(registry, { ensureRegisterAll: false })
        setThemes(remote)
        setThemeKey(Object.keys(remote)[0] || '')
      })
      .catch(() => {})
  }, [])
  if (!themeKey) return null
  const vseedConfig = {
    theme: themeKey,
    chartType: 'pie',
    dataset: DATASET,
    dimensions: [{ id: 'HqlYadRdHJ5c', alias: 'Category' }],
    measures: [{ id: 'Tn97A7q0XVDq', alias: 'Sales' }],
    label: { enable: false },
    animation: {
      enable: true,
      params: {
        appear: { enable: true, effects: ['radial'], ease: 'linear', duration: 1 },
        loop: { enable: true, interval: 2, loop: { enable: true, effects: ['relocate'], ease: 'linear', duration: 1 } },
      },
    },
  }
  return (
    <div style={{ padding: 16, background: '#061A33' }}>
      <select
        value={themeKey}
        onChange={(e) => setThemeKey(e.target.value)}
        style={{
          marginBottom: 12,
          color: '#EAF7FF',
          background: 'rgba(0, 213, 255, 0.18)',
          border: '1px solid rgba(0, 213, 255, 0.45)',
          padding: '6px 12px',
        }}
      >
        {Object.entries(themes).map(([key, value]) => (
          <option key={key} value={key} style={{ color: '#000' }}>
            {value?.name || key}
          </option>
        ))}
      </select>
      <VSeedRender vseed={vseedConfig} />
    </div>
  )
}

Animation radar

Tous les exemples reposent sur le thème grand écran (sombre) et proposent une liste déroulante pour changer de thème. Les thèmes sont chargés depuis : https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/theme.json

1. Animation d'apparition

L'animation d'apparition contrôle la première présentation des surfaces radar et des points.

1.1 radial (depuis le centre)

import { useEffect, useState } from 'react'
import { VSeedRender } from '@components'
import { registerTokenThemes, Builder } from '@visactor/vseed'

const THEME_URL = 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/theme.json'
const DATASET = [
  {
    10001: 'Sales',
    10002: 30,
    10003: 'Tn97A7q0XVDq',
    30001: '2023',
    HqlYadRdHJ5c: 'Labels',
    Tn97A7q0XVDq: 30,
    '7ti8XuX4kcY1': '2023',
  },
  {
    10001: 'Sales',
    10002: 24,
    10003: 'Tn97A7q0XVDq',
    30001: '2022',
    HqlYadRdHJ5c: 'Labels',
    Tn97A7q0XVDq: 24,
    '7ti8XuX4kcY1': '2022',
  },
  {
    10001: 'Sales',
    10002: 20,
    10003: 'Tn97A7q0XVDq',
    30001: '2023',
    HqlYadRdHJ5c: 'Tables',
    Tn97A7q0XVDq: 20,
    '7ti8XuX4kcY1': '2023',
  },
  {
    10001: 'Sales',
    10002: 10,
    10003: 'Tn97A7q0XVDq',
    30001: '2022',
    HqlYadRdHJ5c: 'Tables',
    Tn97A7q0XVDq: 10,
    '7ti8XuX4kcY1': '2022',
  },
  {
    10001: 'Sales',
    10002: 40,
    10003: 'Tn97A7q0XVDq',
    30001: '2023',
    HqlYadRdHJ5c: 'Storage',
    Tn97A7q0XVDq: 40,
    '7ti8XuX4kcY1': '2023',
  },
  {
    10001: 'Sales',
    10002: 20,
    10003: 'Tn97A7q0XVDq',
    30001: '2022',
    HqlYadRdHJ5c: 'Storage',
    Tn97A7q0XVDq: 20,
    '7ti8XuX4kcY1': '2022',
  },
  {
    10001: 'Sales',
    10002: 10,
    10003: 'Tn97A7q0XVDq',
    30001: '2023',
    HqlYadRdHJ5c: 'Furn',
    Tn97A7q0XVDq: 10,
    '7ti8XuX4kcY1': '2023',
  },
  {
    10001: 'Sales',
    10002: 10,
    10003: 'Tn97A7q0XVDq',
    30001: '2022',
    HqlYadRdHJ5c: 'Furn',
    Tn97A7q0XVDq: 10,
    '7ti8XuX4kcY1': '2022',
  },
  {
    10001: 'Sales',
    10002: 50,
    10003: 'Tn97A7q0XVDq',
    30001: '2023',
    HqlYadRdHJ5c: 'Art',
    Tn97A7q0XVDq: 50,
    '7ti8XuX4kcY1': '2023',
  },
  {
    10001: 'Sales',
    10002: 30,
    10003: 'Tn97A7q0XVDq',
    30001: '2022',
    HqlYadRdHJ5c: 'Art',
    Tn97A7q0XVDq: 30,
    '7ti8XuX4kcY1': '2022',
  },
]

const DIMENSIONS = [{ id: 'HqlYadRdHJ5c', alias: 'Category' }]
const MEASURES = [{ id: 'Tn97A7q0XVDq', alias: 'Sales' }]

const useLargeScreenThemes = () => {
  const [themes, setThemes] = useState({})
  const [themeKey, setThemeKey] = useState('')
  useEffect(() => {
    Builder.updateSpec('radar', (spec) => ({
      ...spec,
      point: { ...spec.point, style: { ...(spec.point && spec.point.style), lineWidth: 0, stroke: null } },
      axes: [
        {
          orient: 'radius',
          tick: { forceTickCount: 3 },
          grid: { smooth: false, style: { stroke: 'rgba(255,255,255,0.15)', lineWidth: 1 } },
          label: { visible: false, style: { fontFamily: 'D-DIN' } },
        },
        {
          orient: 'angle',
          sampling: false,
          tick: { visible: true, style: () => ({ visible: false }) },
          domainLine: { style: { visible: false } },
          grid: { style: () => ({ stroke: 'rgba(255,255,255,0.15)', lineDash: [2, 1], lineWidth: 1 }) },
          label: { style: { fontFamily: 'D-DIN', fill: 'rgba(255,255,255,0.65)', fontSize: 12 }, visible: true },
        },
      ],
    }))
    fetch(THEME_URL)
      .then((r) => r.json())
      .then((remote) => {
        const registry = {}
        Object.entries(remote).forEach(([key, value]) => {
          const colors = value?.colors || []
          registry[key] = {
            baseTheme: 'dark',
            colorScheme: colors.length >= 2 ? colors : [colors[0] || '#00D5FF', '#227BFF'],
            linearColorScheme: [colors[0] || '#003B73', colors[1] || '#00D5FF'],
            textPrimary: '#EAF7FF',
            textSecondary: '#8FC7FF',
            borderColor: 'rgba(82, 191, 255, 0.28)',
            surfaceColor: '#071A33',
            tooltipBackgroundColor: 'rgba(5, 18, 38, 0.92)',
            axisGridColor: 'rgba(82, 191, 255, 0.18)',
            accentColor: colors[0] || '#00D5FF',
          }
        })
        registerTokenThemes(registry, { ensureRegisterAll: false })
        setThemes(remote)
        setThemeKey(Object.keys(remote)[0] || '')
      })
      .catch(() => {})
  }, [])
  return { themes, themeKey, setThemeKey }
}

const ThemeSelect = ({ themes, themeKey, onChange }) => (
  <select
    value={themeKey}
    onChange={(e) => onChange(e.target.value)}
    style={{
      marginBottom: 12,
      color: '#EAF7FF',
      background: 'rgba(0, 213, 255, 0.18)',
      border: '1px solid rgba(0, 213, 255, 0.45)',
      padding: '6px 12px',
    }}
  >
    {Object.entries(themes).map(([key, value]) => (
      <option key={key} value={key} style={{ color: '#000' }}>
        {value?.name || key}
      </option>
    ))}
  </select>
)

export default () => {
  const { themes, themeKey, setThemeKey } = useLargeScreenThemes()
  if (!themeKey) return null
  const vseedConfig = {
    theme: themeKey,
    chartType: 'radar',
    dataset: DATASET,
    dimensions: DIMENSIONS,
    measures: MEASURES,
    label: { enable: false },
    animation: {
      enable: true,
      params: { appear: { enable: true, effects: ['radial'], ease: 'circInOut', duration: 1 } },
    },
  }
  return (
    <div style={{ padding: 16, background: '#061A33' }}>
      <ThemeSelect themes={themes} themeKey={themeKey} onChange={setThemeKey} />
      <VSeedRender vseed={vseedConfig} />
    </div>
  )
}

1.2 scale (mise à l'échelle)

import { useEffect, useState } from 'react'
import { VSeedRender } from '@components'
import { registerTokenThemes, Builder } from '@visactor/vseed'

const DATASET = [
  {
    10001: 'Sales',
    10002: 30,
    10003: 'Tn97A7q0XVDq',
    30001: '2023',
    HqlYadRdHJ5c: 'Labels',
    Tn97A7q0XVDq: 30,
    '7ti8XuX4kcY1': '2023',
  },
  {
    10001: 'Sales',
    10002: 24,
    10003: 'Tn97A7q0XVDq',
    30001: '2022',
    HqlYadRdHJ5c: 'Labels',
    Tn97A7q0XVDq: 24,
    '7ti8XuX4kcY1': '2022',
  },
  {
    10001: 'Sales',
    10002: 20,
    10003: 'Tn97A7q0XVDq',
    30001: '2023',
    HqlYadRdHJ5c: 'Tables',
    Tn97A7q0XVDq: 20,
    '7ti8XuX4kcY1': '2023',
  },
  {
    10001: 'Sales',
    10002: 10,
    10003: 'Tn97A7q0XVDq',
    30001: '2022',
    HqlYadRdHJ5c: 'Tables',
    Tn97A7q0XVDq: 10,
    '7ti8XuX4kcY1': '2022',
  },
  {
    10001: 'Sales',
    10002: 40,
    10003: 'Tn97A7q0XVDq',
    30001: '2023',
    HqlYadRdHJ5c: 'Storage',
    Tn97A7q0XVDq: 40,
    '7ti8XuX4kcY1': '2023',
  },
  {
    10001: 'Sales',
    10002: 20,
    10003: 'Tn97A7q0XVDq',
    30001: '2022',
    HqlYadRdHJ5c: 'Storage',
    Tn97A7q0XVDq: 20,
    '7ti8XuX4kcY1': '2022',
  },
  {
    10001: 'Sales',
    10002: 10,
    10003: 'Tn97A7q0XVDq',
    30001: '2023',
    HqlYadRdHJ5c: 'Furn',
    Tn97A7q0XVDq: 10,
    '7ti8XuX4kcY1': '2023',
  },
  {
    10001: 'Sales',
    10002: 10,
    10003: 'Tn97A7q0XVDq',
    30001: '2022',
    HqlYadRdHJ5c: 'Furn',
    Tn97A7q0XVDq: 10,
    '7ti8XuX4kcY1': '2022',
  },
  {
    10001: 'Sales',
    10002: 50,
    10003: 'Tn97A7q0XVDq',
    30001: '2023',
    HqlYadRdHJ5c: 'Art',
    Tn97A7q0XVDq: 50,
    '7ti8XuX4kcY1': '2023',
  },
  {
    10001: 'Sales',
    10002: 30,
    10003: 'Tn97A7q0XVDq',
    30001: '2022',
    HqlYadRdHJ5c: 'Art',
    Tn97A7q0XVDq: 30,
    '7ti8XuX4kcY1': '2022',
  },
]

export default () => {
  const [themes, setThemes] = useState({})
  const [themeKey, setThemeKey] = useState('')
  useEffect(() => {
    Builder.updateSpec('radar', (spec) => ({
      ...spec,
      point: { ...spec.point, style: { ...(spec.point && spec.point.style), lineWidth: 0, stroke: null } },
      axes: [
        {
          orient: 'radius',
          tick: { forceTickCount: 3 },
          grid: { smooth: false, style: { stroke: 'rgba(255,255,255,0.15)', lineWidth: 1 } },
          label: { visible: false, style: { fontFamily: 'D-DIN' } },
        },
        {
          orient: 'angle',
          sampling: false,
          tick: { visible: true, style: () => ({ visible: false }) },
          domainLine: { style: { visible: false } },
          grid: { style: () => ({ stroke: 'rgba(255,255,255,0.15)', lineDash: [2, 1], lineWidth: 1 }) },
          label: { style: { fontFamily: 'D-DIN', fill: 'rgba(255,255,255,0.65)', fontSize: 12 }, visible: true },
        },
      ],
    }))
    fetch('https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/theme.json')
      .then((r) => r.json())
      .then((remote) => {
        const registry = {}
        Object.entries(remote).forEach(([key, value]) => {
          const colors = value?.colors || []
          registry[key] = {
            baseTheme: 'dark',
            colorScheme: colors.length >= 2 ? colors : [colors[0] || '#00D5FF', '#227BFF'],
            linearColorScheme: [colors[0] || '#003B73', colors[1] || '#00D5FF'],
            textPrimary: '#EAF7FF',
            textSecondary: '#8FC7FF',
            borderColor: 'rgba(82, 191, 255, 0.28)',
            surfaceColor: '#071A33',
            tooltipBackgroundColor: 'rgba(5, 18, 38, 0.92)',
            axisGridColor: 'rgba(82, 191, 255, 0.18)',
            accentColor: colors[0] || '#00D5FF',
          }
        })
        registerTokenThemes(registry, { ensureRegisterAll: false })
        setThemes(remote)
        setThemeKey(Object.keys(remote)[0] || '')
      })
      .catch(() => {})
  }, [])
  if (!themeKey) return null
  const vseedConfig = {
    theme: themeKey,
    chartType: 'radar',
    dataset: DATASET,
    dimensions: [{ id: 'HqlYadRdHJ5c', alias: 'Category' }],
    measures: [{ id: 'Tn97A7q0XVDq', alias: 'Sales' }],
    label: { enable: false },
    animation: {
      enable: true,
      params: { appear: { enable: true, effects: ['scale'], ease: 'circInOut', duration: 1 } },
    },
  }
  return (
    <div style={{ padding: 16, background: '#061A33' }}>
      <select
        value={themeKey}
        onChange={(e) => setThemeKey(e.target.value)}
        style={{
          marginBottom: 12,
          color: '#EAF7FF',
          background: 'rgba(0, 213, 255, 0.18)',
          border: '1px solid rgba(0, 213, 255, 0.45)',
          padding: '6px 12px',
        }}
      >
        {Object.entries(themes).map(([key, value]) => (
          <option key={key} value={key} style={{ color: '#000' }}>
            {value?.name || key}
          </option>
        ))}
      </select>
      <VSeedRender vseed={vseedConfig} />
    </div>
  )
}

2. Animation en boucle

L'animation en boucle du radar s'exprime avec atmosphere.effect pour produire un mouvement d'ambiance continu.

2.1 none (boucle) + atmosphere (ripple)

import { useEffect, useState } from 'react'
import { VSeedRender } from '@components'
import { registerTokenThemes, Builder } from '@visactor/vseed'

const DATASET = [
  {
    10001: 'Sales',
    10002: 30,
    10003: 'Tn97A7q0XVDq',
    30001: '2023',
    HqlYadRdHJ5c: 'Labels',
    Tn97A7q0XVDq: 30,
    '7ti8XuX4kcY1': '2023',
  },
  {
    10001: 'Sales',
    10002: 24,
    10003: 'Tn97A7q0XVDq',
    30001: '2022',
    HqlYadRdHJ5c: 'Labels',
    Tn97A7q0XVDq: 24,
    '7ti8XuX4kcY1': '2022',
  },
  {
    10001: 'Sales',
    10002: 20,
    10003: 'Tn97A7q0XVDq',
    30001: '2023',
    HqlYadRdHJ5c: 'Tables',
    Tn97A7q0XVDq: 20,
    '7ti8XuX4kcY1': '2023',
  },
  {
    10001: 'Sales',
    10002: 10,
    10003: 'Tn97A7q0XVDq',
    30001: '2022',
    HqlYadRdHJ5c: 'Tables',
    Tn97A7q0XVDq: 10,
    '7ti8XuX4kcY1': '2022',
  },
  {
    10001: 'Sales',
    10002: 40,
    10003: 'Tn97A7q0XVDq',
    30001: '2023',
    HqlYadRdHJ5c: 'Storage',
    Tn97A7q0XVDq: 40,
    '7ti8XuX4kcY1': '2023',
  },
  {
    10001: 'Sales',
    10002: 20,
    10003: 'Tn97A7q0XVDq',
    30001: '2022',
    HqlYadRdHJ5c: 'Storage',
    Tn97A7q0XVDq: 20,
    '7ti8XuX4kcY1': '2022',
  },
  {
    10001: 'Sales',
    10002: 10,
    10003: 'Tn97A7q0XVDq',
    30001: '2023',
    HqlYadRdHJ5c: 'Furn',
    Tn97A7q0XVDq: 10,
    '7ti8XuX4kcY1': '2023',
  },
  {
    10001: 'Sales',
    10002: 10,
    10003: 'Tn97A7q0XVDq',
    30001: '2022',
    HqlYadRdHJ5c: 'Furn',
    Tn97A7q0XVDq: 10,
    '7ti8XuX4kcY1': '2022',
  },
  {
    10001: 'Sales',
    10002: 50,
    10003: 'Tn97A7q0XVDq',
    30001: '2023',
    HqlYadRdHJ5c: 'Art',
    Tn97A7q0XVDq: 50,
    '7ti8XuX4kcY1': '2023',
  },
  {
    10001: 'Sales',
    10002: 30,
    10003: 'Tn97A7q0XVDq',
    30001: '2022',
    HqlYadRdHJ5c: 'Art',
    Tn97A7q0XVDq: 30,
    '7ti8XuX4kcY1': '2022',
  },
]

export default () => {
  const [themes, setThemes] = useState({})
  const [themeKey, setThemeKey] = useState('')
  useEffect(() => {
    Builder.updateSpec('radar', (spec) => ({
      ...spec,
      point: { ...spec.point, style: { ...(spec.point && spec.point.style), lineWidth: 0, stroke: null } },
      axes: [
        {
          orient: 'radius',
          tick: { forceTickCount: 3 },
          grid: { smooth: false, style: { stroke: 'rgba(255,255,255,0.15)', lineWidth: 1 } },
          label: { visible: false, style: { fontFamily: 'D-DIN' } },
        },
        {
          orient: 'angle',
          sampling: false,
          tick: { visible: true, style: () => ({ visible: false }) },
          domainLine: { style: { visible: false } },
          grid: { style: () => ({ stroke: 'rgba(255,255,255,0.15)', lineDash: [2, 1], lineWidth: 1 }) },
          label: { style: { fontFamily: 'D-DIN', fill: 'rgba(255,255,255,0.65)', fontSize: 12 }, visible: true },
        },
      ],
    }))
    fetch('https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/theme.json')
      .then((r) => r.json())
      .then((remote) => {
        const registry = {}
        Object.entries(remote).forEach(([key, value]) => {
          const colors = value?.colors || []
          registry[key] = {
            baseTheme: 'dark',
            colorScheme: colors.length >= 2 ? colors : [colors[0] || '#00D5FF', '#227BFF'],
            linearColorScheme: [colors[0] || '#003B73', colors[1] || '#00D5FF'],
            textPrimary: '#EAF7FF',
            textSecondary: '#8FC7FF',
            borderColor: 'rgba(82, 191, 255, 0.28)',
            surfaceColor: '#071A33',
            tooltipBackgroundColor: 'rgba(5, 18, 38, 0.92)',
            axisGridColor: 'rgba(82, 191, 255, 0.18)',
            accentColor: colors[0] || '#00D5FF',
          }
        })
        registerTokenThemes(registry, { ensureRegisterAll: false })
        setThemes(remote)
        setThemeKey(Object.keys(remote)[0] || '')
      })
      .catch(() => {})
  }, [])
  if (!themeKey) return null
  const vseedConfig = {
    theme: themeKey,
    chartType: 'radar',
    dataset: DATASET,
    dimensions: [{ id: 'HqlYadRdHJ5c', alias: 'Category' }],
    measures: [{ id: 'Tn97A7q0XVDq', alias: 'Sales' }],
    label: { enable: false },
    animation: {
      enable: true,
      params: {
        appear: { enable: true, effects: ['radial'], ease: 'linear', duration: 1 },
        loop: { enable: true, interval: 2, atmosphere: { effect: 'ripple', ease: 'linear' } },
      },
    },
  }
  return (
    <div style={{ padding: 16, background: '#061A33' }}>
      <select
        value={themeKey}
        onChange={(e) => setThemeKey(e.target.value)}
        style={{
          marginBottom: 12,
          color: '#EAF7FF',
          background: 'rgba(0, 213, 255, 0.18)',
          border: '1px solid rgba(0, 213, 255, 0.45)',
          padding: '6px 12px',
        }}
      >
        {Object.entries(themes).map(([key, value]) => (
          <option key={key} value={key} style={{ color: '#000' }}>
            {value?.name || key}
          </option>
        ))}
      </select>
      <VSeedRender vseed={vseedConfig} />
    </div>
  )
}

2.2 none (boucle) + atmosphere (reveal)

import { useEffect, useState } from 'react'
import { VSeedRender } from '@components'
import { registerTokenThemes, Builder } from '@visactor/vseed'

const DATASET = [
  {
    10001: 'Sales',
    10002: 30,
    10003: 'Tn97A7q0XVDq',
    30001: '2023',
    HqlYadRdHJ5c: 'Labels',
    Tn97A7q0XVDq: 30,
    '7ti8XuX4kcY1': '2023',
  },
  {
    10001: 'Sales',
    10002: 24,
    10003: 'Tn97A7q0XVDq',
    30001: '2022',
    HqlYadRdHJ5c: 'Labels',
    Tn97A7q0XVDq: 24,
    '7ti8XuX4kcY1': '2022',
  },
  {
    10001: 'Sales',
    10002: 20,
    10003: 'Tn97A7q0XVDq',
    30001: '2023',
    HqlYadRdHJ5c: 'Tables',
    Tn97A7q0XVDq: 20,
    '7ti8XuX4kcY1': '2023',
  },
  {
    10001: 'Sales',
    10002: 10,
    10003: 'Tn97A7q0XVDq',
    30001: '2022',
    HqlYadRdHJ5c: 'Tables',
    Tn97A7q0XVDq: 10,
    '7ti8XuX4kcY1': '2022',
  },
  {
    10001: 'Sales',
    10002: 40,
    10003: 'Tn97A7q0XVDq',
    30001: '2023',
    HqlYadRdHJ5c: 'Storage',
    Tn97A7q0XVDq: 40,
    '7ti8XuX4kcY1': '2023',
  },
  {
    10001: 'Sales',
    10002: 20,
    10003: 'Tn97A7q0XVDq',
    30001: '2022',
    HqlYadRdHJ5c: 'Storage',
    Tn97A7q0XVDq: 20,
    '7ti8XuX4kcY1': '2022',
  },
  {
    10001: 'Sales',
    10002: 10,
    10003: 'Tn97A7q0XVDq',
    30001: '2023',
    HqlYadRdHJ5c: 'Furn',
    Tn97A7q0XVDq: 10,
    '7ti8XuX4kcY1': '2023',
  },
  {
    10001: 'Sales',
    10002: 10,
    10003: 'Tn97A7q0XVDq',
    30001: '2022',
    HqlYadRdHJ5c: 'Furn',
    Tn97A7q0XVDq: 10,
    '7ti8XuX4kcY1': '2022',
  },
  {
    10001: 'Sales',
    10002: 50,
    10003: 'Tn97A7q0XVDq',
    30001: '2023',
    HqlYadRdHJ5c: 'Art',
    Tn97A7q0XVDq: 50,
    '7ti8XuX4kcY1': '2023',
  },
  {
    10001: 'Sales',
    10002: 30,
    10003: 'Tn97A7q0XVDq',
    30001: '2022',
    HqlYadRdHJ5c: 'Art',
    Tn97A7q0XVDq: 30,
    '7ti8XuX4kcY1': '2022',
  },
]

export default () => {
  const [themes, setThemes] = useState({})
  const [themeKey, setThemeKey] = useState('')
  useEffect(() => {
    Builder.updateSpec('radar', (spec) => ({
      ...spec,
      point: { ...spec.point, style: { ...(spec.point && spec.point.style), lineWidth: 0, stroke: null } },
      axes: [
        {
          orient: 'radius',
          tick: { forceTickCount: 3 },
          grid: { smooth: false, style: { stroke: 'rgba(255,255,255,0.15)', lineWidth: 1 } },
          label: { visible: false, style: { fontFamily: 'D-DIN' } },
        },
        {
          orient: 'angle',
          sampling: false,
          tick: { visible: true, style: () => ({ visible: false }) },
          domainLine: { style: { visible: false } },
          grid: { style: () => ({ stroke: 'rgba(255,255,255,0.15)', lineDash: [2, 1], lineWidth: 1 }) },
          label: { style: { fontFamily: 'D-DIN', fill: 'rgba(255,255,255,0.65)', fontSize: 12 }, visible: true },
        },
      ],
    }))
    fetch('https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/theme.json')
      .then((r) => r.json())
      .then((remote) => {
        const registry = {}
        Object.entries(remote).forEach(([key, value]) => {
          const colors = value?.colors || []
          registry[key] = {
            baseTheme: 'dark',
            colorScheme: colors.length >= 2 ? colors : [colors[0] || '#00D5FF', '#227BFF'],
            linearColorScheme: [colors[0] || '#003B73', colors[1] || '#00D5FF'],
            textPrimary: '#EAF7FF',
            textSecondary: '#8FC7FF',
            borderColor: 'rgba(82, 191, 255, 0.28)',
            surfaceColor: '#071A33',
            tooltipBackgroundColor: 'rgba(5, 18, 38, 0.92)',
            axisGridColor: 'rgba(82, 191, 255, 0.18)',
            accentColor: colors[0] || '#00D5FF',
          }
        })
        registerTokenThemes(registry, { ensureRegisterAll: false })
        setThemes(remote)
        setThemeKey(Object.keys(remote)[0] || '')
      })
      .catch(() => {})
  }, [])
  if (!themeKey) return null
  const vseedConfig = {
    theme: themeKey,
    chartType: 'radar',
    dataset: DATASET,
    dimensions: [{ id: 'HqlYadRdHJ5c', alias: 'Category' }],
    measures: [{ id: 'Tn97A7q0XVDq', alias: 'Sales' }],
    label: { enable: false },
    animation: {
      enable: true,
      params: {
        appear: { enable: true, effects: ['scale'], ease: 'linear', duration: 1 },
        loop: { enable: true, interval: 3, atmosphere: { effect: 'reveal', ease: 'linear' } },
      },
    },
  }
  return (
    <div style={{ padding: 16, background: '#061A33' }}>
      <select
        value={themeKey}
        onChange={(e) => setThemeKey(e.target.value)}
        style={{
          marginBottom: 12,
          color: '#EAF7FF',
          background: 'rgba(0, 213, 255, 0.18)',
          border: '1px solid rgba(0, 213, 255, 0.45)',
          padding: '6px 12px',
        }}
      >
        {Object.entries(themes).map(([key, value]) => (
          <option key={key} value={key} style={{ color: '#000' }}>
            {value?.name || key}
          </option>
        ))}
      </select>
      <VSeedRender vseed={vseedConfig} />
    </div>
  )
}

2.3 none (boucle) + atmosphere (breath)

import { useEffect, useState } from 'react'
import { VSeedRender } from '@components'
import { registerTokenThemes, Builder } from '@visactor/vseed'

const DATASET = [
  {
    10001: 'Sales',
    10002: 30,
    10003: 'Tn97A7q0XVDq',
    30001: '2023',
    HqlYadRdHJ5c: 'Labels',
    Tn97A7q0XVDq: 30,
    '7ti8XuX4kcY1': '2023',
  },
  {
    10001: 'Sales',
    10002: 24,
    10003: 'Tn97A7q0XVDq',
    30001: '2022',
    HqlYadRdHJ5c: 'Labels',
    Tn97A7q0XVDq: 24,
    '7ti8XuX4kcY1': '2022',
  },
  {
    10001: 'Sales',
    10002: 20,
    10003: 'Tn97A7q0XVDq',
    30001: '2023',
    HqlYadRdHJ5c: 'Tables',
    Tn97A7q0XVDq: 20,
    '7ti8XuX4kcY1': '2023',
  },
  {
    10001: 'Sales',
    10002: 10,
    10003: 'Tn97A7q0XVDq',
    30001: '2022',
    HqlYadRdHJ5c: 'Tables',
    Tn97A7q0XVDq: 10,
    '7ti8XuX4kcY1': '2022',
  },
  {
    10001: 'Sales',
    10002: 40,
    10003: 'Tn97A7q0XVDq',
    30001: '2023',
    HqlYadRdHJ5c: 'Storage',
    Tn97A7q0XVDq: 40,
    '7ti8XuX4kcY1': '2023',
  },
  {
    10001: 'Sales',
    10002: 20,
    10003: 'Tn97A7q0XVDq',
    30001: '2022',
    HqlYadRdHJ5c: 'Storage',
    Tn97A7q0XVDq: 20,
    '7ti8XuX4kcY1': '2022',
  },
  {
    10001: 'Sales',
    10002: 10,
    10003: 'Tn97A7q0XVDq',
    30001: '2023',
    HqlYadRdHJ5c: 'Furn',
    Tn97A7q0XVDq: 10,
    '7ti8XuX4kcY1': '2023',
  },
  {
    10001: 'Sales',
    10002: 10,
    10003: 'Tn97A7q0XVDq',
    30001: '2022',
    HqlYadRdHJ5c: 'Furn',
    Tn97A7q0XVDq: 10,
    '7ti8XuX4kcY1': '2022',
  },
  {
    10001: 'Sales',
    10002: 50,
    10003: 'Tn97A7q0XVDq',
    30001: '2023',
    HqlYadRdHJ5c: 'Art',
    Tn97A7q0XVDq: 50,
    '7ti8XuX4kcY1': '2023',
  },
  {
    10001: 'Sales',
    10002: 30,
    10003: 'Tn97A7q0XVDq',
    30001: '2022',
    HqlYadRdHJ5c: 'Art',
    Tn97A7q0XVDq: 30,
    '7ti8XuX4kcY1': '2022',
  },
]

export default () => {
  const [themes, setThemes] = useState({})
  const [themeKey, setThemeKey] = useState('')
  useEffect(() => {
    Builder.updateSpec('radar', (spec) => ({
      ...spec,
      point: { ...spec.point, style: { ...(spec.point && spec.point.style), lineWidth: 0, stroke: null } },
      axes: [
        {
          orient: 'radius',
          tick: { forceTickCount: 3 },
          grid: { smooth: false, style: { stroke: 'rgba(255,255,255,0.15)', lineWidth: 1 } },
          label: { visible: false, style: { fontFamily: 'D-DIN' } },
        },
        {
          orient: 'angle',
          sampling: false,
          tick: { visible: true, style: () => ({ visible: false }) },
          domainLine: { style: { visible: false } },
          grid: { style: () => ({ stroke: 'rgba(255,255,255,0.15)', lineDash: [2, 1], lineWidth: 1 }) },
          label: { style: { fontFamily: 'D-DIN', fill: 'rgba(255,255,255,0.65)', fontSize: 12 }, visible: true },
        },
      ],
    }))
    fetch('https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/theme.json')
      .then((r) => r.json())
      .then((remote) => {
        const registry = {}
        Object.entries(remote).forEach(([key, value]) => {
          const colors = value?.colors || []
          registry[key] = {
            baseTheme: 'dark',
            colorScheme: colors.length >= 2 ? colors : [colors[0] || '#00D5FF', '#227BFF'],
            linearColorScheme: [colors[0] || '#003B73', colors[1] || '#00D5FF'],
            textPrimary: '#EAF7FF',
            textSecondary: '#8FC7FF',
            borderColor: 'rgba(82, 191, 255, 0.28)',
            surfaceColor: '#071A33',
            tooltipBackgroundColor: 'rgba(5, 18, 38, 0.92)',
            axisGridColor: 'rgba(82, 191, 255, 0.18)',
            accentColor: colors[0] || '#00D5FF',
          }
        })
        registerTokenThemes(registry, { ensureRegisterAll: false })
        setThemes(remote)
        setThemeKey(Object.keys(remote)[0] || '')
      })
      .catch(() => {})
  }, [])
  if (!themeKey) return null
  const vseedConfig = {
    theme: themeKey,
    chartType: 'radar',
    dataset: DATASET,
    dimensions: [{ id: 'HqlYadRdHJ5c', alias: 'Category' }],
    measures: [{ id: 'Tn97A7q0XVDq', alias: 'Sales' }],
    label: { enable: false },
    animation: {
      enable: true,
      params: {
        appear: { enable: true, effects: ['scale'], ease: 'linear', duration: 1 },
        loop: { enable: true, interval: 2, atmosphere: { effect: 'breath', ease: 'linear' } },
      },
    },
  }
  return (
    <div style={{ padding: 16, background: '#061A33' }}>
      <select
        value={themeKey}
        onChange={(e) => setThemeKey(e.target.value)}
        style={{
          marginBottom: 12,
          color: '#EAF7FF',
          background: 'rgba(0, 213, 255, 0.18)',
          border: '1px solid rgba(0, 213, 255, 0.45)',
          padding: '6px 12px',
        }}
      >
        {Object.entries(themes).map(([key, value]) => (
          <option key={key} value={key} style={{ color: '#000' }}>
            {value?.name || key}
          </option>
        ))}
      </select>
      <VSeedRender vseed={vseedConfig} />
    </div>
  )
}

Animation des nuages de points

Tous les exemples reposent sur le thème grand écran (sombre) et proposent une liste déroulante pour changer de thème. Les thèmes sont chargés depuis : https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/theme.json

1. Animation d'apparition

L'animation d'apparition contrôle la première apparition des points.

1.1 scale (mise à l'échelle)

import { useEffect, useState } from 'react'
import { VSeedRender } from '@components'
import { registerTokenThemes } from '@visactor/vseed'

const THEME_URL = 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/theme.json'
const DATASET = [
  {
    10001: 'Sales',
    10002: 30,
    10003: 'Tn97A7q0XVDq',
    30001: '2023',
    HqlYadRdHJ5c: 'Labels',
    Tn97A7q0XVDq: 30,
    '7ti8XuX4kcY1': '2023',
  },
  {
    10001: 'Sales',
    10002: 24,
    10003: 'Tn97A7q0XVDq',
    30001: '2022',
    HqlYadRdHJ5c: 'Labels',
    Tn97A7q0XVDq: 24,
    '7ti8XuX4kcY1': '2022',
  },
  {
    10001: 'Sales',
    10002: 20,
    10003: 'Tn97A7q0XVDq',
    30001: '2023',
    HqlYadRdHJ5c: 'Tables',
    Tn97A7q0XVDq: 20,
    '7ti8XuX4kcY1': '2023',
  },
  {
    10001: 'Sales',
    10002: 10,
    10003: 'Tn97A7q0XVDq',
    30001: '2022',
    HqlYadRdHJ5c: 'Tables',
    Tn97A7q0XVDq: 10,
    '7ti8XuX4kcY1': '2022',
  },
  {
    10001: 'Sales',
    10002: 40,
    10003: 'Tn97A7q0XVDq',
    30001: '2023',
    HqlYadRdHJ5c: 'Storage',
    Tn97A7q0XVDq: 40,
    '7ti8XuX4kcY1': '2023',
  },
  {
    10001: 'Sales',
    10002: 20,
    10003: 'Tn97A7q0XVDq',
    30001: '2022',
    HqlYadRdHJ5c: 'Storage',
    Tn97A7q0XVDq: 20,
    '7ti8XuX4kcY1': '2022',
  },
  {
    10001: 'Sales',
    10002: 10,
    10003: 'Tn97A7q0XVDq',
    30001: '2023',
    HqlYadRdHJ5c: 'Furn',
    Tn97A7q0XVDq: 10,
    '7ti8XuX4kcY1': '2023',
  },
  {
    10001: 'Sales',
    10002: 10,
    10003: 'Tn97A7q0XVDq',
    30001: '2022',
    HqlYadRdHJ5c: 'Furn',
    Tn97A7q0XVDq: 10,
    '7ti8XuX4kcY1': '2022',
  },
  {
    10001: 'Sales',
    10002: 50,
    10003: 'Tn97A7q0XVDq',
    30001: '2023',
    HqlYadRdHJ5c: 'Art',
    Tn97A7q0XVDq: 50,
    '7ti8XuX4kcY1': '2023',
  },
  {
    10001: 'Sales',
    10002: 30,
    10003: 'Tn97A7q0XVDq',
    30001: '2022',
    HqlYadRdHJ5c: 'Art',
    Tn97A7q0XVDq: 30,
    '7ti8XuX4kcY1': '2022',
  },
]

const DIMENSIONS = [{ id: 'HqlYadRdHJ5c', alias: 'Category' }]
const MEASURES = [{ id: 'Tn97A7q0XVDq', alias: 'Sales' }]

const useLargeScreenThemes = () => {
  const [themes, setThemes] = useState({})
  const [themeKey, setThemeKey] = useState('')
  useEffect(() => {
    fetch(THEME_URL)
      .then((r) => r.json())
      .then((remote) => {
        const registry = {}
        Object.entries(remote).forEach(([key, value]) => {
          const colors = value?.colors || []
          registry[key] = {
            baseTheme: 'dark',
            colorScheme: colors.length >= 2 ? colors : [colors[0] || '#00D5FF', '#227BFF'],
            linearColorScheme: [colors[0] || '#003B73', colors[1] || '#00D5FF'],
            textPrimary: '#EAF7FF',
            textSecondary: '#8FC7FF',
            borderColor: 'rgba(82, 191, 255, 0.28)',
            surfaceColor: '#071A33',
            tooltipBackgroundColor: 'rgba(5, 18, 38, 0.92)',
            axisGridColor: 'rgba(82, 191, 255, 0.18)',
            accentColor: colors[0] || '#00D5FF',
          }
        })
        registerTokenThemes(registry, { ensureRegisterAll: false })
        setThemes(remote)
        setThemeKey(Object.keys(remote)[0] || '')
      })
      .catch(() => {})
  }, [])
  return { themes, themeKey, setThemeKey }
}

const ThemeSelect = ({ themes, themeKey, onChange }) => (
  <select
    value={themeKey}
    onChange={(e) => onChange(e.target.value)}
    style={{
      marginBottom: 12,
      color: '#EAF7FF',
      background: 'rgba(0, 213, 255, 0.18)',
      border: '1px solid rgba(0, 213, 255, 0.45)',
      padding: '6px 12px',
    }}
  >
    {Object.entries(themes).map(([key, value]) => (
      <option key={key} value={key} style={{ color: '#000' }}>
        {value?.name || key}
      </option>
    ))}
  </select>
)

export default () => {
  const { themes, themeKey, setThemeKey } = useLargeScreenThemes()
  if (!themeKey) return null
  const vseedConfig = {
    theme: themeKey,
    chartType: 'scatter',
    dataset: DATASET,
    dimensions: DIMENSIONS,
    measures: MEASURES,
    label: { enable: false },
    animation: {
      enable: true,
      params: { appear: { enable: true, effects: ['scale'], ease: 'circInOut', duration: 1 } },
    },
  }
  return (
    <div style={{ padding: 16, background: '#061A33' }}>
      <ThemeSelect themes={themes} themeKey={themeKey} onChange={setThemeKey} />
      <VSeedRender vseed={vseedConfig} />
    </div>
  )
}

1.2 fadeIn (fondu entrant)

import { useEffect, useState } from 'react'
import { VSeedRender } from '@components'
import { registerTokenThemes } from '@visactor/vseed'

const DATASET = [
  {
    10001: 'Sales',
    10002: 30,
    10003: 'Tn97A7q0XVDq',
    30001: '2023',
    HqlYadRdHJ5c: 'Labels',
    Tn97A7q0XVDq: 30,
    '7ti8XuX4kcY1': '2023',
  },
  {
    10001: 'Sales',
    10002: 24,
    10003: 'Tn97A7q0XVDq',
    30001: '2022',
    HqlYadRdHJ5c: 'Labels',
    Tn97A7q0XVDq: 24,
    '7ti8XuX4kcY1': '2022',
  },
  {
    10001: 'Sales',
    10002: 20,
    10003: 'Tn97A7q0XVDq',
    30001: '2023',
    HqlYadRdHJ5c: 'Tables',
    Tn97A7q0XVDq: 20,
    '7ti8XuX4kcY1': '2023',
  },
  {
    10001: 'Sales',
    10002: 10,
    10003: 'Tn97A7q0XVDq',
    30001: '2022',
    HqlYadRdHJ5c: 'Tables',
    Tn97A7q0XVDq: 10,
    '7ti8XuX4kcY1': '2022',
  },
  {
    10001: 'Sales',
    10002: 40,
    10003: 'Tn97A7q0XVDq',
    30001: '2023',
    HqlYadRdHJ5c: 'Storage',
    Tn97A7q0XVDq: 40,
    '7ti8XuX4kcY1': '2023',
  },
  {
    10001: 'Sales',
    10002: 20,
    10003: 'Tn97A7q0XVDq',
    30001: '2022',
    HqlYadRdHJ5c: 'Storage',
    Tn97A7q0XVDq: 20,
    '7ti8XuX4kcY1': '2022',
  },
  {
    10001: 'Sales',
    10002: 10,
    10003: 'Tn97A7q0XVDq',
    30001: '2023',
    HqlYadRdHJ5c: 'Furn',
    Tn97A7q0XVDq: 10,
    '7ti8XuX4kcY1': '2023',
  },
  {
    10001: 'Sales',
    10002: 10,
    10003: 'Tn97A7q0XVDq',
    30001: '2022',
    HqlYadRdHJ5c: 'Furn',
    Tn97A7q0XVDq: 10,
    '7ti8XuX4kcY1': '2022',
  },
  {
    10001: 'Sales',
    10002: 50,
    10003: 'Tn97A7q0XVDq',
    30001: '2023',
    HqlYadRdHJ5c: 'Art',
    Tn97A7q0XVDq: 50,
    '7ti8XuX4kcY1': '2023',
  },
  {
    10001: 'Sales',
    10002: 30,
    10003: 'Tn97A7q0XVDq',
    30001: '2022',
    HqlYadRdHJ5c: 'Art',
    Tn97A7q0XVDq: 30,
    '7ti8XuX4kcY1': '2022',
  },
]

export default () => {
  const [themes, setThemes] = useState({})
  const [themeKey, setThemeKey] = useState('')
  useEffect(() => {
    fetch('https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/theme.json')
      .then((r) => r.json())
      .then((remote) => {
        const registry = {}
        Object.entries(remote).forEach(([key, value]) => {
          const colors = value?.colors || []
          registry[key] = {
            baseTheme: 'dark',
            colorScheme: colors.length >= 2 ? colors : [colors[0] || '#00D5FF', '#227BFF'],
            linearColorScheme: [colors[0] || '#003B73', colors[1] || '#00D5FF'],
            textPrimary: '#EAF7FF',
            textSecondary: '#8FC7FF',
            borderColor: 'rgba(82, 191, 255, 0.28)',
            surfaceColor: '#071A33',
            tooltipBackgroundColor: 'rgba(5, 18, 38, 0.92)',
            axisGridColor: 'rgba(82, 191, 255, 0.18)',
            accentColor: colors[0] || '#00D5FF',
          }
        })
        registerTokenThemes(registry, { ensureRegisterAll: false })
        setThemes(remote)
        setThemeKey(Object.keys(remote)[0] || '')
      })
      .catch(() => {})
  }, [])
  if (!themeKey) return null
  const vseedConfig = {
    theme: themeKey,
    chartType: 'scatter',
    dataset: DATASET,
    dimensions: [{ id: 'HqlYadRdHJ5c', alias: 'Category' }],
    measures: [{ id: 'Tn97A7q0XVDq', alias: 'Sales' }],
    label: { enable: false },
    animation: {
      enable: true,
      params: { appear: { enable: true, effects: ['fadeIn'], ease: 'circInOut', duration: 1 } },
    },
  }
  return (
    <div style={{ padding: 16, background: '#061A33' }}>
      <select
        value={themeKey}
        onChange={(e) => setThemeKey(e.target.value)}
        style={{
          marginBottom: 12,
          color: '#EAF7FF',
          background: 'rgba(0, 213, 255, 0.18)',
          border: '1px solid rgba(0, 213, 255, 0.45)',
          padding: '6px 12px',
        }}
      >
        {Object.entries(themes).map(([key, value]) => (
          <option key={key} value={key} style={{ color: '#000' }}>
            {value?.name || key}
          </option>
        ))}
      </select>
      <VSeedRender vseed={vseedConfig} />
    </div>
  )
}

2. Animation en boucle

L'animation en boucle des nuages de points s'exprime généralement avec atmosphere pour maintenir un rythme continu.

2.1 none (boucle) + atmosphere (ripple)

import { useEffect, useState } from 'react'
import { VSeedRender } from '@components'
import { registerTokenThemes } from '@visactor/vseed'

const DATASET = [
  {
    10001: 'Sales',
    10002: 30,
    10003: 'Tn97A7q0XVDq',
    30001: '2023',
    HqlYadRdHJ5c: 'Labels',
    Tn97A7q0XVDq: 30,
    '7ti8XuX4kcY1': '2023',
  },
  {
    10001: 'Sales',
    10002: 24,
    10003: 'Tn97A7q0XVDq',
    30001: '2022',
    HqlYadRdHJ5c: 'Labels',
    Tn97A7q0XVDq: 24,
    '7ti8XuX4kcY1': '2022',
  },
  {
    10001: 'Sales',
    10002: 20,
    10003: 'Tn97A7q0XVDq',
    30001: '2023',
    HqlYadRdHJ5c: 'Tables',
    Tn97A7q0XVDq: 20,
    '7ti8XuX4kcY1': '2023',
  },
  {
    10001: 'Sales',
    10002: 10,
    10003: 'Tn97A7q0XVDq',
    30001: '2022',
    HqlYadRdHJ5c: 'Tables',
    Tn97A7q0XVDq: 10,
    '7ti8XuX4kcY1': '2022',
  },
  {
    10001: 'Sales',
    10002: 40,
    10003: 'Tn97A7q0XVDq',
    30001: '2023',
    HqlYadRdHJ5c: 'Storage',
    Tn97A7q0XVDq: 40,
    '7ti8XuX4kcY1': '2023',
  },
  {
    10001: 'Sales',
    10002: 20,
    10003: 'Tn97A7q0XVDq',
    30001: '2022',
    HqlYadRdHJ5c: 'Storage',
    Tn97A7q0XVDq: 20,
    '7ti8XuX4kcY1': '2022',
  },
  {
    10001: 'Sales',
    10002: 10,
    10003: 'Tn97A7q0XVDq',
    30001: '2023',
    HqlYadRdHJ5c: 'Furn',
    Tn97A7q0XVDq: 10,
    '7ti8XuX4kcY1': '2023',
  },
  {
    10001: 'Sales',
    10002: 10,
    10003: 'Tn97A7q0XVDq',
    30001: '2022',
    HqlYadRdHJ5c: 'Furn',
    Tn97A7q0XVDq: 10,
    '7ti8XuX4kcY1': '2022',
  },
  {
    10001: 'Sales',
    10002: 50,
    10003: 'Tn97A7q0XVDq',
    30001: '2023',
    HqlYadRdHJ5c: 'Art',
    Tn97A7q0XVDq: 50,
    '7ti8XuX4kcY1': '2023',
  },
  {
    10001: 'Sales',
    10002: 30,
    10003: 'Tn97A7q0XVDq',
    30001: '2022',
    HqlYadRdHJ5c: 'Art',
    Tn97A7q0XVDq: 30,
    '7ti8XuX4kcY1': '2022',
  },
]

export default () => {
  const [themes, setThemes] = useState({})
  const [themeKey, setThemeKey] = useState('')
  useEffect(() => {
    fetch('https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/theme.json')
      .then((r) => r.json())
      .then((remote) => {
        const registry = {}
        Object.entries(remote).forEach(([key, value]) => {
          const colors = value?.colors || []
          registry[key] = {
            baseTheme: 'dark',
            colorScheme: colors.length >= 2 ? colors : [colors[0] || '#00D5FF', '#227BFF'],
            linearColorScheme: [colors[0] || '#003B73', colors[1] || '#00D5FF'],
            textPrimary: '#EAF7FF',
            textSecondary: '#8FC7FF',
            borderColor: 'rgba(82, 191, 255, 0.28)',
            surfaceColor: '#071A33',
            tooltipBackgroundColor: 'rgba(5, 18, 38, 0.92)',
            axisGridColor: 'rgba(82, 191, 255, 0.18)',
            accentColor: colors[0] || '#00D5FF',
          }
        })
        registerTokenThemes(registry, { ensureRegisterAll: false })
        setThemes(remote)
        setThemeKey(Object.keys(remote)[0] || '')
      })
      .catch(() => {})
  }, [])
  if (!themeKey) return null
  const vseedConfig = {
    theme: themeKey,
    chartType: 'scatter',
    dataset: DATASET,
    dimensions: [{ id: 'HqlYadRdHJ5c', alias: 'Category' }],
    measures: [{ id: 'Tn97A7q0XVDq', alias: 'Sales' }],
    label: { enable: false },
    animation: {
      enable: true,
      params: {
        appear: { enable: true, effects: ['scale'], ease: 'linear', duration: 1 },
        loop: { enable: true, interval: 2, atmosphere: { effect: 'ripple', ease: 'linear' } },
      },
    },
  }
  return (
    <div style={{ padding: 16, background: '#061A33' }}>
      <select
        value={themeKey}
        onChange={(e) => setThemeKey(e.target.value)}
        style={{
          marginBottom: 12,
          color: '#EAF7FF',
          background: 'rgba(0, 213, 255, 0.18)',
          border: '1px solid rgba(0, 213, 255, 0.45)',
          padding: '6px 12px',
        }}
      >
        {Object.entries(themes).map(([key, value]) => (
          <option key={key} value={key} style={{ color: '#000' }}>
            {value?.name || key}
          </option>
        ))}
      </select>
      <VSeedRender vseed={vseedConfig} />
    </div>
  )
}

2.2 none (boucle) + atmosphere (breath)

import { useEffect, useState } from 'react'
import { VSeedRender } from '@components'
import { registerTokenThemes } from '@visactor/vseed'

const DATASET = [
  {
    10001: 'Sales',
    10002: 30,
    10003: 'Tn97A7q0XVDq',
    30001: '2023',
    HqlYadRdHJ5c: 'Labels',
    Tn97A7q0XVDq: 30,
    '7ti8XuX4kcY1': '2023',
  },
  {
    10001: 'Sales',
    10002: 24,
    10003: 'Tn97A7q0XVDq',
    30001: '2022',
    HqlYadRdHJ5c: 'Labels',
    Tn97A7q0XVDq: 24,
    '7ti8XuX4kcY1': '2022',
  },
  {
    10001: 'Sales',
    10002: 20,
    10003: 'Tn97A7q0XVDq',
    30001: '2023',
    HqlYadRdHJ5c: 'Tables',
    Tn97A7q0XVDq: 20,
    '7ti8XuX4kcY1': '2023',
  },
  {
    10001: 'Sales',
    10002: 10,
    10003: 'Tn97A7q0XVDq',
    30001: '2022',
    HqlYadRdHJ5c: 'Tables',
    Tn97A7q0XVDq: 10,
    '7ti8XuX4kcY1': '2022',
  },
  {
    10001: 'Sales',
    10002: 40,
    10003: 'Tn97A7q0XVDq',
    30001: '2023',
    HqlYadRdHJ5c: 'Storage',
    Tn97A7q0XVDq: 40,
    '7ti8XuX4kcY1': '2023',
  },
  {
    10001: 'Sales',
    10002: 20,
    10003: 'Tn97A7q0XVDq',
    30001: '2022',
    HqlYadRdHJ5c: 'Storage',
    Tn97A7q0XVDq: 20,
    '7ti8XuX4kcY1': '2022',
  },
  {
    10001: 'Sales',
    10002: 10,
    10003: 'Tn97A7q0XVDq',
    30001: '2023',
    HqlYadRdHJ5c: 'Furn',
    Tn97A7q0XVDq: 10,
    '7ti8XuX4kcY1': '2023',
  },
  {
    10001: 'Sales',
    10002: 10,
    10003: 'Tn97A7q0XVDq',
    30001: '2022',
    HqlYadRdHJ5c: 'Furn',
    Tn97A7q0XVDq: 10,
    '7ti8XuX4kcY1': '2022',
  },
  {
    10001: 'Sales',
    10002: 50,
    10003: 'Tn97A7q0XVDq',
    30001: '2023',
    HqlYadRdHJ5c: 'Art',
    Tn97A7q0XVDq: 50,
    '7ti8XuX4kcY1': '2023',
  },
  {
    10001: 'Sales',
    10002: 30,
    10003: 'Tn97A7q0XVDq',
    30001: '2022',
    HqlYadRdHJ5c: 'Art',
    Tn97A7q0XVDq: 30,
    '7ti8XuX4kcY1': '2022',
  },
]

export default () => {
  const [themes, setThemes] = useState({})
  const [themeKey, setThemeKey] = useState('')
  useEffect(() => {
    fetch('https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/theme.json')
      .then((r) => r.json())
      .then((remote) => {
        const registry = {}
        Object.entries(remote).forEach(([key, value]) => {
          const colors = value?.colors || []
          registry[key] = {
            baseTheme: 'dark',
            colorScheme: colors.length >= 2 ? colors : [colors[0] || '#00D5FF', '#227BFF'],
            linearColorScheme: [colors[0] || '#003B73', colors[1] || '#00D5FF'],
            textPrimary: '#EAF7FF',
            textSecondary: '#8FC7FF',
            borderColor: 'rgba(82, 191, 255, 0.28)',
            surfaceColor: '#071A33',
            tooltipBackgroundColor: 'rgba(5, 18, 38, 0.92)',
            axisGridColor: 'rgba(82, 191, 255, 0.18)',
            accentColor: colors[0] || '#00D5FF',
          }
        })
        registerTokenThemes(registry, { ensureRegisterAll: false })
        setThemes(remote)
        setThemeKey(Object.keys(remote)[0] || '')
      })
      .catch(() => {})
  }, [])
  if (!themeKey) return null
  const vseedConfig = {
    theme: themeKey,
    chartType: 'scatter',
    dataset: DATASET,
    dimensions: [{ id: 'HqlYadRdHJ5c', alias: 'Category' }],
    measures: [{ id: 'Tn97A7q0XVDq', alias: 'Sales' }],
    label: { enable: false },
    animation: {
      enable: true,
      params: {
        appear: { enable: true, effects: ['fadeIn'], ease: 'linear', duration: 1 },
        loop: { enable: true, interval: 2, atmosphere: { effect: 'breath', ease: 'linear' } },
      },
    },
  }
  return (
    <div style={{ padding: 16, background: '#061A33' }}>
      <select
        value={themeKey}
        onChange={(e) => setThemeKey(e.target.value)}
        style={{
          marginBottom: 12,
          color: '#EAF7FF',
          background: 'rgba(0, 213, 255, 0.18)',
          border: '1px solid rgba(0, 213, 255, 0.45)',
          padding: '6px 12px',
        }}
      >
        {Object.entries(themes).map(([key, value]) => (
          <option key={key} value={key} style={{ color: '#000' }}>
            {value?.name || key}
          </option>
        ))}
      </select>
      <VSeedRender vseed={vseedConfig} />
    </div>
  )
}

2.3 none (boucle) + atmosphere (reveal)

import { useEffect, useState } from 'react'
import { VSeedRender } from '@components'
import { registerTokenThemes } from '@visactor/vseed'

const DATASET = [
  {
    10001: 'Sales',
    10002: 30,
    10003: 'Tn97A7q0XVDq',
    30001: '2023',
    HqlYadRdHJ5c: 'Labels',
    Tn97A7q0XVDq: 30,
    '7ti8XuX4kcY1': '2023',
  },
  {
    10001: 'Sales',
    10002: 24,
    10003: 'Tn97A7q0XVDq',
    30001: '2022',
    HqlYadRdHJ5c: 'Labels',
    Tn97A7q0XVDq: 24,
    '7ti8XuX4kcY1': '2022',
  },
  {
    10001: 'Sales',
    10002: 20,
    10003: 'Tn97A7q0XVDq',
    30001: '2023',
    HqlYadRdHJ5c: 'Tables',
    Tn97A7q0XVDq: 20,
    '7ti8XuX4kcY1': '2023',
  },
  {
    10001: 'Sales',
    10002: 10,
    10003: 'Tn97A7q0XVDq',
    30001: '2022',
    HqlYadRdHJ5c: 'Tables',
    Tn97A7q0XVDq: 10,
    '7ti8XuX4kcY1': '2022',
  },
  {
    10001: 'Sales',
    10002: 40,
    10003: 'Tn97A7q0XVDq',
    30001: '2023',
    HqlYadRdHJ5c: 'Storage',
    Tn97A7q0XVDq: 40,
    '7ti8XuX4kcY1': '2023',
  },
  {
    10001: 'Sales',
    10002: 20,
    10003: 'Tn97A7q0XVDq',
    30001: '2022',
    HqlYadRdHJ5c: 'Storage',
    Tn97A7q0XVDq: 20,
    '7ti8XuX4kcY1': '2022',
  },
  {
    10001: 'Sales',
    10002: 10,
    10003: 'Tn97A7q0XVDq',
    30001: '2023',
    HqlYadRdHJ5c: 'Furn',
    Tn97A7q0XVDq: 10,
    '7ti8XuX4kcY1': '2023',
  },
  {
    10001: 'Sales',
    10002: 10,
    10003: 'Tn97A7q0XVDq',
    30001: '2022',
    HqlYadRdHJ5c: 'Furn',
    Tn97A7q0XVDq: 10,
    '7ti8XuX4kcY1': '2022',
  },
  {
    10001: 'Sales',
    10002: 50,
    10003: 'Tn97A7q0XVDq',
    30001: '2023',
    HqlYadRdHJ5c: 'Art',
    Tn97A7q0XVDq: 50,
    '7ti8XuX4kcY1': '2023',
  },
  {
    10001: 'Sales',
    10002: 30,
    10003: 'Tn97A7q0XVDq',
    30001: '2022',
    HqlYadRdHJ5c: 'Art',
    Tn97A7q0XVDq: 30,
    '7ti8XuX4kcY1': '2022',
  },
]

export default () => {
  const [themes, setThemes] = useState({})
  const [themeKey, setThemeKey] = useState('')
  useEffect(() => {
    fetch('https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/theme.json')
      .then((r) => r.json())
      .then((remote) => {
        const registry = {}
        Object.entries(remote).forEach(([key, value]) => {
          const colors = value?.colors || []
          registry[key] = {
            baseTheme: 'dark',
            colorScheme: colors.length >= 2 ? colors : [colors[0] || '#00D5FF', '#227BFF'],
            linearColorScheme: [colors[0] || '#003B73', colors[1] || '#00D5FF'],
            textPrimary: '#EAF7FF',
            textSecondary: '#8FC7FF',
            borderColor: 'rgba(82, 191, 255, 0.28)',
            surfaceColor: '#071A33',
            tooltipBackgroundColor: 'rgba(5, 18, 38, 0.92)',
            axisGridColor: 'rgba(82, 191, 255, 0.18)',
            accentColor: colors[0] || '#00D5FF',
          }
        })
        registerTokenThemes(registry, { ensureRegisterAll: false })
        setThemes(remote)
        setThemeKey(Object.keys(remote)[0] || '')
      })
      .catch(() => {})
  }, [])
  if (!themeKey) return null
  const vseedConfig = {
    theme: themeKey,
    chartType: 'scatter',
    dataset: DATASET,
    dimensions: [{ id: 'HqlYadRdHJ5c', alias: 'Category' }],
    measures: [{ id: 'Tn97A7q0XVDq', alias: 'Sales' }],
    label: { enable: false },
    animation: {
      enable: true,
      params: {
        appear: { enable: true, effects: ['scale'], ease: 'linear', duration: 1 },
        loop: { enable: true, interval: 3, atmosphere: { effect: 'reveal', ease: 'linear' } },
      },
    },
  }
  return (
    <div style={{ padding: 16, background: '#061A33' }}>
      <select
        value={themeKey}
        onChange={(e) => setThemeKey(e.target.value)}
        style={{
          marginBottom: 12,
          color: '#EAF7FF',
          background: 'rgba(0, 213, 255, 0.18)',
          border: '1px solid rgba(0, 213, 255, 0.45)',
          padding: '6px 12px',
        }}
      >
        {Object.entries(themes).map(([key, value]) => (
          <option key={key} value={key} style={{ color: '#000' }}>
            {value?.name || key}
          </option>
        ))}
      </select>
      <VSeedRender vseed={vseedConfig} />
    </div>
  )
}