#Animation
VSeed animation supports appear, loop, ambient, update, and exit stages. This guide combines bar, line/area, pie, radar, and scatter examples in one place so you can compare the animation configuration across chart types.
#Bar / Column Animation
All examples are based on the large-screen (dark) theme and provide a dropdown to switch between different themes.
Themes are loaded from: https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/theme.json
#1. Appear Animation
Appear animation is the motion played when a chart first enters the view.
For bar/column series, the appear effect currently supports growth.
#1.1 growth
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. Loop Animation
Loop animation is the continuous motion after initial rendering, useful for emphasis and dynamic rhythm.
#2.1 highlight (group highlight) + atmo (ambient effect)
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: 3,
loop: { enable: true, effects: ['highLight'], 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.2 growth (loop) + atmo (ambient effect)
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 (loop) + atmo (ambient effect)
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>
)
}#Line / Area Animation
All examples are based on the large-screen (dark) theme and provide a dropdown to switch between different themes.
Themes are loaded from: https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/theme.json
#1. Appear Animation
Appear animation introduces the chart when it first enters the page.
#1.1 load
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
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. Loop Animation
Loop animation keeps the chart in a cyclic motion for long-running display states.
#2.1 load + atmo (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 },
atmo: { 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 + atmo (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 },
atmo: { 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>
)
}#Pie / Donut / Rose Animation
All examples are based on the large-screen (dark) theme and provide a dropdown to switch between different themes.
Themes are loaded from: https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/theme.json
#1. Appear Animation
Appear animation controls how sectors first enter the view.
#1.1 radial
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
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. Loop Animation
Loop animation keeps emphasizing sectors in a periodic way.
#2.1 enlarge
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
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>
)
}#Radar Animation
All examples are based on the large-screen (dark) theme and provide a dropdown to switch between different themes.
Themes are loaded from: https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/theme.json
#1. Appear Animation
Appear animation controls the first entrance of radar polygons and points.
#1.1 radial
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
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. Loop Animation
Radar loop animation is expressed through atmo.effect for continuous ambient motion.
#2.1 none + atmo (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, atmo: { 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 + atmo (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, atmo: { 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 + atmo (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, atmo: { 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>
)
}#Scatter Animation
All examples are based on the large-screen (dark) theme and provide a dropdown to switch between different themes.
Themes are loaded from: https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/theme.json
#1. Appear Animation
Appear animation controls how scatter points first appear.
#1.1 scale
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
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. Loop Animation
Scatter loop animation is typically expressed with atmo for continuous rhythm.
#2.1 none + atmo (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, atmo: { 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 + atmo (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, atmo: { 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 + atmo (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, atmo: { 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>
)
}