资讯动态

TinyBase 响应式 SVG 图表组件实战:基于 ui-react-dom-charts 在 React 中渲染数据图表

发布时间:2026/10/13 3:11:12 来源:尧图企业网站定制
数据库后端前端【免费下载链接】tinybaseA reactive data store sync engine.项目地址https://gitcode.com/gh_mirrors/ti/tinybase点击查看免费下载本文围绕 TinyBase 官方 Chart Components (React) 演示系列展开完整讲解ui-react-dom-charts模块的用法如何把 Store 中的 Table 或 Queries 的 ResultTable 渲染为可直接运行的 SVG 图表如何用纯 CSS 定制图表外观、组合多条序列、处理数值/分类/时间等不同数据形状以及如何通过XAxis/YAxis子组件覆盖坐标轴行为。读完本文你将能基于 TinyBase 的响应式数据模型从零搭建一套可复用、可深度定制、随数据变化自动刷新的 React 图表应用并理解其底层实现原理。该演示系列是一个图表主题画廊覆盖六个渐进式主题基础折线图、CSS 样式定制、多序列图表组合、数据排序与类型、坐标轴覆盖、时间轴。对应的官方演示文档位于 site/demos/12_chart_components_react模块源码位于 src/ui-react-dom-charts完整类型声明在 src/types/ui-react-dom-charts/index.d.ts。快速开始从图表应用模板起步原文档建议直接通过官方 CLI 脚手架启动运行npm create tinybaselatest并在交互式提示中选择Charting app模板当前仓库版本为 9.6.0见 package.json。该模板会为你生成一个包含 importmap 与最小样式的可编辑图表应用后续六个主题都可以在此基础上逐步演化。如果要在现有页面中手工引入则按原文档配置 importmap 与模块导入script typeimportmap { imports: { tinybase: https://esm.sh/tinybase, tinybase/ui-react: https://esm.sh/tinybase/ui-react, tinybase/ui-react-dom-charts: https://esm.sh/tinybase/ui-react-dom-charts, react: https://esm.sh/react, react/jsx-runtime: https://esm.sh/react/jsx-runtime, react-dom/client: https://esm.sh/react-dom/client } } /script其中tinybase提供createStoretinybase/ui-react提供Provider与useCreateStoretinybase/ui-react-dom-charts提供全部图表组件。后的版本号通常由脚手架构建流程自动填充为当前版本如9.6.0。六个主题及其对应的官方演示页面如下主题官方演示BasicsLineChart 组件StylingStyled LineChart 组件Composition组合图表Data排序与类型Axes坐标轴覆盖Time时间轴第一个图表用LineChart /渲染 Store Table准备数据Store 与 Provider图表组件本身不持有数据而是通过 React Context 从Provider中取得 Store。顶层组件用useCreateStore惰性创建 Store 并写入一张sales表每行代表一个月份包含month月份标签、order排序序号与revenue收入三个 Cellconst App () { const store useCreateStore(() createStore().setTable(sales, { jan: {month: Jan, order: 1, revenue: 12}, feb: {month: Feb, order: 2, revenue: 18}, mar: {month: Mar, order: 3, revenue: 15}, apr: {month: Apr, order: 4, revenue: 24}, may: {month: May, order: 5, revenue: 21}, jun: {month: Jun, order: 6, revenue: 28}, }), ); return ( Provider store{store} Body / /Provider ); }; addEventListener(load, () createRoot(document.body).render(App /));绑定图表LineChart组件从 Provider Context 中读取 Store。使用tableId指定数据源表再用xCellId与yCellId分别绑定 x 轴与 y 轴读取的 Cellconst Body () ( main LineChart classNamechart tableIdsales xCellIdmonth yCellIdrevenue sortCellIdorder / /main );这里sortCellIdorder用于按orderCell 对行进行排序确保 x 轴按 1 月到 6 月的自然顺序绘制若不排序行会按默认行序遍历。除了绑定 Table图表组件同样可以绑定 Queries 的 ResultTable——只需把tableId换成queryId配合可选的queries属性这一分支在 src/ui-react-dom-charts/components/summary.ts 的useInitialSeriesSummary中分别通过getSortedRowIds/getResultSortedRowIds处理。让 SVG 可见最小必要 CSS图表组件输出的是svg元素唯一必需的 CSS 是给它一个可见的尺寸与字体。原文档给出的基础样式如下font-face { font-family: Inter; src: url(https://tinybase.org/fonts/inter.woff2) format(woff2); } * { box-sizing: border-box; } body { background: white; font-family: Inter, sans-serif; margin: 0; } main { margin: 2rem auto; max-width: 52rem; padding: 0 2rem; } .chart { display: block; font-size: 12px; height: 20rem; width: 100%; } media (max-width: 40rem) { main { padding: 0 1rem; } }其中.chart的display: block、width、height决定 SVG 的显示尺寸font-size会被组件读取并用于布局计算。仅凭以上代码即可得到一个使用默认 SVG 样式的可用折线图。从源码看LineChart本身是便捷封装它先用useInitialSeriesSummary计算初始序列摘要再把 props 拆分为数据源 绑定两部分最终渲染为一个CartesianChart包裹的LineSeries见 LineChart.tsx 与 props.ts。也就是说LineChart≈CartesianChart 单个LineSeries的语法糖BarChart同理见 BarChart.tsx。CSS 全面定制样式化折线图第二个主题在第一个 demo 基础上只改 CSS不改任何图表 props展示出数据与表现分离的设计组件负责响应式数据与几何计算CSS 负责全部视觉呈现。这也是组件默认使用currentColor着色这一设计带来的红利——在图表根元素上设置color即可同时影响绝大多数 SVG 元素。图表框架.chart { background: #f6f8fb; border: 1px solid #d9e2ec; border-radius: 0.5rem; box-shadow: 0 0.75rem 2rem #26364a1a; color: #1f2937; display: block; font-size: 12px; height: 20rem; padding: 1rem; width: 100%; }坐标轴与网格线通过更具体的选择器分别调整网格、轴线、刻度文字与轴标题.chart { .grid { color: #d8e1eb; stroke-dasharray: 4 6; } .axes { color: #677489; .title { fill: #1f2937; font-weight: 700; } } }数据区域面积、折线与数据点折线图的数据部分由三个可独立定制的部分构成面积area、折线line与数据点points.chart { .plot { .area { fill: #0ea5e9; fill-opacity: 0.12; } .line { stroke: #0284c7; stroke-linecap: round; stroke-linejoin: round; stroke-opacity: 1; stroke-width: 3; } .points { fill: white; stroke: #0284c7; stroke-width: 2; } } }Tooltip鼠标悬停数据点会出现的提示框同样是普通 SVG 元素可完全定制.chart { .tooltip-lines { stroke: #0284c7; stroke-dasharray: 3 3; stroke-opacity: 0.35; } .tooltip { font-weight: 700; rect { fill: #172033; fill-opacity: 0.94; } text { fill: white; } } }从源码确认这套 SVG 结构CartesianChart渲染svg (grid axes plot tooltip)四层见 CartesianChart.tsxGrid输出.grid组Grid.tsxAxes输出.axes组并以fill{CURRENT_COLOR}即currentColor着色Axes.tsxLine输出.area、.line、.pointsLine.tsxTooltip输出.tooltip-lines与.tooltipTooltip.tsx。这也解释了为什么只设置根元素 color 即可联动全图。组合图表CartesianChart与多个序列第三个主题展示如何用CartesianChart在一个 SVG 中组合多条序列父组件负责共享坐标轴、网格、布局与 tooltip每个子序列LineSeries/BarSeries只声明自己的 Cell 绑定、标签与 CSS 类。导入时换成可组合的系列组件import React from react; import {createRoot} from react-dom/client; import {createStore} from tinybase; import {Provider, useCreateStore} from tinybase/ui-react; import { BarSeries, CartesianChart, LineSeries, } from tinybase/ui-react-dom-charts;数据表中每个月份行同时携带多个 y 值 Cell它们即可成为同一图表中的不同序列const App () { const store useCreateStore(() createStore().setTable(sales, { jan: {month: Jan, order: 1, revenue: 62, profit: 33, orders: 18}, feb: {month: Feb, order: 2, revenue: 48, profit: 24, orders: 14}, mar: {month: Mar, order: 3, revenue: 31, profit: 19, orders: 10}, apr: {month: Apr, order: 4, revenue: 73, profit: 41, orders: 24}, may: {month: May, order: 5, revenue: 39, profit: 22, orders: 12}, jun: {month: Jun, order: 6, revenue: 55, profit: 30, orders: 17}, }), ); return ( Provider store{store} Body / /Provider ); }; addEventListener(load, () createRoot(document.body).render(App /));图表画廊中的每个CartesianChart提供数据源与图框子序列组件各自声明绑定const Body () ( main section CartesianChart classNamechart chart-lines tableIdsales LineSeries classNameseries-revenue labelRevenue sortCellIdorder xCellIdmonth yCellIdrevenue / LineSeries classNameseries-profit labelProfit sortCellIdorder xCellIdmonth yCellIdprofit / /CartesianChart /section section CartesianChart classNamechart chart-bars tableIdsales BarSeries classNameseries-orders labelOrders sortCellIdorder xCellIdmonth yCellIdorders / BarSeries classNameseries-profit labelProfit sortCellIdorder xCellIdmonth yCellIdprofit / /CartesianChart /section section CartesianChart classNamechart chart-mixed tableIdsales BarSeries classNameseries-orders labelOrders sortCellIdorder xCellIdmonth yCellIdorders / LineSeries classNameseries-revenue labelRevenue sortCellIdorder xCellIdmonth yCellIdrevenue / /CartesianChart /section /main );注意label会被用作该序列在 tooltip 与 y 轴标题中的名称从 getTitle 可以看到多个序列的 y 轴标题会以yLabel ?? yCellId去重后用 拼接例如Revenue Profit。因为每个序列都有独立classNameCSS 可以为每个序列独立配色。示例中三张卡片布局、通用.chart样式、以及按序列定制的.series-revenue/.series-profit/.series-orders颜色规则与混搭图表的特殊规则.chart-mixed .series-revenue .area {fill: none}如下font-face { font-family: Inter; src: url(https://tinybase.org/fonts/inter.woff2) format(woff2); } * { box-sizing: border-box; } body { font-family: Inter, sans-serif; margin: 0; } main { display: grid; gap: 1rem; grid-template-columns: repeat(3, minmax(0, 1fr)); margin: 2rem auto; max-width: 72rem; padding: 0 2rem; section { min-width: 0; } } .chart { display: block; font-size: 12px; height: 14rem; width: 100%; .grid { color: #d8e1eb; stroke-dasharray: 4 6; } .axes { color: #677489; .title { fill: #1f2937; font-weight: 700; } } .area { fill-opacity: 0.1; } .line { fill: none; stroke-linecap: round; stroke-linejoin: round; stroke-width: 3; } .points { fill: white; stroke-width: 2; } } .series-revenue { .area { fill: #2b8c67; } .line, .points { stroke: #2b8c67; } } .series-profit { .area { fill: #d16b3f; } .line, .points { stroke: #d16b3f; } .bar { fill: #d16b3f; } } .series-orders { .bar { fill: #5367c9; } } .chart-mixed { .series-revenue { .area { fill: none; } .line { stroke-width: 4; } } } media (max-width: 52rem) { main { grid-template-columns: 1fr; } }从实现角度看组合机制CartesianChart通过Children.forEach解析子元素识别系列组件带SERIES标记、XAxis与YAxis配置组件分别带X_AXIS/Y_AXIS标记序列与坐标轴配置分开收集见 CartesianChart.tsx 与 context.ts。每个BarSeries挂载时向父组件注册registerBarSeries父组件据此统计柱状序列数量、为同组柱条计算分组偏移见 Bars.tsx每个序列通过setSeriesSummary把自身的数据范围bounds与 x 值集合上报告父组件父组件合并所有序列的范围后统一计算刻度与坐标轴CartesianChart.tsx。数据类型与排序数值、ISO 日期、分类与布尔第四个主题聚焦 x 值的常见形态连续数值、ISO 日期字符串、有序分类标签、布尔分类。其通用规则是x 值支持 number / string / boolean而 y 值必须是有限数值finite number。从源码 data.ts 的getXValue/getYValue可以确认x 接受有限数值、字符串、true/false其余类型如对象、NaN、Infinity会被过滤y 则只接受isFiniteNumber为真的数值非法行会被剔除出数据点。示例 Store 用三张表分别演示不同形状const App () { const store useCreateStore(() createStore() .setTable(measurements, { third: {x: 3, score: 17}, first: {x: 1, score: 8}, fifth: {x: 5, score: 27}, second: {x: 2, score: 13}, fourth: {x: 4, score: 22}, }) .setTable(daily, { d1: {day: 2026-01-01, signups: 11}, d2: {day: 2026-01-02, signups: 17}, d3: {day: 2026-01-05, signups: 21}, d4: {day: 2026-01-08, signups: 28}, }) .setTable(channels, { organic: {channel: Organic, rank: 1, visits: 39}, referral: {channel: Referral, rank: 3, visits: 24}, paid: {channel: Paid, rank: 2, visits: 31}, direct: {channel: Direct, rank: 4, visits: 18}, }) .setTable(flags, { active: {enabled: true, order: 1, accounts: 28}, inactive: {enabled: false, order: 2, accounts: 11}, }), ); return ( Provider store{store} Body / /Provider ); }; addEventListener(load, () createRoot(document.body).render(App /));对应四个图表的绑定方式const Body () ( main section LineChart classNamechart chart-numbers tableIdmeasurements xCellIdx yCellIdscore / /section section LineChart classNamechart chart-dates tableIddaily xCellIdday yCellIdsignups / /section section BarChart classNamechart chart-categories tableIdchannels xCellIdchannel yCellIdvisits sortCellIdrank / /section section CartesianChart classNamechart chart-booleans tableIdflags BarSeries classNameseries-accounts labelAccounts sortCellIdorder xCellIdenabled yCellIdaccounts / /CartesianChart /section /main );类型推断规则与 getResolvedXScale 一致全部为数值→ 连续线性 x 轴LINEAR折线按真实数值间距定位全部为 ISO 日期字符串形如YYYY-MM-DD或以T开头的日期时间→ 时间轴TIME刻度为 UTC 时间标签其他字符串或布尔值→ 分类轴CATEGORYx 值按首次出现顺序或sortCellId排序后的行序离散排列布尔标签渲染为true/false折线图仅当所有 x 值都是数值时才算连续continuousX见 getSeriesSummary。复用同一套 CSS 结构即可覆盖四种图表font-face { font-family: Inter; src: url(https://tinybase.org/fonts/inter.woff2) format(woff2); } * { box-sizing: border-box; } body { font-family: Inter, sans-serif; margin: 0; } main { display: grid; gap: 1rem; grid-template-columns: repeat(4, minmax(0, 1fr)); margin: 2rem auto; max-width: 88rem; padding: 0 2rem; section { min-width: 0; } } .chart { display: block; font-size: 12px; height: 14rem; width: 100%; .grid { color: #d8e1eb; stroke-dasharray: 4 6; } .axes { color: #677489; .title { fill: #1f2937; font-weight: 700; } } .area { fill: #2b8c67; fill-opacity: 0.12; } .line { fill: none; stroke: #2b8c67; stroke-linecap: round; stroke-linejoin: round; stroke-width: 3; } .points { fill: white; stroke: #2b8c67; stroke-width: 2; } .bar { fill: #5367c9; } -booleans { .bar { fill: #d16b3f; } } } media (max-width: 52rem) { main { grid-template-columns: 1fr; } }补充一点measurements表中行是乱序插入的但仍会按 x 数值绘制因为数值行在连续线性轴上按值定位而分类轴的行顺序取决于行排序结果这正是channels表用rank排序、flags表用order排序的原因。坐标轴覆盖XAxis与YAxis配置子组件第五个主题演示XAxis/YAxis作为配置子组件configuration children的用法它们不渲染独立图表而是把标题、边界、刻度、刻度格式与 SVG 类名注入父级CartesianChart的共享坐标轴。示例把日期存为Unix 秒级时间戳并用XAxis显式声明其为时间刻度、格式化收到的Date对象const timestamp (day) Date.UTC(2026, 0, day) / 1000; const formatDate (date) date.toLocaleDateString(en-US, { day: numeric, month: short, timeZone: UTC, }); const App () { const store useCreateStore(() createStore().setTable(sales, { d1: {date: timestamp(1), revenue: 18}, d2: {date: timestamp(2), revenue: 26}, d3: {date: timestamp(3), revenue: 22}, d4: {date: timestamp(4), revenue: 39}, d5: {date: timestamp(5), revenue: 44}, d6: {date: timestamp(6), revenue: 41}, d7: {date: timestamp(7), revenue: 53}, }), ); return ( Provider store{store} Body / /Provider ); }; addEventListener(load, () createRoot(document.body).render(App /));图表部分同时覆盖 x 轴与 y 轴const Body () ( main CartesianChart classNamechart chart-axes tableIdsales XAxis classNameaxis-dates max{timestamp(7)} min{timestamp(1)} scaletime tickFormatter{formatDate} ticks{[timestamp(1), timestamp(3), timestamp(5), timestamp(7)]} timestampUnitsecond titleSale date / YAxis classNameaxis-revenue max{60} min{0} tickFormatter{(tick) $${tick}k} ticks{[0, 20, 40, 60]} titleRevenue / LineSeries xCellIddate yCellIdrevenue / /CartesianChart /main );关键 props 一览类型定义见 src/types/ui-react-dom-charts/index.d.ts组件Prop说明XAxisscaleauto \| category \| linear \| time显式指定 x 刻度类型默认autoXAxismin/max轴边界time刻度下接受 number / string / DateTimeValueXAxisticks显式刻度数组TimeValue[]传入后刻度不再自动生成XAxistickCount自动刻度数量的目标值默认 10见 wilkinson.tsXAxistickFormatter刻度格式化time刻度下签名为(tick: Date, timestamp: number) string其余为(tick: boolean \| number \| string) stringXAxistimestampUnitmillisecond \| second声明时间戳单位TimeXAxisProps专用XAxistitle轴标题覆盖从 Cell 名推断的默认标题XAxisclassName追加到 x 轴 SVG 组类名形如x axis-datesYAxismin/max/ticks/tickCount/title/className与 x 轴对应tickFormatter签名为(tick: number) string样式上两个轴的className分别加到.x与.y组上因此可以独立配色font-face { font-family: Inter; src: url(https://tinybase.org/fonts/inter.woff2) format(woff2); } * { box-sizing: border-box; } body { background: white; font-family: Inter, sans-serif; margin: 0; } main { margin: 2rem auto; max-width: 52rem; padding: 0 2rem; } .chart { background: #f7f9fb; border: 1px solid #dce5ee; border-radius: 0.5rem; color: #596779; display: block; font-size: 12px; height: 20rem; padding: 1rem; width: 100%; .grid { color: #d7e0ea; stroke-dasharray: 4 6; } .axes { .title { fill: #1f2937; font-weight: 700; } } .axis-dates { fill: #275f83; } .axis-revenue { fill: #6f4a1c; } .area { fill: #3b82a0; fill-opacity: 0.12; } .line { stroke: #1f7799; stroke-linecap: round; stroke-linejoin: round; stroke-width: 3; } .points { fill: white; stroke: #1f7799; stroke-width: 2; } } media (max-width: 40rem) { main { padding: 0 1rem; } }源码层面XAxis/YAxis组件本身渲染为null只是携带 props 的标记组件XAxis.tsx、YAxis.tsxCartesianChart在getParsedChildren中读取它们的 props并在getAxisBounds中让显式min/max/ticks优先于数据推断的边界CartesianChart.tsx。单元测试 ui-react-dom-charts.test.tsx 验证了该行为XAxis/YAxis的className、tickFormatter、ticks、title会完整生效且序列缺席时轴配置也能独立渲染uses explicit axis definitions before series data exists。时间轴ISO 字符串自动识别与 Unix 时间戳显式声明第六个主题对比时间轴的两种用法ISO 日期字符串可被自动识别Unix 秒级时间戳必须显式声明否则会被当作普通的大数值线性轴处理。const timestamp (day) Date.UTC(2026, 0, day) / 1000; const formatDate (date) date.toLocaleDateString(en-US, { day: numeric, month: short, timeZone: UTC, }); const App () { const store useCreateStore(() createStore() .setTable(visits, { d1: {day: 2026-01-01, visits: 42}, d2: {day: 2026-01-02, visits: 57}, d3: {day: 2026-01-05, visits: 64}, d4: {day: 2026-01-08, visits: 81}, }) .setTable(orders, { d1: {time: timestamp(1), orders: 12}, d2: {time: timestamp(2), orders: 18}, d3: {time: timestamp(5), orders: 21}, d4: {time: timestamp(8), orders: 25}, }), ); return ( Provider store{store} Body / /Provider ); }; addEventListener(load, () createRoot(document.body).render(App /));两个图表的绑定差异只在于第二个是否提供XAxisconst Body () ( main section LineChart classNamechart chart-iso tableIdvisits xCellIdday yCellIdvisits / /section section LineChart classNamechart chart-unix tableIdorders xCellIdtime yCellIdorders XAxis classNameaxis-unix scaletime tickFormatter{formatDate} ticks{[timestamp(1), timestamp(3), timestamp(5), timestamp(7)]} timestampUnitsecond titleOrder date / /LineChart /section /main );注意便捷组件LineChart同样接受children——它内部会把额外子元素透传给底层的CartesianChart见 LineChart.tsx。两个图表共享大部分样式仅用不同配色区分font-face { font-family: Inter; src: url(https://tinybase.org/fonts/inter.woff2) format(woff2); } * { box-sizing: border-box; } body { background: white; font-family: Inter, sans-serif; margin: 0; } main { display: grid; gap: 1rem; grid-template-columns: repeat(2, minmax(0, 1fr)); margin: 2rem auto; max-width: 64rem; padding: 0 2rem; section { min-width: 0; } } .chart { background: #f7f9fb; border: 1px solid #dce5ee; border-radius: 0.5rem; color: #596779; display: block; font-size: 12px; height: 18rem; padding: 1rem; width: 100%; .grid { color: #d7e0ea; stroke-dasharray: 4 6; } .axes { .title { fill: #1f2937; font-weight: 700; } } .line { stroke-linecap: round; stroke-linejoin: round; stroke-opacity: 0.9; stroke-width: 3; } .points { fill: white; stroke-width: 2; } } .chart-iso { .area { fill: #3b82a0; fill-opacity: 0.12; } .line, .points { stroke: #1f7799; } } .chart-unix { .area { fill: #2b8c67; fill-opacity: 0.12; } .line, .points { stroke: #2b8c67; } .axis-unix { fill: #2f6c4c; } } media (max-width: 52rem) { main { grid-template-columns: 1fr; padding: 0 1rem; } }时间轴识别的底层逻辑ISO 自动识别依赖正则ISO_DATE /^\d{4}-\d{2}-\d{2}(?:$|T)/data.ts当所有 x 值都是能被normalizeTimeValue解析为有限时间戳的字符串时getResolvedXScale判定为TIMEnormalizeTimeValue对数值按timestampUnit归一化second单位 ×1000 转毫秒、对 ISO 字符串与Date实例取毫秒时间戳data.ts时间刻度的自动生成由getTimeTicks完成在预定义的 22 档时间间隔1 秒到 100 年见 wilkinson.ts中按间隔跨度、覆盖率、密度、标签间距评分择优同时保证刻度间距不小于标签宽度spacing labelSize * 1.2时淘汰——因此刻度标签是确定性的 UTC 日期/时间格式wilkinson.ts。源码视角响应式数据流与渲染架构结合 src/ui-react-dom-charts 源码可以把整套图表机制归纳为三层架构1. 便捷组件层LineChart/BarChart把tableId/queryId与xCellId/yCellId/sortCellId等绑定 props 拆分给CartesianChart与内置系列是单序列图表的语法糖props.ts。2. 编排层CartesianChart通过 ContextCartesianChartContext向下提供数据源、刻度、边界、布局与回调向上汇总每个系列的SeriesSummaryx 范围、y 范围、x 值集合、是否连续合并出统一 domain并解析XAxis/YAxis配置子组件决定轴行为context.ts、CartesianChart.tsx。3. 图元层Grid/Axes/Line/Bars/Tooltip基于统一 domain 把数据点缩放到像素坐标输出纯 SVG。数据响应性来自useSeriesData它通过useSortedRowIds/useResultSortedRowIds获取有序行 id并用useCellListener/useResultCellListener订阅 x/y Cell 变化——任何绑定 Cell 更新都会触发序列重渲染进而把新摘要上报给父组件、由父组件增量合并 domainseries.ts。布局方面useLayout通过ResizeObserver观察 SVG 尺寸变化并读取计算样式中的font-size缩放默认间距常量DEFAULT_STYLE [0.5, 0.5, 0.25, 3.5, 4, 1, 1]因此图表可以自适应容器svg.ts。连续轴刻度采用改进的 Wilkinson 算法NICE_NUMBERS [1, 5, 2, 2.5, 4, 3]、权重[0.25, 0.2, 0.5, 0.05]生成整齐好看的刻度步长wilkinson.ts。这些行为都被单元测试覆盖test/unit/core/ui-react/ui-react-dom-charts.test.tsx 验证了 Table/Query 两种数据源绑定、Provider与storesById上下文、XAxis/YAxis配置生效、分类轴标签与数据点对齐、ISO 字符串自动识别为时间轴、秒级时间戳归一化、确定性 UTC 刻度标签等场景类型测试 test/unit/core/types/ui-react-dom-charts.tsx 则约束了tableId与queryId互斥、timestampUnit取值、tickFormatter签名等类型安全边界。总结Chart Components (React) 演示系列完整展示了 TinyBaseui-react-dom-charts模块的设计哲学声明式数据绑定图表直接消费 Store Table 或 Queries ResultTable数据变化自动驱动 SVG 重绘无需手动维护图表状态数据与表现分离几何与响应式由组件负责currentColor 明确的 SVG 类名体系.grid/.axes/.plot/.area/.line/.points/.bar/.tooltip让全部视觉细节都可以用纯 CSS 定制组件组合CartesianChart编排共享坐标轴LineSeries/BarSeries各自绑定数据XAxis/YAxis作为配置子组件提供精细的刻度、边界与格式化控制智能类型推断数值→线性轴、ISO 日期字符串→时间轴、字符串/布尔→分类轴同时允许用scaletime等方式显式覆盖。从基础折线图到多序列组合、从数据形状处理到时间轴与坐标轴覆盖这六个主题构成了一条平滑的学习路径。继续深入可阅读同目录下的其他主题文档site/demos或直接查看 src/ui-react-dom-charts 源码与 test/unit/core/ui-react/ui-react-dom-charts.test.tsx 测试来验证更多边界行为。赞分享数据库后端前端【免费下载链接】tinybaseA reactive data store sync engine.项目地址https://gitcode.com/gh_mirrors/ti/tinybase点击查看免费下载相关推荐TinyBase 图表组件组合实战用 CartesianChart 在同一张数据表中渲染多序列图表TinyBase 图表组件组合实战用 CartesianChart 在同一张数据表中渲染多序列图表 本文基于 TinyBase 仓库中 ui react do数据库后端前端TinyBase ValuesInHtmlTable / (Solid) 实战用 ui-solid-dom 组件把 Store Values 渲染成响应式 HTML 表格TinyBase ValuesInHtmlTable / Solid 实战用 ui solid dom 组件把 Store Values 渲染成响应式 HT数据库后端前端TinyBase 实战指南使用 ValuesInHtmlTable 在 React 中渲染 Store 的 Values 数据表TinyBase 实战指南使用 ValuesInHtmlTable 在 React 中渲染 Store 的 Values 数据表 本文基于 TinyBase数据库后端前端上一篇Mnemosyne TripleStore教程为你的AI Agent构建可『时间旅行』的知识图谱下一篇Qwen3-ASR Web UI 实战:Gradio 在线语音转写界面与流式 Demo 一键启动教程创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