Openinula VChart
为了方便 Openinula 技术栈的同学更好得使用 VChart,我们提供了 VChart 的 Openinula 封装包:@visactor/openinula-vchart。该组件主要对 VChart 的图表部件做 Openinula 组件化的封装,相关的配置项均与 VChart 一致。
我们将在本教程中详细讲解如在 Openinula 项目中使用 VChart,并创建一个简单的柱状图。更多详细 api 文档,请查看 @visactor/openinula-vchart 仓库的文档。
快速上手
如何安装
要开始使用 Openinula VChart,首先需要在你的 Openinula 项目中安装 @visactor/openinula-vchart 包。在项目根目录下,使用以下命令安装该包:
npm install @visactor/openinula-vchart
或者使用 yarn 进行安装:
yarn add @visactor/openinula-vchart
创建图表
以创建一个简单的状图为例。在你的 Openinula 组件中,引入 visactor/openinula-vchart 的 <BarChart> 组件,并在组件中使用它们。下面是一个创建柱状图的示例代码:
import React, { useRef } from 'openinula'; import { BarChart, Bar, Legend, Axis } from '@visactor/openinula-vchart'; const App = () => { const chartRef = useRef(null); const handleChartClick = () => { console.log('图表被点击了'); }; const barData = [ { type: 'Autocracies', year: '1930', value: 129 }, { type: 'Autocracies', year: '1940', value: 133 }, { type: 'Autocracies', year: '1950', value: 130 }, { type: 'Autocracies', year: '1960', value: 126 }, { type: 'Autocracies', year: '1970', value: 117 }, { type: 'Autocracies', year: '1980', value: 114 }, { type: 'Autocracies', year: '1990', value: 111 }, { type: 'Autocracies', year: '2000', value: 89 }, { type: 'Autocracies', year: '2010', value: 80 }, { type: 'Autocracies', year: '2018', value: 80 }, { type: 'Democracies', year: '1930', value: 22 }, { type: 'Democracies', year: '1940', value: 13 }, { type: 'Democracies', year: '1950', value: 25 }, { type: 'Democracies', year: '1960', value: 29 }, { type: 'Democracies', year: '1970', value: 38 }, { type: 'Democracies', year: '1980', value: 41 }, { type: 'Democracies', year: '1990', value: 57 }, { type: 'Democracies', year: '2000', value: 87 }, { type: 'Democracies', year: '2010', value: 98 }, { type: 'Democracies', year: '2018', value: 99 } ]; return ( <div> <BarChart ref={chartRef} data={[{ id: 'id0', values: barData }]} onClick={handleChartClick}> <Bar seriesField="type" xField={['year', 'type']} yField="value" bar={{ style: { stroke: '#000', strokeWidth: 1 }, state: { hover: { stroke: 'black' } } }} /> <Axis orient="bottom" type="band" /> <Axis orient="left" max={200} type="linear" /> <Legend visible={true} position="start" orient="top" padding={{ bottom: 12 }} /> </BarChart> </div> ); }; export default App;
在这个示例中,我们创建了一个简单的柱状图,使用了一些基本的组件和配置。

