@visx/grid 网格线组件完全指南:为 visx 图表添加横向、纵向与极坐标网格
【免费下载链接】visx🐯 visx | visualization components项目地址: https://gitcode.com/gh_mirrors/vi/visx
@visx/grid是 visx 可视化组件库中专用于绘制图表网格线的包:<GridRows />渲染水平网格线、<GridColumns />渲染垂直网格线,而<Grid />可一次同时渲染两者。本文以 packages/visx-grid/Readme.md 为主线,结合仓库源码(types.ts、GridRows.tsx、GridColumns.tsx、Grid.tsx 等)深入讲解全部组件的属性、默认值与实现原理,读完你可以直接在 visx 折线图、柱状图、散点图中快速接入横纵网格,也可以在雷达图/玫瑰图中使用极坐标网格(GridAngle、GridRadial、GridPolar)。
包定位与核心组件
@visx/grid的职责非常单一——根据已有的 scale(比例尺)在指定宽度/高度范围内生成网格线。它自身不包含坐标轴,只负责网格背景。从 src/index.ts 可以确认该包对外导出的全部组件:
GridRows—— 水平网格线(横线)GridColumns—— 垂直网格线(竖线)Grid—— 横线 + 竖线的组合容器GridAngle—— 极坐标角度网格线(从圆心向外辐射的射线)GridRadial—— 极坐标径向网格线(同心圆弧)GridPolar—— 极坐标网格的组合容器(角度线 + 径向线)
其中前三者服务于常见的直角坐标系图表(折线、柱状、散点、面积等),后三者服务于极坐标系图表(雷达图、玫瑰图等)。
安装
在 React 18 或 19 项目中安装:
npm install --save @visx/grid根据 package.json,该包以 React 18/19 为 peerDependencies,运行时依赖@visx/curve、@visx/group、@visx/point、@visx/scale、@visx/shape与classnames(这些依赖会在安装时自动带入)。包同时提供 CommonJS(lib/)与 ES Module(esm/)两种产物,且声明sideEffects: false,可安全参与 tree-shaking。
基本用法:Grid 组合横纵网格线
Readme 中的核心示例展示了最常用的方式——用一个<Grid />同时获得横纵两组网格线:
import { Grid } from '@visx/grid'; // 或者 // import * as Grid from '@visx/grid'; // <Grid.Grid /> const grid = ( <Grid xScale={xScale} yScale={yScale} width={xMax} height={yMax} numTicksRows={numTicksForHeight(height)} numTicksColumns={numTicksForWidth(width)} /> );要点说明:
xScale/yScale分别是映射 x、y 坐标的 scale,可以是@visx/scale创建的 scale,也可以是原生 d3-scale(见 Grid.tsx 的类型定义GridScale)。width/height决定网格线的绘制范围:横向线从左边界画到width,纵向线从上边界画到height。numTicksRows/numTicksColumns控制横、纵网格线的“近似”条数。注意源码注释特别强调:由于 d3 的 tick 算法,这个数值是近似的(约等于),需要精确控制时应改用tickValues。- 上述示例中
numTicksForHeight/numTicksForWidth是 visx 常用的辅助约定,即让网格线数量随绘图区域尺寸自适应(例如Math.floor(height / 100)),Readme 以占位函数形式给出,实践中可自行实现或用固定数字。
为什么 numTicks 是“近似值”?
看 GridRows.tsx 的实现:const ticks = tickValues ?? getTicks(scale, numTicks);。当不传tickValues时,网格线位置由@visx/scale的getTicks依据 d3 的scale.ticks(count)算法生成。d3 的 tick 算法会根据 scale 的 domain 自动把请求的 tick 数“圆整”到美观的数值(如 1、2、5 的倍数),因此实际条数可能与传入值略有出入。若你的场景(如网格线与坐标轴标签严格对齐)要求完全一致,请直接传入tickValues。
GridRows 与 GridColumns:单独使用
当只需要一组方向的网格线时,可以单独使用<GridRows />或<GridColumns />。二者实现几乎对称,区别仅在于:
GridRows接收scale(y 方向 scale)与width,横向绘制;每条线的 y 坐标由scale(d)计算,x 从 0 到width。GridColumns接收scale(x 方向 scale)与height,纵向绘制;每条线的 x 坐标由scale(d)计算,y 从 0 到height。
两个组件的默认值(见各自源码的组件签名)完全一致:top = 0、left = 0、stroke = '#eaf0f6'(浅蓝灰)、strokeWidth = 1、numTicks = 10。
import { GridRows, GridColumns } from '@visx/grid'; // 只画横向网格线 <GridRows scale={yScale} width={xMax} numTicks={5} stroke="#eee" /> // 只画纵向网格线 <GridColumns scale={xScale} height={yMax} numTicks={5} />内部实现:从 tick 到 SVG Line
以 GridRows.tsx 为例,渲染流程为:
ticks = tickValues ?? getTicks(scale, numTicks)得到刻度值数组;- 计算偏移
scaleOffset = (offset ?? 0) + getScaleBandwidth(scale) / 2; - 对每个 tick,
y = coerceNumber(scale(d)) ?? 0 + scaleOffset,构建from/to两个Point(来自@visx/point),横跨[0, width]; - 用
@visx/shape的<Line />逐条渲染,外层包裹@visx/group的<Group className="visx-rows" />。
值得注意的两点实现细节:
- band 类 scale 的自动居中:
getScaleBandwidth(见 utils/getScaleBandwidth.ts)检测 scale 是否具有bandwidth()方法(如scaleBand),有则取带宽的一半作为偏移,让网格线落在 band 的中间而不是边缘。这样在使用scaleBand做柱状图时,网格线会与柱子中心对齐。 - scale 输出非数字的容错:
coerceNumber保证即使 scale 输出undefined(某些 scale 在 domain 外会返回 undefined),也回退为 0,避免渲染崩溃。类型层面由GridScaleOutput = number | NumberLike | undefined(见 types.ts)承接。
通用属性(CommonGridProps)速查
GridRows、GridColumns及极坐标组件共同继承CommonGridProps(定义于 types.ts):
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
className | string | — | 应用到线组<g>元素的类名 |
children | ({ lines }) => ReactNode | — | 自定义渲染函数,接收{ lines: GridLines }覆盖默认的<Line>渲染 |
top/left | number | 0 | 对网格组容器<g>的整体平移偏移(像素) |
stroke | string | '#eaf0f6' | 网格线描边颜色 |
strokeWidth | string \| number | 1 | 网格线描边粗细 |
strokeDasharray | string | — | 网格线虚线样式,如'4,4' |
numTicks | number | 10 | 网格线近似条数(d3 算法会圆整,见上文) |
lineStyle | CSSProperties | — | 应用到每条网格线的 style 样式对象 |
offset | number | — | 每条网格线的像素平移量(Rows 平移 y、Columns 平移 x),叠加在 band 居中偏移之上 |
children渲染函数的lines数据结构为GridLines(见 types.ts):数组元素包含from: { x?, y? }、to: { x?, y? }与index,你可以完全自定义网格线的渲染方式(例如换成<circle>或其他标记)。
此外,GridRowsProps/GridColumnsProps还接受tickValues?: ScaleInput<Scale>[]——传入精确刻度值数组,指定后覆盖numTicks。同时由于类型定义中AllGridRowsProps/AllGridColumnsProps继承了@visx/shape的LineProps与SVGLineElement的原生属性(见 GridRows.tsx),你还可以直接透传vectorEffect、opacity等任意 SVG 属性到每条线上。
Grid 组合组件的专属属性
<Grid />内部正是把GridRows(用yScale)与GridColumns(用xScale)组合进同一个<Group className="visx-grid">(见 Grid.tsx)。除继承通用属性外,它提供成对的前缀属性,让横、纵两组网格线可以独立定制:
| 属性 | 说明 |
|---|---|
xScale/yScale | 必填,分别映射 x(Columns)与 y(Rows)坐标 |
xOffset/yOffset | 对 Columns 线做 x 平移 / 对 Rows 线做 y 平移 |
numTicksRows/numTicksColumns | 横、纵网格线近似条数 |
rowLineStyle/columnLineStyle | 分别应用到 Rows / Columns 的 style 对象 |
rowTickValues/columnTickValues | 横、纵网格线的精确刻度值数组,优先于 numTicks* |
Grid还透传className、stroke、strokeWidth、strokeDasharray等公共属性给两组线;restProps也会一并透传给内部的 Rows 与 Columns,因此适合把vectorEffect="non-scaling-stroke"这类 SVG 属性一次性应用到全部网格线。
典型用法(让横纵网格线拥有不同样式):
<Grid xScale={xScale} yScale={yScale} width={width} height={height} stroke="#ddd" strokeWidth={1} numTicksRows={8} numTicksColumns={12} rowLineStyle={{ strokeDasharray: '4,4' }} columnLineStyle={{ strokeDasharray: '2,2' }} />极坐标网格:GridAngle、GridRadial 与 GridPolar
除直角坐标网格外,@visx/grid还提供完整的极坐标网格能力,通常用于雷达图、玫瑰图或径向条形图。
GridAngle:角度线(辐射射线)
GridAngle(见 GridAngle.tsx)根据角度 scale 从圆心向外绘制射线。除通用属性外还有:
innerRadius(默认0):射线起始半径;outerRadius(必填):射线终止半径;lineClassName:应用到所有角度线的类名。
实现上,每个 tick 的角度为scale(tick) - Math.PI / 2(把 d3 角度 scale 的起始方向对齐到 12 点钟方向),再经 utils/polarToCartesian.ts 转换为直角坐标的起点与终点,最后用<Line />渲染。
<GridAngle scale={angleScale} innerRadius={0} outerRadius={radius} numTicks={12} stroke="#ddd" />GridRadial:径向线(同心圆弧)
GridRadial(见 GridRadial.tsx)根据径向 scale 绘制同心圆弧,内部使用@visx/shape的<Arc />渲染。专属属性:
startAngle(默认0)/endAngle(默认2 * Math.PI):圆弧的起止角度(弧度);arcThickness:若指定,则每条圆弧会具有该厚度(用于填充场景),此时内半径由scale(radius - arcThickness)计算;fill(默认'transparent')/fillOpacity(默认1):圆弧填充色与不透明度;lineClassName:应用到所有径向弧线的类名。
一个实现细节:当不指定arcThickness时,内半径取Math.min(...scale.domain()),即弧线贴紧 domain 最小值,形成常见的“蛛网”式同心圆。
GridPolar:极坐标组合
GridPolar(见 GridPolar.tsx)是GridAngle+GridRadial的组合容器,专门为极坐标成对属性,例如scaleAngle/scaleRadial、innerRadius/outerRadius、numTicksAngle/numTicksRadial、tickValuesAngle/tickValuesRadial、strokeAngle/strokeRadial、strokeWidthAngle/strokeWidthRadial、strokeDasharrayAngle/strokeDasharrayRadial、lineStyleAngle/lineStyleRadial、classNameAngle/classNameRadial、lineClassNameAngle/lineClassNameRadial、fillRadial、arcThickness、startAngle/endAngle等,几乎为每个内部组件属性都提供了带Angle/Radial后缀的独立版本,便于分别定制射线与圆弧。
<GridPolar scaleAngle={angleScale} scaleRadial={radialScale} innerRadius={0} outerRadius={radius} numTicksAngle={12} numTicksRadial={5} strokeAngle="#bbb" strokeRadial="#ddd" />测试覆盖与进一步探索
该包在 test/ 目录下为每个组件都配备了单元测试(Grid.test.tsx、GridRows.test.tsx、GridColumns.test.tsx、GridAngle.test.tsx、GridRadial.test.tsx、GridPolar.test.tsx以及utils.test.ts),可在packages/visx-grid目录下通过 vitest 运行。阅读这些测试是理解各组件边界行为(如默认值、tickValues优先级、children 自定义渲染)的捷径。
若想看到网格线的实际应用,可参考同仓库的@visx/xychart(packages/visx-xychart)与@visx/chart(packages/visx-chart)等更上层封装,它们内部即基于此类基础网格组件构建。visx 的网格组件与 packages/visx-axis 的坐标轴组件配合使用,即可快速搭建出带完整坐标参考系的专业图表。
【免费下载链接】visx🐯 visx | visualization components项目地址: https://gitcode.com/gh_mirrors/vi/visx
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考