- 数据可视化
- 图表库
- 前端
【免费下载链接】react-vis
Data Visualization Components
react-vis 是一个基于 React 的数据可视化组件库,本文聚焦其核心折线图系列组件
LineSeries/LineMarkSeries,完整讲解 SVG 与 Canvas 两种渲染模式的区别、数据格式约定、全部 API 参数与交互处理器,并结合仓库源码剖析其底层实现原理,帮助你快速上手并深度定制 react-vis 折线图。
LineSeries是 react-vis 中最常用的时间序列/折线图组件,它同时提供了基于 SVG 与基于 Canvas 的两套渲染实现,并派生出叠加数据点的LineMarkSeries组合组件。读完本文,你将掌握:如何在XYPlot中声明折线图、如何通过curve平滑曲线、如何用getNull处理空数据点、如何配置虚线/实线/线宽等描边样式,以及如何利用onNearestX、onNearestXY与系列级事件处理器实现鼠标交互。
注意:本仓库中的 react-vis 已进入弃用(deprecated)状态,详见 DEPRECATED.md——该库不再接收补丁与新特性,但核心实现与 API 文档仍然完整可用,可继续作为参考与 fork 基础。
一、组件定位:一套 API,SVG 与 Canvas 双渲染
react-vis 为折线图提供了两套渲染实现,二者暴露的 API 完全一致,仅组件名不同:
- SVG 模式:直接使用
LineSeries,渲染为<path>元素,支持动画; - Canvas 模式:改用
LineSeriesCanvas,渲染到<canvas>,在大量数据点场景下性能更好,但会禁用动画(文档原文明确标注:using the Canvas version of this layer disables animation)。
在 packages/showcase/plot/line-chart.js 中给出了一个可一键切换两种模式的真实示例:用一个布尔状态决定渲染哪个组件,data、curve、strokeDasharray等属性对两个组件都适用:
const Line = useCanvas ? LineSeriesCanvas : LineSeries; <XYPlot width={300} height={300}> <Line className="first-series" data={[{x: 1, y: 3}, {x: 2, y: 5}, {x: 3, y: 15}, {x: 4, y: 12}]} /> <Line className="third-series" curve={'curveMonotoneX'} data={[{x: 1, y: 10}, {x: 2, y: 4}, {x: 3, y: 2}, {x: 4, y: 15}]} strokeDasharray={useCanvas ? [7, 3] : '7, 3'} /> </XYPlot>从源码结构看,Canvas 系列组件通过静态属性标记自己的渲染需求:line-series-canvas.js 中声明了static get requiresSVG() { return false; }与static get isCanvas() { return true; },XYPlot据此决定如何挂载与绘制该系列。
LineMarkSeries:折线 + 数据点标记
若希望在折线上同时绘制数据点,react-vis 提供了LineMarkSeries(SVG 版)与LineMarkSeriesCanvas(Canvas 版)。从 line-mark-series.js 的源码可以看到它的实现本质上是组合:内部同时渲染一个LineSeries与一个MarkSeries,并支持用lineStyle/markStyle分别控制线体与标记的样式:
<g className="rv-xy-plot__series rv-xy-plot__series--linemark"> <LineSeries {...this.props} style={{...style, ...lineStyle}} /> <MarkSeries {...this.props} style={{...style, ...markStyle}} /> </g>Canvas 版本的 line-mark-series-canvas.js 同样是对两个基础图层renderLayer的串联调用。测试 line-series.test.js 验证了LineMarkSeries渲染出的 DOM 中同时存在path与circle(.rv-xy-plot__series circle共 6 个,对应两组各 3 个数据点)。
二、数据格式参考
LineSeries接收一个对象数组data,每个对象必须包含以下字段:
x
- 类型:
number - 含义:数据点在序列中的从左到右的水平位置。
y
- 类型:
number - 含义:序列顶部边缘的垂直位置(自顶向下)。
示例:
const data = [ {x: 1, y: 3}, {x: 2, y: 5}, {x: 3, y: 15}, {x: 4, y: 12} ];在渲染时,line-series.js 通过this._getAttributeFunctor('x')与this._getAttributeFunctor('y')从XYPlot的 scale 配置中取出 x/y 的映射函数,再交给 d3-shape 的line()生成路径。这意味着 x/y 的取值最终由坐标轴 scale 决定(连续线性 scale、时间 scale 等均可,详见 scales-and-data.md)。
三、API 参考
以下参数对LineSeries、LineSeriesCanvas及LineMarkSeries系列均适用。
color(可选)
- 类型:
string|number - 默认值:见 colors.md 中的默认色板
折线的颜色。可以直接传入字面量颜色(如"red"或"#f70"),也可以在顶层(XYPlot上)定义颜色 scale 后传入一个数字,由 scale 插值出实际颜色;若什么都不传,将使用 react-vis 默认色板着色。
在源码中,stroke 的取值逻辑是stroke || color——stroke优先级更高(见下文),二者都未提供时颜色为空、由 CSS 默认样式接管。
curve(可选)
- 类型:
string|function - 默认值:
null
应用 d3-shape 库中的曲线函数来平滑折线。可以直接传函数名(字符串),也可以传入配置好的函数(如curveCatmullRom.alpha(0.5),需要自行引入 d3-shape 包):
// 仅传函数名(d3-shape 内置曲线) const stringCurveProp = <LineSeries data={data} curve={'curveMonotoneX'} .../>; // 传入配置过的函数 const configuredCurve = d3Shape.curveCatmullRom.alpha(0.5); const funcCurveProp = <LineSeries data={data} curve={configuredCurve} .../>;源码 line-series.js 的_renderLine对两种形式分别处理:字符串形式会在d3Shape命名空间中查找对应曲线;函数形式则直接作为 curve 传入 d3 的line().curve(...):
let line = d3Shape.line(); if (curve !== null) { if (typeof curve === 'string' && d3Shape[curve]) { line = line.curve(d3Shape[curve]); } else if (typeof curve === 'function') { line = line.curve(curve); } }Canvas 版 line-series-canvas.js 的曲线解析逻辑与 SVG 版完全一致。
data
- 类型:
Array<Object> - 说明:该系列的数据数组,格式见上文「数据格式参考」。若传入
data={null},组件直接返回空(源码中if (!data) return null;),测试也覆盖了这一行为。
getNull(可选)
- 类型:
function - 默认值:
null(源码 defaultProps 中实际为() => true,即默认所有点都绘制)
对每个数据元素调用并返回一个布尔值,指定该数据点是否应被绘制——等价于 d3-shape 中line.defined(...)的行为,用于跳过空值/异常点:
// 仅绘制 y 值不为 null 的数据点 <LineSeries getNull={(d) => d.y !== null} data={data} />注意:该属性的旧名称为nullAccessor,已被重命名为getNull。源码中仍兼容旧名,但会打印一条警告(nullAccessor has been renamed to getNull),建议统一使用getNull。测试 line-series.test.js 通过NullData示例验证了使用getNull后折线在空值区间断开、且 Crosshair 在空值点不显示数据的行为。
opacity(可选)
- 类型:
number - 默认值:
1
折线的不透明度,取值范围 0(完全透明)到 1(完全不透明)。源码中会先取opacity属性值,若非法(非有限数值)则回退到DEFAULT_OPACITY。
stroke(可选)
- 类型:
string|number - 默认值:见 colors.md
描边颜色。若同时提供color与stroke,stroke会覆盖color(源码取值顺序为stroke || color)。
strokeDasharray(可选)
- 类型:
string - 说明:自定义
stroke-dasharray属性,控制路径上虚线与间隙的排布模式。Canvas 版该属性需传数值数组,例如[7, 5](对应 SVG 版的字符串'7, 5')。
strokeStyle(可选)
- 类型:
string - 说明:设为
dashed时系列使用虚线,设为solid或不设置时使用实线。如需完全自定义虚线排布,用strokeDasharray。源码中内置了两组预设值(line-series.js):
const STROKE_STYLES = { dashed: '6, 2', solid: null };实际渲染时strokeDasharray的取值为STROKE_STYLES[strokeStyle] || strokeDasharray,即strokeStyle预设优先。
strokeWidth(可选)
- 类型:
string|number - 说明:折线宽度。默认由 react-vis 的 CSS 决定(2px)。Canvas 版的
defaultProps中显式设置了strokeWidth: 2。
测试中的“Line Styling”用例验证了opacity={0.5}、strokeWidth="3px"、stroke="rgb(255, 255, 255)"、strokeDasharray="3, 1"会被逐一写入path的 style 上。
style(可选)
- 类型:
object - 说明:一个包含 CSS 属性的对象,会直接应用到系列渲染出的 SVG 元素上,用于在不写样式类名与样式表的前提下做额外定制,详见 style.md。
<LineSeries data={data} style={{strokeLinejoin: "round"}} />在渲染时style对象会展开合并到path的最终 style 中(位于各专用属性之后,因此可以覆盖它们)。
四、交互处理器(Interaction Handlers)
交互难点提示:默认 2px 宽的折线在鼠标操作时可能难以精确命中。官方建议的三种对策:
- 使用邻近(proximity)处理器
onNearestX、onNearestXY——它们不需要精确悬停在线上,而是基于鼠标位置与数据点的距离自动选中最近点; - 加宽折线,使其更容易被鼠标够到;
- 额外绘制一条近透明但更宽的折线专门用来捕获鼠标事件。
下面逐一说明系列级事件处理器。它们的实现位于基类 abstract-series.js。
onNearestX(可选)
- 类型:
function(value, {event, innerX, index}) - 说明:鼠标每次移动时触发,回调参数为x 坐标与光标最接近的数据点。
value是该数据点对象;info对象包含:
innerX:数据点在绘图区内的水平位置(left position);index:数据点在data数组中的下标;event:原生事件对象。
源码_handleNearestX会遍历data,用当前鼠标的 x 与每个点的映射 x 计算绝对距离,取最小距离对应的数据点回调。
onNearestXY(可选)
- 类型:
function(value, {event, innerX, innerY, index}) - 说明:鼠标每次移动时触发,回调参数为二维距离上最接近光标的数据点。
value是该数据点对象;info对象包含:
innerX:数据点在绘图区内的水平位置;innerY:数据点在绘图区内的垂直位置(top position);index:数据点在data数组中的下标;event:原生事件对象。
源码_handleNearestXY的实现基于 d3-voronoi:用每个数据点作为节点构造 Voronoi 图,再查找鼠标坐标所在的单元,从而在 O(最近邻) 语义下找到最近点——这也解释了为什么该处理器在点稀疏、形态不规则的图上依然稳定。
onSeriesClick
- 类型:
function - 默认值:无
- 说明:用户点击该系列时触发,回调携带对应事件对象。
<LineSeries ... onSeriesClick={(event) => { // 点击系列时做点什么 // 可以通过 event 访问事件对象 }} />onSeriesMouseOut
- 类型:
function - 默认值:无
- 说明:鼠标移出该系列时触发,回调携带对应事件对象。
<LineSeries ... onSeriesMouseOut={(event) => { // 鼠标移出时做点什么 }} />onSeriesMouseOver
- 类型:
function - 默认值:无
- 说明:鼠标移入该系列时触发,回调携带对应事件对象。
<LineSeries ... onSeriesMouseOver={(event) => { // 鼠标悬停时做点什么 }} />onSeriesRightClick
- 类型:
function - 默认值:无
- 说明:用户在该系列上右键单击时触发,回调携带对应事件对象。
<LineSeries ... onSeriesRightClick={(event) => { // 右键点击系列时做点什么 }} />在 abstract-series.js 中,这四个系列级处理器分别由_seriesClickHandler、_seriesMouseOutHandler、_seriesMouseOverHandler、_seriesRightClickHandler实现,并绑定在LineSeries渲染出的<path>上(onClick、onMouseOut、onMouseOver、onContextMenu)。此外基类还提供了onValueMouseOver、onValueMouseOut、onValueClick、onValueRightClick等数据点级处理器(在LineMarkSeries场景下作用于每个标记点)。更多交互能力(Crosshair、Highlight、Hint 等)参见 interaction.md。
五、源码级实现要点
1. 路径生成:d3-shape 的line()
line-series.js 的核心渲染逻辑非常简洁——拿到 x/y 的 scale functor 后,依次应用curve、defined(getNull)、.x(x).y(y),最终生成 path 的d属性。这也决定了getNull、curve与 d3-shape 的line()是同一套语义。
2. 渲染输出结构
SVG 版输出一个path元素:
- className 为
rv-xy-plot__series rv-xy-plot__series--line(可叠加自定义className); - 通过
transform: translate(marginLeft, marginTop)对齐绘图区边距; - style 中按序合并
opacity、strokeDasharray、strokeWidth、stroke与用户style。
3. Canvas 版绘制流程
line-series-canvas.js 通过静态方法renderLayer(props, ctx)在共享 canvas 上绘制:
const strokeColor = rgb(stroke); ctx.strokeStyle = `rgba(${strokeColor.r}, ${strokeColor.g}, ${strokeColor.b}, ${opacity})`; ctx.lineWidth = strokeWidth; if (strokeDasharray) { ctx.setLineDash(strokeDasharray); } line.context(ctx)(data); ctx.stroke();值得注意的细节:
- 颜色通过
d3-color的rgb()解析并拼成带透明度的rgba()字符串; - 虚线通过
ctx.setLineDash(strokeDasharray)设置,因此 Canvas 版的strokeDasharray必须是数值数组(如[7, 3]); - 绘制结束后会将
lineWidth与setLineDash恢复默认值,避免污染后续图层的绘制; - 由于是命令式绘制,该组件自身
render()只返回一个空<div/>,真正的绘制全部发生在图层渲染阶段。
4. 默认值一览
| 参数 | 默认值 | 说明 |
|---|---|---|
opacity | 1 | 不透明度 |
curve | null | 直线连接 |
strokeStyle | 'solid' | 实线 |
getNull | () => true | 所有点均绘制 |
strokeWidth(Canvas) | 2 | 与 CSS 默认 2px 一致 |
style | {} | 空对象 |
5. 动画支持
SVG 版支持动画:当传入animation属性时,组件会包裹在Animation中并传入ANIMATED_SERIES_PROPS(见 utils/series-utils.js)。Canvas 版不支持动画,这是选择渲染模式时最重要的取舍依据。
六、测试与示例验证
仓库提供了完整的测试与可运行示例帮助你验证上述行为:
- 单元测试:packages/react-vis/tests/components/line-series.test.js 覆盖了基础渲染、
data={null}空渲染、SVG/Canvas 一键切换、LineMarkSeries组合输出、多色折线、时间序列图、多图联动、描边样式与getNull空数据场景,是理解组件行为的权威参考。 - 可运行示例:packages/showcase/plot/line-chart.js 演示了 SVG/Canvas 切换、字符串与函数两种
curve用法、strokeDasharray在两种模式下的不同写法('7, 3'vs[7, 3]),以及style中自定义虚线(注意注释提示:strokeDasharray写在style中无法迁移到 Canvas 版)。更多场景参见 line-mark-series.md、line-series-canvas 相关示例。
七、使用建议与限制总结
- 模式选择:数据点少、需要动画 → 用
LineSeries;数据量大、追求绘制性能 → 用LineSeriesCanvas(牺牲动画)。 - 空数据处理:统一使用
getNull(旧名nullAccessor已废弃);空值处折线会断开,配合MarkSeries/Crosshair 可直观呈现数据缺口。 - 交互手感:折线本身细窄,优先依赖
onNearestX/onNearestXY做邻近命中,或用透明加宽线扩大命中区域。 - 样式优先级:
stroke覆盖color;strokeStyle预设覆盖strokeDasharray;style对象最后合并、可覆盖上述所有专用属性。 - 维护状态:react-vis 已进入弃用状态(见 DEPRECATED.md),不再发布新特性;如需长期维护或深度定制,建议在 fork 基础上使用,或参考其实现思路迁移到更新的图表库。
- 数据可视化
- 图表库
- 前端
【免费下载链接】react-vis
Data Visualization Components
相关推荐
react-vis LineMarkSeries 实战指南:折线 + 标记点组合系列的数据格式、API 与交互事件
react vis LineMarkSeries 实战指南:折线 + 标记点组合系列的数据格式、API 与交互事件 LineMarkSeries 是 react
数据可视化图表库前端基于 React 的 Canvas 渲染缓存方案:@ice/cache-canvas 组件深入解析
基于 React 的 Canvas 渲染缓存方案:@ice/cache canvas 组件深入解析 @ice/cache canvas 是 ice.js 框架生
前端Web框架SSR前端构建插件系统微前端跨平台ECharts 渲染机制深度解析:Canvas 与 SVG 性能对比
ECharts 渲染机制深度解析:Canvas 与 SVG 性能对比 在数据可视化领域,选择合适的渲染技术直接影响图表性能与用户体验。ECharts 作为一款基
数据可视化图表库前端
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考