组件选择
为了适应不同用户场景和使用习惯,VChart 提供了两种标签风格:
<VChart />:一个大而全的统一入口标签,封装了图表的规范、提供规范的更新、卸载逻辑。适用于 toB 页面,存在页面搭建类的产品页面,以及自行封装 VChart 的业务方迁移。<BarChart />、<LineChart />、<CommonChart />等语法化标签:适用于简单页面,开发人员手写代码。方便实现拆包按需加载。
上面的柱状图示例使用了语法化标签 <BarChart />,你可以根据需要选择适合的标签风格。接下来,我们将介绍如何根据你的需求调整图表。
图表配置及优化
在创建了一个简单的柱状图之后,你可能会需要根据项目的需求进行一些调整。VChart 提供了丰富的配置选项,以满足不同的场景。
- 图表样式:你可以通过配置
bar属性中的style来设置柱状图的样式。例如,修改柱的颜色、描边、宽度等。 - 自定义图例:通过设置
<Legend>组件的visible、position、orient等属性,你可以自定义图例的显示和位置。 - 调整坐标轴:在
<Axis>组件中,你可以设置orient、type等属性来调整坐标轴的显示,或通过修改label属性来调整轴上的标 签显示。
以下是一个根据需求调整后的柱状图:
<BarChart ref={chartRef} data={[{ id: 'id0', values: barData }]} onClick={handleChartClick}> <Bar seriesField="type" xField={['year', 'type']} yField="value" bar={{ style: { fill: 'blue', stroke: 'black', strokeWidth: 1 }, state: { hover: { fill: 'orange' } } }} /> <Axis orient="bottom" type="band" /> <Axis orient="left" max={200} type="linear" label={{ visible: true }} /> <Legend visible={true} position="start" orient="top" padding={{ bottom: 12 }} /> </BarChart>
通过调整配置,我们得到了一个更符合在实际项目中的柱状图。
统一图表标签
<VChart /> 接收的一个完整的spec作为图表定义,其spec的数据结构完全等同于 VChart 中的定义,因此开发者可以将任何对于 VChart 合法的 spec 送入 Openinula-VChart 中进行图表渲染。
Props
如果你已经有了 spec 图表描述信息,使用统一图表标签是比较快捷的方式,只需要引入VChart组件即可:
import { VChart } from '@visactor/openinula-vchart';
VChart组件即封装的 Openinula 组件,其 props 定义如下:
interface VChartProps extends EventsProps { /** 图表定义 */ spec: any; /** 图表配置 */ options?: ChartOptions; /** 图表渲染完成事件 */ onReady?: (instance: VChart, isInitial: boolean) => void; /** throw error when chart run into an error */ onError?: (err: Error) => void; /** * 切换到同步渲染 * * @since 1.8.3 **/ useSyncRender?: boolean; /** * props更 新的时候,跳过所有函数的检查,即所有的函数都认为没有更新 * * @since 1.6.5 **/ skipFunctionDiff?: boolean; }
EventsProps 的定义参考事件交互章节
onReady是一个内置的回调事件,会在图表渲染或更新时触发,其入参分 别代表图表实例对象,以及是否初次渲染。
举例来说,开发者可以在初次渲染时,将需要触发的回调事件注册在图表实例上以实现图表交互功能。
语法化标签
语法化标签是指 Openinula-VChart 将图表的图表容器以及各个组件都封装为 Openinula 组件导出给开发者,开发者可以通过更加语义化、更接近原生 Openinula 声明的方式来定义图表。需要说明的是语法化标签的定义内容,在多数场景下都是可以和图表描述 spec 进行相互转化的,本文主要的内容也是解释语法化标签与 spec 的关系。
标签的分类
目前 Openinula-VChart 共导出了三种类型的组件标签,分别是图表标签、组件标签、系列标签
图表标签
图表类型的标签,包括以下这些:
import { AreaChart, BarChart, LineChart, ScatterChart, PieChart, RoseChart, RadarChart, MapChart, HistogramChart, WordCloudChart, FunnelChart, BoxPlotChart, CircularProgressChart, LinearProgressChart, RangeColumnChart, CommonChart } from '@visactor/openinula-vchart';
一般情况下使用这些标签直接决定了图表类型。而其中比较特殊的是 CommonChart,这是一个通用图表类型标签,可以用来实现组合图、双轴图等图表类型。更多组合图说明可参考:
这些图表标签的 Props 定义为:
interface ChartComponent extends EventsProps { /** 数据 */ data?: IData; /** 画布宽度 */ width?: number; /** 画布高度 */ height?: number; /** 图表配置 */ options?: ChartOptions; /** 图表渲染完成事件 */ onReady?: (instance: VChart, isInitial: boolean) => void; }
EventsProps的定义参考事件交互章节
onReady是一个内置的回调事件,会在图表渲染或更新时触发,其入参分别代表图表实例对象,以及是否初次渲染。
组件标签
组件标签是指 VChart 图表内部的可视化组件,包括以下这些:
import { // 坐标轴组件 Axis, // 图例组件 Legend, // Brush组件 Brush, // Crosshair组件 Crosshair, // DataZoom 组件 DataZoom, // Indicator 组件 Indicator, MarkArea, MarkLine, MarkPoint, Player, ScrollBar, Title, Tooltip, // 自定义图元系列 Mark, // Region } from '@visactor/openinula-vchart';
这些组件并不 会真实的存在于 DOM 结构中,这样的写法只是为了更清晰的展示图表的结构组成。其中这些组件的配置项完全对齐 VChart 中对应组件的定义,区别在于原本的数据结构定义在此时可以作为 props 参数进行配置。
系列组件
系列组件是指和图表类型对应的图元系列所定义的组件。若指定了图表组件的类型,则应该使用与之对应的系列组件进行图元的定义,系列组件包含以下这些:
import { Area, Bar, Line, Scatter, Map, Pie, Rose, Radar, Dot, Link, CircularProgress, WordCloud, Funnel, LinearProgress, RangeColumn, BoxPlot } from '@visactor/openinula-vchart';
语法化标签的使用
首先明确语法化标签 props 定义的基本原则:语法标签的 props 与 spec 里对应组件的 api 定义是基本对等的,此外语法标签上新增了更多回调入口用于方便的进行事件挂载。
举例说明,现有折线图 spec 定义如下:
{ "type": "line", "data": [ { "id": "lineData", "values": [ { "State": "WY", "年龄段": "小于5岁", "人口数量": 25635 }, { "State": "WY", "年龄段": "5至13岁", "人口数量": 1890 }, { "State": "WY", "年龄段": "14至17岁", "人口数量": 9314 }, { "State": "DC", "年龄段": "小于5岁", "人口数量": 30352 }, { "State": "DC", "年龄段": "5至13岁", "人口数量": 20439 }, { "State": "DC", "年龄段": "14至17岁", "人口数量": 10225 }, { "State": "VT", "年龄段": "小于5岁", "人口数量": 38253 }, { "State": "VT", "年龄段": "5至13岁", "人口数量": 42538 }, { "State": "VT", "年龄段": "14至17岁", "人口数量": 15757 }, { "State": "ND", "年龄段": "小于5岁", "人口数量": 51896 }, { "State": "ND", "年龄段": "5至13岁", "人口数量": 67358 }, { "State": "ND", "年龄段": "14至17岁", "人口数量": 18794 }, { "State": "AK", "年龄段": "小于5岁", "人口数量": 72083 }, { "State": "AK", "年龄段": "5至13岁", "人口数量": 85640 }, { "State": "AK", "年龄段": "14至17岁", "人口数量": 22153 } ] } ], "xField": "State", "yField": "人口数量", "seriesField": "年龄段", "legends": { "visible": true, "orient": "top" }, "axes": [ { "type": "band", "orient": "bottom" }, { "type": "linear", "orient": "left", "label": { "style": { "fill": "#aaa", "fontSize": 14 } } } ] }
与之对应的语法化标签定义如下:
import React from 'openinula'; import { LineChart, Line, Axis, Legend } from '@visactor/openinula-vchart'; function MyChart(props) { const lineData = [ { id: 'lineData', values: [ { State: 'WY', 年龄段: '小于5岁', 人口数量: 25635 }, { State: 'WY', 年龄段: '5至13岁', 人口数量: 1890 }, { State: 'WY', 年龄段: '14至17岁', 人口数量: 9314 }, { State: 'DC', 年龄段: '小于5岁', 人口数量: 30352 }, { State: 'DC', 年龄段: '5至13岁', 人口数量: 20439 }, { State: 'DC', 年龄段: '14至17岁', 人口数量: 10225 }, { State: 'VT', 年龄段: '小于5岁', 人口数量: 38253 }, { State: 'VT', 年龄段: '5至13岁', 人口数量: 42538 }, { State: 'VT', 年龄段: '14至17岁', 人口数量: 15757 }, { State: 'ND', 年龄段: '小于5岁', 人口数量: 51896 }, { State: 'ND', 年龄段: '5至13岁', 人口数量: 67358 }, { State: 'ND', 年龄段: '14至17岁', 人口数量: 18794 }, { State: 'AK', 年龄段: '小于5岁', 人口数量: 72083 }, { State: 'AK', 年龄段: '5至13岁', 人口数量: 85640 }, { State: 'AK', 年龄段: '14至17岁', 人口数量: 22153 } ] } ]; return ( <BarChart data={barData} onClick={ev => { console.log('chart click', ev); }} > <Bar xField="State" yField="人口数量" /> <Axis orient="bottom" type="band" /> <Axis orient="left" type="linear" /> <Legend visible={true} onLegendItemClick={ev => { console.log('legend click', ev); }} /> </BarChart> ); } export default MyChart;
语法化标签未覆盖的组件
如果在使用 Openinula-VChart 时涉及到语法化标签未覆盖的组件,可以使用统一图表标签作为兜底方案。
按需加载
Openinula-VChart 本身代码都支持按需加载,当需要 VChart 按需加载的时候,建议使用 <VChartSimple /> 标签,
<VChartSimple />组件和<VChart />组件使用方法基本完全相同,唯一差异点为,需要用户从 @viasctor/vchart/esm/core 引用 VChart 构造类,并传入给 <VChartSimple />;
VChart 按需引用参考相关文档
interface VChartSimpleProps extends EventsProps { /** 图表定义 */ spec: any; /** 图表配置 */ options?: ChartOptions; /** 图表渲染完成事件 */ onReady?: (instance: VChart, isInitial: boolean) => void; /** throw error when chart run into an error */ onError?: (err: Error) => void; /** * 切换到同步渲染 * * @since 1.8.3 **/ useSyncRender?: boolean; /** * props更新的时候,跳过所有函数的检查,即所有的函数都认为没有更新 * * @since 1.6.5 **/ skipFunctionDiff?: boolean; /** * VChart构造类 * * @since 1.8.3 **/ vchartConstructor: IVChartConstructor; }
事件交互
基础事件
统一图表标签(VChart)或是语法化图表标签(BarChart 等)最外层图表组件,其 Props 上都支持底层渲染层抛出的场景树事件EventsProps。
EventsProps的定义如下:
interface EventsProps { onPointerDown?: (e: any) => void | boolean; onPointerUp?: (e: any) => void | boolean; onPointerUpOutside?: (e: any) => void | boolean; onPointerTap?: (e: any) => void | boolean; onPointerOver?: (e: any) => void | boolean; onPointerMove?: (e: any) => void | boolean; onPointerEnter?: (e: any) => void | boolean; onPointerLeave?: (e: any) => void | boolean; onPointerOut?: (e: any) => void | boolean; onMouseDown?: (e: any) => void | boolean; onMouseUp?: (e: any) => void | boolean; onMouseUpOutside?: (e: any) => void | boolean; onMouseMove?: (e: any) => void | boolean; onMouseOver?: (e: any) => void | boolean; onMouseOut?: (e: any) => void | boolean; onMouseEnter?: (e: any) => void | boolean; onMouseLeave?: (e: any) => void | boolean; onPinch?: (e: any) => void | boolean; onPinchStart?: (e: any) => void | boolean; onPinchEnd?: (e: any) => void | boolean; onPan?: (e: any) => void | boolean; onPanStart?: (e: any) => void | boolean; onPanEnd?: (e: any) => void | boolean; onDrag?: (e: any) => void | boolean; onDragStart?: (e: any) => void | boolean; onDragEnter?: (e: any) => void | boolean; onDragLeave?: (e: any) => void | boolean; onDragOver?: (e: any) => void | boolean; onDragEnd?: (e: any) => void | boolean; onRightDown?: (e: any) => void | boolean; onRightUp?: (e: any) => void | boolean; onRightUpOutside?: (e: any) => void | boolean; onTouchStart?: (e: any) => void | boolean; onTouchEnd?: (e: any) => void | boolean; onTouchEndOutside?: (e: any) => void | boolean; onTouchMove?: (e: any) => void | boolean; onTouchCancel?: (e: any) => void | boolean; onPress?: (e: any) => void | boolean; onPressUp?: (e: any) => void | boolean; onPressEnd?: (e: any) => void | boolean; onSwipe?: (e: any) => void | boolean; onDrop?: (e: any) => void | boolean; onWeel?: (e: any) => void | boolean; onClick?: (e: any) => void | boolean; onDblClick?: (e: any) => void | boolean; }
组件标签的事件
除了场景树事件,图表组件还支持了自定义事件,自定义事件可以在语义化的组件标签上进行监听,也可以在最外层的 Chart 上进行监听
<Legend />自定义事件
interface LegendEventsProps { /** 图例项hover事件 */ onLegendItemHover?: (e: any) => void | boolean; /** 图例项hover事件 */ onLegendItemUnHover?: (e: any) => void | boolean; /** 图例项点击事件 */ onLegendItemClick?: (e: any) => void | boolean; onLegendFilter?: (e: any) => void | boolean; onLegendSelectedDataChange?: (e: any) => void | boolean; }
<Brush />自定义事件
interface BrushEventsProps { /** Brush开始事件 */ onBrushStart?: (e: any) => void | boolean; /** Brush更新事件 */ onBrushChange?: (e: any) => void | boolean; /** Brush结束事件 */ onBrushEnd?: (e: any) => void | boolean; /** Brush清除事件 */ onBrushClear?: (e: any) => void | boolean; }
<DataZoom />自定义事件
interface DataZoomEventsProps { /** DataZoom 更新事件 */ onDataZoomChange?: (e: any) => void | boolean; }
<Player />自定义事件
interface PlayerEventsProps { onPlayerPlay?: (e: any) => void | boolean; onPlayerPause?: (e: any) => void | boolean; onPlayerEnd?: (e: any) => void | boolean; onPlayerChange?: (e: any) => void | boolean; onPlayerForward?: (e: any) => void | boolean; onPlayerBackward?: (e: any) => void | boolean; }
<ScrollBar />自定义事件
interface ScrollBarEventsProps { onScrollBarChange?: (e: any) => void | boolean; }
系列组件的事件
系列组件(例如Bar、Line等组件)同样继承了EventsProps事件,详情可参考上文。
其他事件
除了上述事件,下列事件都可以在 Chart 进行监听
- 自定义维度事件
interface DimensionEventsProps { onDimensionHover?: (e: any) => void | boolean; onDimensionClick?: (e: any) => void | boolean; }
- 层次数据图表事件
interface HierarchyEventsProps { onDrill?: (e: any) => void | boolean; }
- 生命周期相关事件
interface ScrollBarEventsProps { onInitialized?: (e: any) => void | boolean; onRendered?: (e: any) => void | boolean; onRenderFinished?: (e: any) => void | boolean; onAnimationFinished?: (e: any) => void | boolean; onLayoutStart?: (e: any) => void | boolean; onLayoutEnd?: (e: any) => void | boolean; }