资讯动态

PostHog quill-charts:Canvas 渲染图表库的选型、主题、组合与测试全解

发布时间:2026/9/13 22:49:09 来源:尧图企业网站定制
PostHog quill-chartsCanvas 渲染图表库的选型、主题、组合与测试全解【免费下载链接】posthog:hedgehog: PostHog is the leading platform for building self-driving products. Our developer tools – AI observability, analytics, session replay, flags, experiments, error tracking, logs, and more – capture all the context agents need to diagnose problems, uncover opportunities, and ship fixes. Steer it all from Slack, web, desktop, or the MCP.项目地址: https://gitcode.com/GitHub_Trending/po/posthogposthog/quill-charts是 PostHog monorepo 中 quill 设计体系下的 Canvas 图表库D3 负责比例尺canvas 负责绘制React 负责 DOM 覆盖层配色完全由 quill 设计 tokenCSS 变量在运行时注入库本身不携带任何 CSS。本文以包内自带的 Agent 速查文档 AGENTS.md 为主线结合 源码类型定义、包 README 和 src/docs/ 下的专题文档覆盖图表选型、主题接线、Series 数据契约、组合渲染、坐标轴/域控制、tooltip 与交互、易踩的坑以及 jsdom 下的测试契约帮助你在 PostHog 前端或任何 quill 宿主中正确、可复现地构建图表。一、包的定位与公开 API 面从 package.json 可以看到包的运行时依赖非常克制d3-array、d3-scale、d3-shape、d3-color、dayjs、simple-statistics和floating-ui/reactReact 18/19 均为 peer 依赖sideEffects: false。这个依赖面印证了它的分层设计——比例尺交给 D3 子包绘制在 canvas 完成覆盖层图例、tooltip、参考线是纯 React DOM。入口文件 src/index.ts 导出了完整的公开面大致分四类图表组件LineChart、TimeSeriesLineChart、BarChart、TimeSeriesBarChart、ComboChart、TimeSeriesComboChart、FunnelChart、PieChart、ScatterChart、BoxPlot、Heatmap、SlopeChart、Sparkline、MetricCard核心与上下文基类Chart、RadialChart、ChartErrorBoundary以及给自定义覆盖层用的useChart/useChartLayout/useChartHover主题与默认配置useChartTheme、themeFromCssVars、themeDefaultsFromCssVars、DEFAULT_CHART_COLORS、applyChartDefaults、DEFAULT_CHART_CONFIG内置覆盖层与工具ReferenceLine、ValueLabels、AxisTitles、HighlightedRange、AnomalyPointsLayer、DefaultTooltip、TooltipSurface/TooltipSwatch/TooltipFooter以及统计工具ciRanges、linearRegression、movingAverage、trendLine。消费方约定组件和类型一律从posthog/quill-charts导入不要引用内部源码路径。这一点在 仓库的 consumer skill 中被明确重申。二、图表选型一张表决定用哪个组件AGENTS.md 的核心就是选型表 每图行为备注。完整选型决策如下组件适用场景LineChart类别型 x 轴的趋势面积填充、fill-between置信带TimeSeriesLineChart时间索引标签ISO 字符串 时区/间隔感知的 x 轴目标线、趋势线、移动平均、置信带均可配置BarChart类别对比——barLayout: stacked \| grouped \| percentaxisOrientation: horizontal做横向条形TimeSeriesBarChart与时间序列折线图相同的 x 轴处理但用柱状渲染支持每序列yAxisId多轴ComboChart同一个 band x 轴上混排 bar/line/area 序列——通过Series.type指定仅支持纵向TimeSeriesComboChartComboChart加上时间序列外壳日期 x 轴、目标线、图例、数值标签FunnelChart漏斗步骤斜线填充的流失轨道上的分组柱——每个步骤一个 band、每个变体一根柱数值按第一步的百分比计PieChart部分与整体每序列一个值innerRadiusRatio变环形图ScatterChart双连续数值轴——每个{ x, y }点一个标记接收points而非labelsBoxPlot分布摘要——每个标签一组{ min, p25, median, mean, p75, max }Heatmap二维密度网格如时间 × 延迟——xLabels×yLabels数据为cells[row][col]SlopeChart两点间变化——每序列一条线data: [start, end]Sparkline无坐标轴的迷你内联趋势——渐变折线或堆叠柱默认关闭 tooltipMetricCard标题数字 sparkline 变化率徽章仪表盘指标卡各图表的细化行为写在 docs/chart-types.md。几个值得展开的点TimeSeriesLineChart是LineChart加时间序列外壳由xAxis.timezone和interval生成日期刻度、间隔感知的 tooltip 头、goalLines、valueLabels、图例以及派生序列配置confidenceIntervals、movingAverage、trendLines。comparisonOf把对比序列映射到主序列以便渲染为暗色。派生序列趋势线、移动平均被标记为overlay: true因此不参与堆叠和基线计算——趋势外推不会把 y 轴拽到零以下而置信带不是overlay因为它代表真实的数据不确定性其范围应当影响坐标轴。ScatterChart接收ScatterSeries[]每点{ x, y, label?, radius?, color?, meta? }没有labels因为两个轴都是连续的。两轴默认各自浮动到数据范围——强制任一轴从 0 开始会把相关性压进角落。命中检测是二维且基于边界的光标落在大标记内部时优先于中心更近的小标记。showBestFit为每序列画一条虚线最小二乘线在像素位置上拟合对线性轴和对数轴都取正确的残差。FunnelChart是分组BarChart的薄封装自带漏斗外观斜线填充的track、圆角与阴影柱、百分比值轴。steps是显示标签数组允许重复band 按步骤索引定位每序列的data[stepIndex]是相对第一步的转化率0..100funnelFromCounts从原始{ label, count }构造单序列情形基数为 0 时得到 0 而不是NaNonStepClick报告{ stepIndex, converted }converted: false表示点击的是柱上方的斜线流失轨道。SlopeChart每序列一条从左before到右after的线data是[start, end]。标签冲突时变化量最大的序列保留其名称meta.incompleteEnd通过stroke.partial.fromFraction只把最后一段的中点以后画成虚线表示终点是当前未结束的周期。PieChart每序列取data[0]innerRadiusRatio变环形图theme.backgroundColor是 hover 弹出遮罩的必需项缺了就不做弹出子组件通过useRadialLayout()读取layout.slices、cx、cy、centroidAngle等。Heatmap是xLabels×yLabels的密度网格第 0 行在底部cells[row][col]映射到单一强调色的深浅默认对数色阶colorScale: linear可关闭onBrush报告行列索引范围。MetricCard是标题数字 变化徽章 sparkline的自包含指标卡。title{null}去掉标题行restingSubtitle如Avg只在静止时显示hover 时让位给悬停点的标签sparklineDashedFromIndex把尾部进行中的周期画成虚线。其无头部分resolveDelta、computeFallbackChangePercent、useAnimatedNumber、useHoverIntent也被导出给上层posthog/quill-components的可组合Metric复用——因为 charts 包不能依赖 primitives 的Card/Badge。三、主题接线颜色从 CSS 变量来库不带调色板库在颜色上是无头的每个图表都接收一个ChartThemecore/types.ts 中定义自己不拥有调色板。官方推荐的接线方式import { useChartTheme } from posthog/quill-charts const theme useChartTheme() // 读取 CSS 变量并跟踪 light/dark 切换 LineChart series{series} labels{labels} theme{theme} /序列颜色来自--data-color-1..15坐标轴等外壳颜色来自--color-graph-axis-label/--color-graph-axis-line/--color-graph-crosshair。themeFromCssVars()是一次性非 React读法DEFAULT_CHART_COLORS是无 DOM 环境的回退调色板。主题助手自带默认外壳样式淡色虚线网格、更实的轴线、虚线十字线。对应的开关在DEFAULT_CHART_CONFIG中由LineChart、BarChart、ComboChart及其时间序列变体通过applyChartDefaults垫在用户config之下消费方逐字段退出如showGrid: false嵌套的tooltip配置按 key 合并设一个字段不会丢掉其余默认值如placement: cursor。theme.skipDraw只挂载 canvas 不绘制用于确定性的视觉快照测试异步绘制管线会和截图抢跑。序列上省略color时按序列索引从调色板自动取色推荐做法。显式color必须是具体颜色hex、rgbline、area、bar 序列会把颜色原样交给 canvas所以var(--…)需要宿主先行解析只有Heatmap和ScatterChart会自己解析var()。这条规则直接写在Series.color的 JSDoc 里。按 包 README 的安装建议加载posthog/quill-tokens/color-system.css后useChartTheme()才能解析出真实的 quill 配色否则得到内置回退调色板不是品牌色。useChartTheme/themeFromCssVars默认从document.body读变量如果你用 scoped token 构建变量挂在[data-quill]下且 quill 不挂在body传root指向 scoped 子树内部如useChartTheme({ root: myQuillEl })。四、Series 数据契约标准 Series 形态const series: Series[] [ { key: visits, label: Visits, data: [20, 35, 28] }, // data.length labels.length { key: goal, label: Goal, data: [30, 30, 30], overlay: true }, // 不参与堆叠与基线 ]结合Series的 JSDoccore/types.tskey、label、data必填。key同时是 React key 与堆叠身份标识缺失数值请按仓库约定用NaN而不是 0 表达见 consumer skill 的missing numeric series values条目。labels必须唯一。x 比例尺以字符串为键定位重复标签会折叠到首次出现处并把序列画反。时间序列图表请传 ISO 日期字符串用xAxis.timezone/interval格式化刻度不要用会在年份间重复的显示标签。meta携带任意数据流入 tooltip 和点击回调用SeriesMyMeta收窄类型以便在 handler 里做类型化读取。visibility控制序列出现的场所四个位各有默认值excluded默认 false完全排除——不渲染、不参与比例尺、不进 tooltip、不做命中检测、tooltip默认 truefalse 时序列仍渲染但仍不参与 tooltip 行、total默认 truefalse 时不计入内置 tooltip 的合计行适合百分比列与计数列并排的场景、valueLabel默认 true控制ValueLabels覆盖层是否标注该序列。overlay: true标记派生序列趋势线、移动平均使其排除在堆叠计算和 y 轴基线之外。折线/面积独有points半径、stroke.pattern虚线模式、stroke.partial部分区间虚线fromFraction可按比例切分末段、fillopacity默认 0.5lowerData做 fill-between如置信区间下界gradient垂直渐隐填充。柱状独有bars[]逐柱覆盖color/label/metahatch: true把单柱画成对角斜线填充标记尚未落定的桶任意索引可用trackData逐柱封顶可交互范围——超出部分是完全惰性的空白无 hover、tooltip、高亮、点击漏斗对比图用它把较短周期的量级差画成空白而不是流失。多轴与组合图yAxisId指定该序列挂哪条 y 轴默认left见DEFAULT_Y_AXIS_IDtype: line | bar | area供ComboChart读取缺省回退到图表级defaultSeriesType。五、尺寸图表填满容器父级必须有真实尺寸所有图表Sparkline除外填满容器父级必须有真实尺寸——零高度的 flex 子项不会画出任何东西给包裹元素显式高度h-64或定高列里的flex-1。Sparkline是唯一直接接收height/widthprops 的组件。ChartConfig.margins只覆盖你设置的边未设的边保留计算所得边距因此{ top: reserveOrUndefined }这类条件构造对象是安全的建议传模块级常量以保持引用稳定。六、组合config 子组件覆盖层一个典型的组合用法TimeSeriesLineChart series{series} labels{isoLabels} theme{theme} config{{ xAxis: { timezone: UTC, interval: day }, yAxis: { format: currency, currency: USD }, tooltip: { pinnable: true }, legend: { show: true, position: right }, }} ReferenceLine value{100} orientationhorizontal variantgoal labelTarget / ValueLabels modestack-total offset{8} / /TimeSeriesLineChart规则与机制覆盖层ReferenceLine/ReferenceLines、ValueLabels、AxisTitles、TrendLineOverlay、HighlightedRange、AnomalyPointsLayer以子组件方式组合详见 docs/overlays.md。自定义覆盖层是任意 React 组件通过useChartLayout()比例尺、尺寸、主题hover 不触发重渲染和useChartHover()悬停点每次 mousemove 重渲染读取状态useChart()是二合一的向后兼容入口。内置DefaultTooltip由config.tooltip配置传tooltiprender prop 则整体接管内容收到TooltipContext。自定义但想保留内置外观时可组合导出的TooltipSurface浮动面板、TooltipSwatch色点、TooltipFooter分隔线 弱化行也可以直接DefaultTooltip {...ctx} footer... /扩展。config.legend渲染内置图例点击模型是普通点击隔离isolate被点序列——其余行全部隐藏不绘制、不参与比例尺、不进 tooltip坐标轴重新缩放进腾出的空间再次点击被隔离的行恢复全部⌘/Ctrl或 Shift点击单个切换显隐。通过hiddenKeysonToggleSeriesonSetHiddenSeries三件套可改为受控状态visibilityGroupKey让消费方按一个可见位分组多行如对比两个周期的场景renderItem可包裹每行以追加右键菜单等增强。交互onPointClick收到PointClickData主序列、dataIndex、label、value、crossSeriesData、cursor、分组柱上的inTrackArea横向拖拽缩放onDateRangeZoom报告{ startLabel, endLabel, startIndex, endIndex }——图表本身不管理缩放状态由父级通常是更新日期筛选决定2D 框选onAreaSelect同时报告 x 的标签范围与原始 x/y 像素跨度y 留在像素空间由各图表适配器映射到自己的 band如Heatmap转成行索引、ScatterChart反算回数据值。两者都设时onAreaSelect优先。细节见 docs/interactions.md。七、坐标轴、格式与值域控制这部分的行为语义跨多个字段沉淀在 docs/axes.md 与ChartConfig/ValueDomain的 JSDoccore/types.ts中网格/轴线/刻度showGrid画网格线且始终对齐主左y 轴刻度次轴上的showGrid被忽略因为两套不对齐的刻度是噪声showAxisLines画 L 形轴线{ x, y }可分别开关开轴线时折线描边会在 y 轴处修剪、在 x 轴处落地使线条贴在轴上showTickMarks画与轴同一像素网格对齐的短刻度线curve: monotone用单调三次曲线平滑折线经过每个点且不过冲。y 轴格式format支持numeric | short | percentage | percentage_scaled | currency | duration | duration_ms | duration_ns外加prefix/suffix。不设format/tickFormatter时轴用能区分各刻度所需的最少统一小数位数值/百分比格式默认两位小数但在 0.1 以下每多一个前导零多一位小数避免 0..0.012 的延迟轴每格都显示0.01duration 系格式在分钟以下保留三位有效数字之后切换为1m 30s拆解。基线与范围非负轴默认钳到 0TimeSeriesLineChart用yAxis.startAtZero: false、LineChart用config.floatBaseline: true可让轴浮动到数据范围log 轴忽略、只作用于主轴。yAxis.min/max固定端点柱状/组合图有意忽略min/max——柱的长度编码量级截断基线会让 10 和 11 画成 1 和 2应改用valueDomain重新缩放柱体。TimeSeriesBarChart接收config.valueDomain{ min: 0, max: dataMax }可让最高的柱顶到绘图区顶部。ValueDomain语义core/types.ts省略则自动取数据范围并做d3.nice()两端都设时锁定域、跳过nice()并覆盖 percent 布局适合让独立图表互相可比只设一端则钳住该端。非有限值视为未设{ min: 0, max: Math.max(...[]) }得到零下界而非坍缩min max的倒置对回退到自动域而不是交换——因为这些值可能来自保存的查询、API 或 MCP静默重解释会画出没人要求的轴。include用于把离屏的目标线纳入域在nice()之前合并。多 y 轴给序列设yAxisId即可挂第二轴时间序列图表把config.yAxis传数组每个YAxisConfig带id、position、scale、format、label、hide、startAtZero、min、max。第二轴仅在有序列指向它时渲染主轴拥有网格线柱序列的轴保持零基线。含义固定的次轴0..1 概率、0..100 百分比要钉住min/max否则高于其全部数据的参考线会画出绘图区。超过两条轴也可以多轴按侧向外堆叠、左右交替。空白绘图区诊断覆盖层是 DOM、按坐标轴定位网格、轴线、刻度线和序列在 canvas 上。如果标签和目标线位置正确但绘图区空白说明布局没坏、只是 canvas 绘制失败了先查theme.skipDraw再查drawStatic抛错位图还可能被syncCanvasSize的真实 resize 或浏览器丢失 2D 上下文而丢弃useChartCanvas会在两者之后重绘Safari 不触发contextrestored丢上下文后将持续空白。八、Tooltip 行为速查tooltips.md 的关键结论placement库默认cursor跟随光标follow-data跟踪悬停 x 处最高数据点top固定在顶边。pinnable开启点击固定未固定时 tooltip 是pointer-events: none行点击只在固定后生效。resolveClickToNearestSeries默认 falsepinnable 多序列图上点击直接解析最近序列并触发onPointClick跳过先固定再点行。只在序列位置上无歧义时使用漏斗分解区域序列重叠时趋势线保持默认。hitArea柱状图bar要求光标在已绘制柱体上短柱上方空白不显示任何东西band允许整个 band 内触发一像素高的柱或零值桶也能报告数值——Sparkline的柱因此用band。不加 render prop 就能定制格式config.tooltip透传valueFormatter(value, entry)第二参是该行的seriesData条目可按序列各自meta做货币/时长格式化且可返回 node、labelFormatter、showTotal/totalLabel/totalFormatter、sortedByValue、hideZeroRows、onRowClick、footer。给了 render prop 则这些字段被忽略。合计行排除overlay序列与visibility.total: false的序列可加和序列少于 2 时不显示合计。读TooltipContext的要点seriesData按声明顺序、逐可见序列一项按entry.series.key查行而不是按位置BarChart设置hoveredSeriesKey光标下的柱/段与inTrackArea分组布局中光标越过柱填充范围的区域——描述悬停段的 tooltip 必须用它选择而不是seriesData[0]percent 布局下value是 0..1 分数原始值在ValueLabelContext.rawValueonUnpin只在固定期间存在从行点击打开模态框前先调用它。触控笛卡尔图在触控设备上走 tap 模型——点一点显示 tooltippinnable 时固定行可点做下钻点另一点移动过去点已显示 tooltip 的那一点则执行点击动作点图表外、滚动或 Escape 关闭。鼠标行为不变分流逻辑在useChartInteraction中按 pointerdown 的pointerType判定。九、那些真的会咬人的坑AGENTS.md 的 Gotchas that bite 一节逐条继承如下并补充源码侧的对照重复标签会把序列画反——用 ISO 日期而不是显示标签percent 布局下所有 formatter 收到的是 0..1 分数ValueLabelContext.rawValue才有原始值柱状和组合图有意忽略yAxis.min/max——固定域请用valueDomain固定pinnedtooltip 的onRowClick只有在config.tooltip.pinnable设置时才触发从它打开模态框前先调ctx.onUnpin()堆叠柱上描述悬停段要用TooltipContext.hoveredSeriesKey不要用seriesData[0]含义固定的次轴0..1、0..100必须钉min/max否则高于其数据的参考线画不到绘图区里自带 hover/点击的自定义覆盖层根节点要加data-hog-charts-interactive-overlay否则图表 tooltip 会和它抢光标顶/底部图例的高度上限只对显式容器height生效对 flex 推导出的高度不生效每次渲染都生成新的Series[]引用会重算比例尺并重绘 canvas——请做 memoization。十、测试契约data-attr是公开接口图表测试体系定义在 docs/TESTING.md 与 src/testing/。两类读者测试使用图表的业务代码用你自己的render从posthog/quill-charts/testing导入ensureJsdom安装ResizeObserver、getBoundingClientRectmock 与同步requestAnimationFrameshim幂等、getHogChart(scope)等import { render } from testing-library/react import { ensureJsdom, getHogChart, hoverAtIndex, waitForHogChartTooltip } from posthog/quill-charts/testing ensureJsdom() it(renders the goal line on the dashboard chart, async () { const { container } render(Dashboard /) const chart getHogChart(container) expect(chart.referenceLines()).toHaveLength(1) expect(chart.yTicks()).toContain(0) hoverAtIndex(chart.element, 1, LABELS.length) const tooltip await waitForHogChartTooltip() expect(tooltip.textContent).toContain(Tue) })accessor 面完整列表见 testing/accessor.ts包括chart.canvas静态数据 canvas非 aria-hidden 的 hover 覆盖层、chart.seriesCount取自 canvas 的 aria-label、chart.yTicks()/yRightTicks()/xTicks()、chart.referenceLines()、chart.valueLabels()、chart.anomalyPoints()等。交互驱动器是模块级hoverAtIndex/clickAtIndex/dragSelection/waitForHogChartTooltip。测试库本身图表级测试位于charts/name/Name.test.tsx本仓库中每个图表都有对应测试文件如 LineChart.test.tsx用renderHogChart顶层渲染图表——它比render多三样自动ensureJsdom、tooltip 上下文捕获chart.waitForTooltip()返回结构化TooltipSnapshot含seriesData、isPinned、缓存的labels.length。两条硬契约必须记住data-attr选择器与HogChartaccessor 是稳定契约重命名data-attr等同破坏性变更——它破坏消费方的测试如同重命名导出的类型。jsdom 的 canvas 是 stub像素不是可行测试面断言全部走 DOM。反模式清单不要 mockcore/canvas-renderer几何应直接在 core/bar-layout.test.ts 里对computeSeriesBars测不要读scales._private它是不透明的图表类型私有槽不要做像素快照或getContext(2d)间谍不要querySelector(canvas)兜底renderHogChart缺 canvas 会直接抛错it.each矩阵每行至少要断言一个可观察属性不要伸进 React 内部。十一、文档地图与维护约定AGENTS.md 的自我定位是地图prop 级语义在各配置与 props 类型的 JSDoc 上src/core/types.ts、src/utils/use-axis-formatters.ts、各图表的*Props跨多个字段的行为在src/docs/专题文档里。阅读顺序是先读类型再读专题文档。文档索引均已换算为仓库根相对路径文档覆盖内容src/README.md公开面、安装、主题、自定义 tooltip 与覆盖层基础、sparklinedocs/chart-types.md各图表行为scatter、funnel、slope、pie、box plot、heatmap、sparkline、metric carddocs/axes.md默认值、网格与轴线外壳、x 轴标签、y 格式、基线与范围、多轴、边距、空白绘图区诊断docs/bars.md布局、逐柱覆盖、minBarSize、trackData、命中检测、趋势线、combodocs/tooltips.mdconfig.tooltip、DefaultTooltipprops、自定义组件、context 字段、触控docs/legend.md点击模型、受控状态、可见性分组、行渲染、布局上限docs/overlays.md内置覆盖层与编写自定义覆盖层docs/interactions.md点击、拖拽缩放、2D 框选、hoverdocs/CONTRIBUTING.md分层结构、约定、新增图表类型docs/TESTING.mdaccessor 契约与测试配方维护规则同样写死在地图里新增或修改图表、覆盖层、配置项时——把 JSDoc 写在 prop 上、在组件旁加或更新 story、行为跨多个字段时更新src/docs/的对应专题文档只有当变更改变了消费方该选哪个图表/哪种做法或新增了坑时才向 AGENTS.md 加行不要往既有条目追加子句这个文件保持一张地图。库的详细行为收敛在本文件与src/docs/中消费侧入口是 consumer skill它把任务映射到上述包文档与示例。十二、结语posthog/quill-charts的设计可以概括为三句话颜色无头全部从 quill CSS token 注入回退有保底调色板、绘制与交互分层canvas 静态层 DOM 覆盖层hover 状态独立 context 避免无谓重渲染、测试即契约data-attr选择器 accessor 构成稳定公开面。掌握本文的选型表、Series 契约、valueDomain语义、tooltip/图例点击模型和坑位清单再按先 JSDoc 后专题文档的路径深入 src/docs/即可在 PostHog 前端或任何 quill 宿主中以库内置能力而非自造轮子的方式交付图表。【免费下载链接】posthog:hedgehog: PostHog is the leading platform for building self-driving products. Our developer tools – AI observability, analytics, session replay, flags, experiments, error tracking, logs, and more – capture all the context agents need to diagnose problems, uncover opportunities, and ship fixes. Steer it all from Slack, web, desktop, or the MCP.项目地址: https://gitcode.com/GitHub_Trending/po/posthog创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

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

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

免费获取报价