赞
踩
为了更好得使用 G2 进行数据可视化,我们需要了解 G2 图表的组成以及相关概念。
完整的 G2 图表组成如下图所示:
每个图表通常包含两个坐标轴,在直角坐标系(笛卡尔坐标系)下,分别为 x 轴和 y 轴,在极坐标轴下,则分别由角度和半径 2 个维度构成。
每个坐标轴由坐标轴线(line)、刻度线(tickLine)、刻度文本(label)、标题(title)以及网格线(grid)组成。
查看 Axis 教程获取更多信息。
图例作为图表的辅助元素,用于标定不同的数据类型以及数据的范围,用于辅助阅读图表,帮助用户在图表中进行数据的筛选过滤。
查看 Legend 教程获取更多信息。
几何标记(Geometry),即我们所说的点、线、面这些几何图形,在 G2 中几何标记的类型决定了生成图表的类型。也就是数据被可视化后的实际表现,不同的几何标记都包含对应的图形属性。
查看 Geom 教程获取更多信息。
当鼠标悬停在某个点上时,会以提示框的形式显示当前点对应的数据的信息,比如该点的值,数据单位等。数据提示框内提示的信息还可以通过格式化函数动态指定。
查看 Tooltip 教程获取更多信息。
当需要在图表上绘制一些辅助线、辅助框或者图片时,比如增加平均值线、最高值线或者标示明显的范围区域时,可以使用辅助标记 guide。
查看 Guide 教程获取更多信息。
本节主要讲解如何创建以及配置 Chart 图表对象,主要内容包括图表容器全局样式配置、绘图区、图表宽度自适应等相关内容。
<div id="c1"></div>
实例化 Chart 对象时,绑定 dom 容器的方式有两种:
1. 传入 dom 容器的 id
const chart = new G2.Chart({
container: 'c1',
width: 1000,
height: 500
});
2. 传入 dom 容器的 html 节点对象
const chart = new G2.Chart({
container: document.getElementById('c1'),
width: 1000,
height: 500
});
注:为了兼容 G2 3.0 之前的版本,也可使用
id
属性代替container
,用法相同。
1. 图表的宽高
创建 chart 对象时,需要指定图表的宽高,通过如下方式指定:
const chart = new G2.Chart({
container: 'c1',
width: 1000,
height: 500
});
2. 图表的样式
图表样式包括画布背景、绘图区域背景以及内边框,分别对应如下属性:
background
:用于设置整个 chart 的图表背景样式,包括边框,背景色,透明度,圆角等;
plotBackground
:用于设置 chart 绘图区域的背景样式,包括边框,背景色,透明度,圆角等;
padding
:用于设置边距,用法同 CSS 中的 padding 属性相同, [上,右,下,左];
用法如下,具体详见 API 文档:
const chart = new G2.Chart({
container: 'c1',
width: 1000,
height: 500,
padding: [ 20, 20, 95, 80 ] // 上,右,下,左
});
下图展示了 G2 的图表布局:
默认情况下,G2 图表的宽度需要用户手动设置 width
参数,当需要图表跟随图表容器宽度变化时,则需要开启 forceFit
属性,默认其值为 false,开启方式如下:
const chart = new G2.Chart({
container: 'c1',
forceFit: true,
height : 400
});
此时,不需要设置 width
属性,即使设置了也不会生效。
另外还可以手动得调用自适应函数 chart.forceFit()
来响应页面变化。
chart.forceFit(); // 手动调用自适应函数
注意
:
G2 的图表是根据父容器的宽度来计算宽度,如果父容器隐藏,则会将宽度计算成 0,显示父容器时,需要调用一下 chart.forceFit()。
仅当浏览器的窗口变化时,图表会重新计算宽度,其他情况下不进行宽度的计算和自适应。
默认情况下,G2 的图表动画处于开启状态,可以通过如下两种方式关闭图表动画:
// 方式一: 设置 animate 属性
const chart = new G2.Chart({
container: 'c1',
width: 800,
height : 400,
animate: false // 关闭图表动画
});
// 方式二: 手动调用 animate 函数
chart.animate(false); // 关闭动画
单个几何标记(折线、面积图)的动画可以在 Geom 上设置
chart 对象支持两种数据载入的方式:
data
属性传入const chart = new G2.Chart({
id: 'c1',
width: 600,
height: 300,
data: [
{ x: 'a', y: 1 },
{ x: 'b', y: 2 },
...
]
});
chart.source(data)
方法,每个字段的列定义也可以在这里传入chart.source(data, {
x: {
type: 'cat'
},
y: {
min: 0
}
})
G2 支持两种格式的数据源:
JSON 数组
G2 接收的数据格式非常简单:标准的 JSON 数组,其中每个数组元素是一个标准的 JSON 对象:
Example:
const data = [
{ gender: '男', count: 40 },
{ gender: '女', count: 30 }
];
chart.source(data);
详见 DataSet 教程。
G2 更新数据的方式主要有三种:
仅仅是更新图表的数据
清理所有,重新绘制
使用 DataView 时的更新
如果需要马上更新图表,使用 chart.changeData(data)
即可
chart.changeData(newData);
view.changeData(data)
如果仅仅是更新数据,而不需要马上更新图表,可以调用 chart.source(data)
,需要更新图表时调用 chart.repaint()
chart.source(newData);
chart.guide().clear();// 清理guide
chart.repaint();
更新数据时还可以清除图表上的所有元素,重新定义图形语法,重新绘制
chart.line().position('x*y');
chart.render();
chart.clear(); // 清理所有
chart.source(newData); // 重新加载数据
chart.interval().position('x*y').color('z');
chart.render();
由于 DataSet
支持状态量 state
,一旦更改状态量,图表即一起刷新,详情查看 DataSet 教程。
自 G2 3.0 版本开始,原先内置的数据处理模块 frame 从 G2 包中抽离出来,独立成为 DataSet 包。DataSet 的目标是为数据可视化场景提供状态驱动(state driven)的、丰富而强大的数据处理能力。
术语 | 英文 | 描述 |
---|---|---|
数据集 | DataSet | 一组数据集合 |
数据视图 | DataView | 单个数据视图,目前有普通二维数据(类似一张数据库表)、树形数据、图数据和地理信息数据几种类型 |
状态量 | state | 数据集内部流转的控制数据状态的变量 |
变换 | Transform | 数据变换函数,数据视图做数据处理时使用,包括图布局、数据补全、数据过滤等等 |
连接器 | Connector | 数据接入函数,用于把某种数据源(譬如 csv)载入到某个数据视图上 |
在 G2 的 1.x 和 2.x 版本里,统计函数和数据处理是和图形语法混合在一起的。这一方面导致了不必要的隐喻,造成额外的理解成本,另一方面把数据处理模块( Frame 和 Stat )内置也限制了 G2 数据处理能力的进一步发展。
为追求更极致的体验,我们把数据处理部分从 G2 中完全抽离出来,对数据处理本身进行了进一步的抽象,扩展和优化,从而实现了一个独立的数据处理模块 DataSet。
首先我们把数据处理分为两个大的步骤:数据连接(Connector)和数据转换(Transform)。Connector 负责导入和归一化数据(譬如导入 CSV 数据,导入 GeoJSON 数据等),Transform 负责进行各种数据转换操作(譬如图布局、数据统计、数据补全等)。通过这样的分层,支持了前端社区非常全面的数据处理相关的算法和模块;其次,我们在单个数据视图(DataView)的基础上增加了数据集(DataSet)的概念,通过统一的 DataSet 管理,实现了各个数据视图之间的状态同步和交互。整个数据处理模块的架构如下图。
DataSet 支持状态量(State)可以实现多个图表之间的联动
可以通过<script>
标签引入在线资源或者本地脚本。
<!-- 引入在线资源 -->
<script src="https://unpkg.com/@antv/data-set"></script>
<!-- 引入本地脚本 -->
<script src="./data-set.js"></script>
这样,就可以在后续脚本中得到全局变量 DataSet。
<script src="https://unpkg.com/@antv/data-set"></script>
<script>
const dv = new DataSet.View();
</script>
我们提供了 DataSet 的 npm 包,可以通过下面的命令进行安装。
npm install @antv/data-set --save
安装后即可使用 import
或者 require
进行引用。
import { View } from '@antv/data-set';
const dv = new View();
DataSet 主要完成了以下功能:
源数据的解析,将 CSV, DSV, GeoJSON 转成标准的JSON,查看 Connector
加工数据,包括 filter, map, fold(补数据) 等操作,查看 Transform
统计函数,汇总统计、百分比、封箱 等统计函数,查看 Transform
特殊数据处理,包括 地理数据、矩形树图、桑基图、文字云 的数据处理,查看 Transform
如果仅仅是对数据进行加工,不需要图表联动
在G2 3.0 中使用 DataSet 的状态量(State) 可以很容易的实现图表的联动,步骤如下:
创建 DataSet 对象,指定状态量
创建 DataView 对象,在 transform 中使用状态量
创建图表,引用前面创建 DataView
改变状态量,所有 DataView 更新
// step1 创建 dataset 指定状态量 const ds = new DataSet({ state: { year: '2010' } }); // step2 创建 DataView const dv = ds.createView().source(data); dv.transform({ type: 'filter', callback(row) { return row.year === ds.state.year; } }); // step3 引用 DataView chart.source(dv); // step4 更新状态量 ds.setState('year', '2012');
注意
:
在 DataSet 创建了状态量后,默认会影响其管理的所有的 DataView, 可以通过 watchingStates
明确的指定受那些状态量影响,设置为空数组时不受状态量的影响。
所有引用了 DataSet 管理的 DataView 的图表都会受自动刷新,不需要手工刷新。
假设我们有一个 CSV 文件 population-by-age.csv
,里面的数据是美国各个州不同年龄段的人口数量,文件内容如下:
State | 小于5岁 | 5至13岁 | 14至17岁 | 18至24岁 | 25至44岁 | 45至64岁 | 65岁及以上 |
---|---|---|---|---|---|---|---|
WY | 38253 | 60890 | 29314 | 53980 | 137338 | 147279 | 65614 |
DC | 36352 | 50439 | 25225 | 75569 | 193557 | 140043 | 70648 |
VT | 32635 | 62538 | 33757 | 61679 | 155419 | 188593 | 86649 |
ND | 41896 | 67358 | 33794 | 82629 | 154913 | 166615 | 94276 |
AK | 52083 | 85640 | 42153 | 74257 | 198724 | 183159 | 50277 |
SD | 58566 | 94438 | 45305 | 82869 | 196738 | 210178 | 116100 |
… | … | … | … | … | … | … | … |
我们希望把 CSV 文件的内容载入,画一个以州为横轴,人口数量为纵轴的层叠柱状图,并且在查看某个柱子的时候,希望能看到对应某个州的对比各个年龄段人口数量的饼图。下面我们来看看应该怎么画?
Step1:创建数据集 DataSet 实例,管理 state 状态量
const ds = new DataSet({
state: {
currentState: 'WY'
}
});
Step2:为层叠柱状图创建数据视图 View 实例,装载数据
/*
* 如果不需要用到状态管理之类的功能,也可以不基于 DataSet 实例创建数据视图
* 直接用 const dv = new DataSet.View();
* 本例需要用状态量在不同的数据视图实例之间通信,所以需要有一个 DataSet 实例管理状态量
*/
$.get('/assets/data/population-by-age.csv', data => {
const dvForAll = ds
.createView('populationByAge', {
watchingStates: [], // 用空数组,使得这个实例不监听 state 变化
}) // 在 DataSet 实例下创建名为 populationByAge 的数据视图
.source(data, {
type: 'csv', // 使用 CSV 类型的 Connector 装载 data
});
});
Step3:合并人口数量列(新增"年龄段"和"人口"字段,把各个年龄段的人口数量列数据合并到这两列上)
dvForAll.transform({
type: 'fold',
fields: [ '小于5岁','5至13岁','14至17岁','18至24岁','25至44岁','45至64岁','65岁及以上' ],
key: 'age',
value: 'population'
});
Step4:为饼图创建数据视图实例,继承上一个数据视图的数据,通过状态量 currentState 过滤数据、统计不同年龄段人口占比
const dvForOneState = ds .createView('populationOfOneState') .source(dvForAll); // 从全量数据继承,写法也可以是 .source('populationByAge') dvForOneState .transform({ // 过滤数据 type: 'filter', callback(row) { return row.state === ds.state.currentState; } }) .transform({ type: 'percent', field: 'population', dimension: 'age', as: 'percent' });
Step5:最后使用 G2 绘图、绑定事件
const c1 = new G2.Chart({ id: 'c1', forceFit: true, height: 400, }); c1.source(dvForAll); c1.legend({ position: 'top', }); c1.axis('population', { label: { formatter: val => { return val / 1000000 + 'M'; } } }); c1.intervalStack() .position('state*population') .color('age') .select(true, { mode: 'single', style: { stroke: 'red', strokeWidth: 5 } }); c1.on('tooltip:change', function(evt) { const items = evt.items || []; if (items[0]) { ds.setState('currentState', items[0].title); } }); const c2 = new G2.Chart({ id: 'c2', forceFit: true, height: 300, padding: 0, }); c2.source(dvForOneState); c2.coord('theta', { radius: 0.8 // 设置饼图的大小 }); c2.legend(false); c2.intervalStack() .position('percent') .color('age') .label('age*percent',function(age, percent) { percent = (percent * 100).toFixed(2) + '%'; return age + ' ' + percent; }); c1.render(); c2.render();
效果:
度量 Scale,是数据空间到图形空间的转换桥梁,负责原始数据到 [0, 1] 区间数值的相互转换工作,从原始数据到 [0, 1] 区间的转换我们称之为归一化操作。
不同的数据类型对应不同的度量,如
连续数据类型,如 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10
一组数据,在其原始数值范围 [0, 10] 内通过度量转换至 [0, 1] 范围的数据,变成 0, 0.1, 0.2, ..., 0.9, 1
,同时通过 invert 反转,还需要度量后的数值恢复至原始值;
分类数据类型,如 ['男', '女']
这一组数据,通过度量转换后变成 [0, 1],同样时通过 invert 反转可恢复至原始值。
在 G2 中度量用于完成以下功能:
将数据转换到 [0, 1] 范围内,方便将数据映射到位置、颜色、大小等图形属性;
将归一化后的数据反转回原始值。例如 分类a
转换成 0.2,那么对应 0.2
需要反转回 分类a
;
划分数据,用于在坐标轴、图例显示数值的范围、分类等信息。
Scale 的功能非常简单易理解,但是在 G2 的数据处理流程中起着非常重要的承接作用,通过阅读 G2 数据处理流程章节,可以更好得理解度量 Scale。
度量的类型是由原始数据的值类型所决定的,所以在介绍度量的类型之前,需要了解下 G2 对数据的分类方式。
在 G2 中我们按照数值是否连续对数据进行分类:
分类(非连续)数据,又分为有序分类和无序分类;
连续数据,时间也是一种连续数据类型。
Example:
const data = [ { month: '一月', temperature: 7, city: 'tokyo' }, { month: '二月', temperature: 6.9, city: 'newYork' }, { month: '三月', temperature: 9.5, city: 'tokyo' }, { month: '四月', temperature: 14.5, city: 'tokyo' }, { month: '五月', temperature: 18.2, city: 'berlin' } ] // 指定度量(或称 列定义) chart.scale({ month: { alias: '月份' // 为属性定义别名 }, temperature: { alias: '温度' // 为属性定义别名 } });
在上述数据中,month
代表月份,temperature
代表温度,city
代表城市,其中 month
和 city
都是分类类型数据,但是不同的是 month
作为月份是有序的分类类型,而 city
是无序的分类类型,而 temperature
是连续的数值类型。
根据上述的数据分类方式,G2 提供了不同的度量类型:
数据类型 | 度量类型 |
---|---|
连续 | linear、log、pow、time |
分类(非连续) | cat、timeCat |
另外 G2 还提供了 identity
类型的度量用于数据源中 常量 数据的操作。
对于 G2 生成的所有度量对象,均拥有以下属性,这些属性均可以由用户进行配置。
{
type: {string}, // 度量的类型
range: {array}, // 数值范围区间,即度量转换的范围,默认为 [0, 1]
alias: {string}, // 为数据属性定义别名,用于图例、坐标轴、tooltip 的个性化显示
ticks: {array}, // 存储坐标轴上的刻度点文本信息
tickCount: {number}, // 坐标轴上刻度点的个数,不同的度量类型对应不同的默认值
formatter: {function}, // 回调函数,用于格式化坐标轴刻度点的文本显示,会影响数据在坐标轴、图例、tooltip 上的显示
}
默认生成度量的机制如下:
查看用户是否制定了对应字段的数据类型,查看列定义
如果没有,判断字段的第一条数据的字段类型
下面就让我们来详细了解下各个度量的类型:
连续的数据值,如这一组数据:[1, 2, 3, 4, 5],除了通用的属性外,还包含以下自有属性:
{
nice: {boolean}, // 默认为 true,用于优化数值范围,使绘制的坐标轴刻度线均匀分布。例如原始数据的范围为 [3, 97],如果 nice 为 true,那么就会将数值范围调整为 [0, 100]
min: {number}, // 定义数值范围的最小值
max: {number}, // 定义数值范围的最大值
minLimit: {number}, // 对数据的最小值的限制,无论数据中是否存在比这个值小的数据,生成的坐标点不会小于这个值
maxLimit: {number}, // 对数据的最大值的限制,无论数据中是否存在比这个值大的数据,生成的坐标点不会大于这个值
tickCount: {number}, // 定义坐标轴刻度线的条数,默认为 5
tickInterval: {number}, // 用于指定坐标轴各个刻度点的间距,为原始数据值的差值,tickCount 和 tickInterval 不可以同时声明
}
说明
:
min,max,minLimt 和 maxLimit 都会影响坐标轴上坐标点的生成
min 和 minLimt 的差别主要体现在 如果数据中的数据的范围是 10-20 对于 min: 0 来说,会生成从 0 - 20 范围的坐标点,但是对于 minLimit 来说只要保证生成的坐标点不小于 0 即可,生成的坐标点的范围可能依然在 10 - 20 之间。
max 和 maxLimit 的差别同 min 和 minLimit 类似, max 体现在坐标轴上肯定会出现 max 或者比 max 大的值,但是绝对不会出现比 maxLimit 大的值。
通过下面学生成绩的示例来说明:
const data = [
{ name: '张三', score: 53 },
{ name: '王五', score: 92 }
];
chart.source(data);
chart.point().position('name*score').color('name');
img,[object Object],
说明
我们知道学生分数的范围是 0 - 100 ,所以 50 - 90 并不满足我们的需求,我们可以限定 min,max 的范围
const data = [
{ name: '张三', score: 53 },
{ name: '王五', score: 92 }
];
chart.source(data, {
score: {
min: 0,
max: 100
}
});
chart.point().position('name*score').color('name');
说明
minLimit 和 maxLimit 主要应用的场景是生成的度量范围超出了用户定义的范围如:
chart.source(data, {
score: {
min: 0,
max: 100,
tickCount: 4
}
});
说明
:
由于此时用户设置了 tickCount: 4 为了满足用户对坐标点个数的需求,有时候会扩大数据的范围
设置了 maxLimit 后
chart.source(data, {
score: {
min: 0,
maxLimit: 100,
tickCount: 4
}
});
连续非线性的 log 类型度量,该度量会将 [1, 10, 100, 1000] 先转换成 [0, 1, 2, 3] 然后再进行归一化操作。log 类型的数据可以将非常大范围的数据映射到一个均匀的范围内。
log 度量是 linear 的子类,支持所有通用的属性和 linear 度量的属性,特有的属性如下:
{
base: {number}, // log 的基数,默认是 2
}
对于以下场景,建议将数据的度量类型指定为 log 类型:
散点图中数据的分布非常广,同时数据分散在几个区间内是,例如分布在 0 - 100, 10000 - 100000,1千万 - 1亿内,这时候适合使用 log 度量;
热力图中数据分布不均匀时也会出现只有非常高的数据点附近才有颜色,此时需要使用 log 度量,对数据进行 log 处理。
对比使用未使用 log 和使用了log 后的效果
// 数据
const data = [
{ site: '站点1', pv: 10 },
{ site: '站点2', pv: 99 },
{ site: '站点3', pv: 10000 }
];
chart.source(data, {
pv: {
type: 'log',
base: 10
}
});
连续非线性的 pow 类型度量,该度量将 [2, 4, 8, 16, 32] 先转换成 [1, 2, 3, 4, 5] 然后再进行归一化操作。
pow 类型的度量也是 linear 类型的一个子类,除了支持所有通用的属性和 linear 度量的属性外也有自己的属性:
{
exponent: {number}, // 指数,默认是 2
}
连续的时间类型,是一种特殊的连续性数据。time 类型的度量也是 linear 的子类,除了支持所有通用的属性和 linear 度量的属性外,还有自己特殊的属性:
{
mask: {string}, // 指定时间的显示格式,默认:'YYYY-MM-DD'
}
目前 G2 会自动识别如下形式的时间格式,当用户需要生成 time 类型的度量时,建议将原始时间数据转换为如下形式:
时间戳,如 1436237115500;
时间字符串: ‘2015-03-01’,‘2015-03-01 12:01:40’,‘2015/01/05’,‘2015-03-01T16:00:00.000Z’。
分类类型数据的度量。除了拥有通用的度量属性外,用户还可以设置 values
属性:
{
values: {array}, // 指定当前字段的分类值
}
G2 在生成 cat 类型的度量时,values
属性的值一般都会从原始数据源中直接获取,但对于下面两种场景,需要用户手动指定 values 值:
const data = [
{ a: 'a1', b:'b1', type: '最小' },
{ a: 'a2', b:'b2', type: '最大' },
{ a: 'a3', b:'b3', type: '适中' }
];
chart.scale('type', {
type: 'cat',
values: [ '最小', '适中', '最大' ]
});
如果不声明度量的values字段,那么默认的顺序是:‘最小’,‘最大’,‘适中’。
Example:
const data = [
{ a: 'a1', b:'b1', type: 0 },
{ a: 'a2', b:'b2', type: 2 },
{ a: 'a3', b:'b3', type: 1 }
]
chart.scale('type', {
type: 'cat',
values: [ '最小', '适中', '最大' ]
});
此处必须指定 ‘cat’ 类型,values 的值必须按照索引跟枚举类型一一对应。
timeCat 度量对应时间数据,但是不是连续的时间类型,而是有序的分类数据。例如股票交易的日期,此时如果使用 time 类型,那么由于节假日没有数据,折线图、k 线图就会发生断裂,所以此时需要使用 timeCat 类型度量将日期转换为有序的分类数据,该度量默认会对数据做排序。
timeCat 是 cat 度量的子类,除了支持所有通用的属性和 cat 度量的属性外也有自己的属性:
{
mask: {string}, // 指定时间的显示格式,默认:'YYYY-MM-DD'
}
timeCat 和 time 类型度量的差别和应用场景
timeCat 是分类类型的度量,所以适合于显示 柱状图
或者固定时间内没有数据的场景(股票图)
time 是连续类型的度量,更适合显示折线图、面积图,表现数据的趋势
所谓的列定义,即是对度量 scale 的操作。
G2 默认提供了一套生成度量的机制,但是这套机制并不能满足全部的需求,因此我们为用户提供了手动指定度量类型的方法,以满足多样的可视化需求,这种使用方式我们称之为 列定义。
说明:列定义上的操作可以理解为直接修改数据源中的数据属性,因此它会影响坐标轴、tooltip 提示信息、图例、辅助元素 guide 以及几何标记的标签文本 label 的数据内容显示。
G2 提供了两种列定义操作方式:
chart.source(data, defs);
数据源载入时定义。
chart.scale('field', defs);
该方法会覆盖 chart.source()
中定义的对应字段的列定义。
这种方式可以一次性为多个数据列进行度量类型的定义。
Example:
const defs = {
'a': {
type: 'time', // 指定 time 类型
mask: 'YYYY-MM-DD HH:mm:ss' // 指定时间的输出格式
},
'b': {
type: 'linear', // 指定 linear 连续类型
min: 0 // 指定度量的最小值
},
'c': {
type: 'cat', // 指定 cat 分类类型
values: [ '一月', '二月', '三月' ] // 重新指定 c 属性每一个的值
}
};
chart.source(data, defs);
该方法有两种使用方式:
Example:
const data = [
{ type: 0, value: 1 },
{ type: 1, value: 2 },
{ type: 2, value: 3 }
];
chart.scale('type', {
type: 'cat', // 声明 type 字段为分类类型
values: [ 'A', 'B', 'C' ], // 重新显示的值
alias: '类型' // 设置属性的别名
});
Example:
const data = [
{ type: 0, value: 1 },
{ type: 1, value: 2 },
{ type: 2, value: 3 }
];
chart.scale({
type: {
type: 'cat', // 声明 type 字段为分类类型
values: [ 'A', 'B', 'C' ], // 重新显示的值
alias: '类型' // 设置属性的别名
},
value: {
nice: false
}
});
该实例为 x 轴和 y 轴的两个数据字段都进行了列定义,将 x 轴的数据指定为 time 类型并指定了时间的显示格式,而 y 轴格式化了显示形式,为其加上了单位 ‘k’,观察图表,tooltip 和 坐标轴都受了列定义的影响。
完整代码:
const data = [ { value: 10, time: '2015-03-01T16:00:00.000Z' }, { value: 15, time: '2015-03-01T16:10:00.000Z' }, { value: 26, time: '2015-03-01T16:20:00.000Z' }, { value: 9, time: '2015-03-01T16:30:00.000Z' }, { value: 12, time: '2015-03-01T16:40:00.000Z' }, { value: 23, time: '2015-03-01T16:50:00.000Z' }, { value: 18, time: '2015-03-01T17:00:00.000Z' }, { value: 21, time: '2015-03-01T17:10:00.000Z' }, { value: 22, time: '2015-03-01T17:20:00.000Z' } ]; const chart = new G2.Chart({ container : 'c1', forceFit: true, height : 300 }); chart.source(data, { 'time': { type: 'time', nice: false, mask: 'HH:mm' }, 'value': { formatter: val => { return val + ' k'; } } }); chart.line().position('time*value').size(2); chart.render();
即我们所说的点、线、面这些几何图形。G2 中并没有特定的图表类型(柱状图、散点图、折线图等)的概念,用户可以单独绘制某一种类型的图表,如饼图,也可以绘制混合图表,比如折线图和柱状图的组合。
G2 生成的图表的类型,都是由几何标记决定的。可以通过下图直观得理解什么是几何标记:
创建好 chart 对象之后,就可以通过如下方式选择几何标记的类型:
const geom = chart.point().xx().xx(); // 这里使用了 point 类型的 geom,该方法会返回 geom 对象
目前 G2 支持的几何标记的类型如下:
geom 类型 | 描述 |
---|---|
point | 点,用于绘制各种点图。 |
path | 路径,无序的点连接而成的一条线,常用于路径图的绘制。 |
line | 线,点按照 x 轴连接成一条线,构成线图。 |
area | 填充线图跟坐标系之间构成区域图,也可以指定上下范围。 |
interval | 使用矩形或者弧形,用面积来表示大小关系的图形,一般构成柱状图、饼图等图表。 |
polygon | 多边形,可以用于构建色块图、地图等图表类型。 |
edge | 两个点之间的链接,用于构建树图和关系图中的边、流程图中的连接线。 |
schema | 自定义图形,用于构建箱型图(或者称箱须图)、蜡烛图(或者称 K 线图、股票图)等图表。 |
heatmap | 用于热力图的绘制。 |
虽然 G2 没有特定的图表类型概念,但是仍基本支持所有传统图表类型的绘制。
下表展示了 G2 中的 geom 几何标记类型和传统图表的对应关系,更多的图表详见 G2 官网的 demo。
geom 类型 | 图表类型 | 备注 |
---|---|---|
point | 点图、折线图中的点 | 点的形状有很多,也可以使用图片代表点(气泡图),同时点也可以在不同坐标系下显示,所以可以扩展出非常多的图表类型。 |
path | 路径图,地图上的路径 | 路径图是无序的线图。 |
line | 折线图、曲线图、阶梯线图 | 在极坐标系下可以转换成雷达图。 |
area | 区域图(面积图)、层叠区域图、区间区域图 | 极坐标系下可用于绘制雷达区域图。 |
interval | 柱状图、直方图、南丁格尔玫瑰图、饼图、条形环图(玉缺图)、漏斗图等 | 通过坐标系的转置、变化,可以生成各种常见的图表类型;所有的图表都可以进行层叠、分组。 |
polygon | 色块图(像素图)、热力图、地图 | 多个点可以构成多边形。 |
schema | k 线图,箱型图 | 自定义的图表类型。 |
edge | 树图、流程图、关系图 | 与点一起构建关系图。 |
heatmap | 热力图 | – |
几何标记 geom 对象方法主要有两种:
图形属性(attr)方法:用户设置数据到视觉通道的映射,详细信息查看 图形属性
属性方法之外的方法
具体 API 详见文档
使用几何标记实现各种图表类型时,对于每一种几何标记来说,在绘制的时候有不同的形状(shape),视觉通道跟图形属性的映射方式不一样也会生成不同的图形:
点图,可以使用圆点、三角形、正方形、十字符号等表示点
线图,可以有折线、曲线、点线等
多边形,可以是实心的多边形,也可以是空心的仅有边框的多边形
下面提供了 G2 中各个 geom 内置提供的 shape 类型,在后续图形属性章节,会详细介绍 shape 的使用方法。
geom 类型 | shape 类型 | 解释 |
---|---|---|
point | ‘circle’,‘square’,‘bowtie’,‘diamond’, ‘hexagon’,‘triangle’,‘triangle-down’,‘hollowCircle’, ‘hollowSquare’,‘hollowBowtie’, ‘hollowDiamond’,‘hollowHexagon’, ‘hollowTriangle’,‘hollowTriangle-down’,‘cross’,‘tick’, ‘plus’,‘hyphen’,‘line’ | hollow开头的图形都是空心的 |
line | ‘line’,‘smooth’,‘dot’,‘dash’, ‘dotSmooth’,‘spline’ | dot :点线,smooth: 平滑线 |
area | ‘area’,‘smooth’,‘line’,‘dotLine’, ‘smoothLine’,‘dotSmoothLine’ | [area]和[smooth]是填充内容的区域图,其他图表是空心的线图 |
interval | ‘rect’,‘hollowRect’,‘line’, ‘tick’,‘stroke’,‘funnel’, ‘pyramid’ | [hollowRect]是空心的矩形, [line]和 [tick] 都是线段,stroke:带边框的矩形, ‘funnel’ 漏斗图;‘pyramid’ 金字塔图 |
polygon | ‘polygon’,‘hollow’,‘stroke’ | polygon:多边形、hollow:空心多边形和 stroke:带边框的多边形 |
schema | ‘box’,‘candle’ | 目前仅支持箱须图、K线图 |
edge | ‘line’,‘vhv’,‘smooth’,‘arc’ | vhv:直角折线,arc:弧线,分为笛卡尔坐标系、极坐标系、带权重和不带权重四种情况。 |
以折线图为例:
chart.line().position('x*y').shape('type', ['line', 'dot']);
chart.line().position('x*y').shape('type', function(type) {
if(type === 'a') {
return 'line';
} else {
return 'dot';
}
});
如果上面各种几何标记的图形形状没法满足你需求的话,可以进行 自定义shape
图形属性对应视觉编码中的视觉通道,是 G2 语法元素非常重要和灵活的一部分,不同的几何标记拥有自己的图形属性。G2 中支持的图形属性有下面几种:
position:位置,二维坐标系内映射至 x 轴、y 轴;
color:颜色,包含了色调、饱和度和亮度;
size:大小,不同的几何标记对大小的定义有差异;
shape:形状,几何标记的形状决定了某个具体图表类型的表现形式,例如点图,可以使用圆点、三角形、图片表示;线图可以有折线、曲线、点线等表现形式;
opacity:透明度,图形的透明度,这个属性从某种意义上来说可以使用颜色代替,需要使用 ‘rgba’ 的形式,所以在 G2 中我们独立出来。
在 G2 中,我们这样定义图形属性的映射语法。
首先需要明确一点:图形属性是属于每一个几何标记 geom(Geometry) 的,所以我们先要声明几何标记,然后再在该几何标记对象上进行图形属性的映射,代码如下:
chart.<geomType>().<attrType>(fields[, callback]);
其中:
geomType,几何标记类型,具体支持的类型请阅读几何标记章节;
attrType,图形属性类型,对应视觉通道;
fields,参与单个视觉通道映射的字段,可以是单个字段也可以是多个字段,多个字段使用 *
分割
callback,回调函数,用于定义如何解析视觉通道,如不提供则只用 G2 默认提供的视觉通道解析方式。
除了 attr(fields[, callback])
的函数原型外,G2 为了用户使用的便利性,结合各个视觉通道的特点,还提供了更为便捷的使用方式,在本章后面会进行详细的介绍。
语法示例:
chart.point().position('a*b').color('c');
chart.interval().position('a*b').color('c', (cValue) => {
if (cvalue === 'fail') {
return 'red';
}
return 'green';
});
G2 对于每个图形属性的参数 fields 的解析规则如下:
如果是单个单词,如 color('a')
会判断该属性是否是输入数据源的字段属性,如果不是则会将其解析为一个常量;
如果是多个属性的映射,需要使用 *
进行连接,G2 会依次对这些字段进行解析和映射,如 position('cut*price')
;
position 位置属性的映射,用于确定由数据中的哪几个字段来确定数据在平面坐标系的位置。通俗地解释,即确定 x 轴和 y 轴的数据字段。它是唯一一个可以用于编码分类又可用于编码定序或者定量的数据属性。
以下面的语句为例,在 position 属性上,映射了两个属性: cut 和 price,分别表示将 cut 数据值映射至 x 轴坐标点,price 数据值映射至 y 轴坐标点。
chart.point().position('cut*price');
下面是对 ‘*’ 连接符的解释:
以 chart.point().position('x*y')
为例,point 代表图形,即最后需要生成点图,而 position 代表位置,position('x*y')
代表数据在图形中的位置由 x 和 y 这两个维度的变量决定,x * y 的数据处理结果可以理解为:
(x1, y1) 这样的数值对,最后就会被转换为画布上对应的坐标点。
从可视化编码的角度对颜色进行分析,可以将颜色分为亮度、饱和度和色调三个视觉通道,其中前两个可以认为是用于编码定量和定序数据的视觉通道,而色调属于编码定性数据的视觉通道。而在 G2 中并不如此详细区分,统一使用 color 方法进行映射配置。
color 支持的映射语法如下:
color('field')
,field 为数据属性,这时候 G2 会在内部调用默认的回调函数,读取默认提供的颜色进行数据值到颜色值的映射;
color('field', colors)
,将数据值映射至指定的颜色值 colors(可以是字符串也可以是数组),此时用于通常映射分类数据;
color('field', 'color1-color2-colorN')
,指定颜色的渐变路径,用于映射连续的数据;
color('field', callback)
,使用回调函数进行颜色值的自定义;可以使用多个字段使用*号连接
color('#ffffff')
, 直接指定颜色常量,不进行数据映射。
将 city
属性的数据值映射至制定的颜色来区分不同的城市。
.color('city', [ '#1f77b4', '#ff7f0e', '#2ca02c' ])
完整的代码如下:
$.getJSON('/assets/data/avg-temp.json', function(data) { const ds = new DataSet(); const dv = ds.createView().source(data); dv.transform({ type: 'fold', fields: [ 'New York', 'San Francisco', 'Austin' ], key: 'city', value: 'value' }); const chart = new G2.Chart({ container: 'c1', forceFit: true, height : 400, padding: [ 20, 120, 80, 80 ] }); chart.source(dv, { date: { type: 'time', mask: 'YYYY.MM', tickCount: 12 }, value: { alias: 'Temperature, ºF' } }); chart.axis('date', { line: null, tickLine: { stroke: '#000', length: 6 // 刻度线长度 } }); chart.axis('value', { tickLine: { stroke: '#000', length: 6 // 刻度线长度 }, label: { textStyle: { fill: '#000' } }, line: { stroke: '#000' }, grid: null }); chart.line() .position('date*value') .color('city', [ '#1f77b4', '#ff7f0e', '#2ca02c' ]) .shape('spline') .size(2); chart.render(); });
对于连续的数据,我们可以为 color 指定颜色渐变的路径,以可视化数据在某一范围的变化趋势。
.color('Population', '#e5f5e0-#31a354')
完整代码如下:
$.getJSON('/assets/data/usa.geo.json', function(mapData) { const chart = new G2.Chart({ container: 'c2', width: 800, height: 400, padding: [ 40, 140 ] }); chart.legend(false); chart.axis(false); chart.tooltip({ showTitle: false }); // 同步度量 chart.scale({ longitude: { sync: true }, latitude: { sync: true } }); // 绘制地图背景 const ds = new DataSet(); const bgDataView = ds.createView('back') .source(mapData, { type: 'GeoJSON' }); const bgView = chart.view(); bgView.source(bgDataView); bgView.polygon().position('longitude*latitude') .color('#e6e6e6') .active(false) .tooltip(false) .style({ stroke: '#999', lineWidth: 1 }); $.getJSON('/assets/data/2014-usa-population.json', function(data) { // 绘制 choropleth map const view = chart.view(); const userPolygonDv = ds.createView() .source(data) .transform({ geoDataView: bgDataView, field: 'State', type: 'geo.region', as: [ 'longitude', 'latitude' ] }); view.source(userPolygonDv); view.polygon() .position('longitude*latitude') .color('Population','#e5f5e0-#31a354') .tooltip('State*Population'); // 绘制文字 const textView = chart.view(); const centerDv = ds.createView() .source(data) .transform({ geoDataView: bgDataView, field: 'State', type: 'geo.centroid', as: [ 'longitude', 'latitude' ] }); textView.source(centerDv); textView.point() .position('longitude*latitude') .size(0) .label('code', { offset: 0, textStyle: { fontSize: 10 } }); chart.render(); }); });
有时候颜色需要根据字段值进行特殊的指定,所以 G2 提供了回调函数来指定图形的颜色。
// 根据单个字段计算颜色 chart.point().position('x*y').color('z', z => { if (z >= 100) { return 'red'; } return 'blue'; }); // 根据多个字段计算颜色 chart.point().position('x*y').color('level*value', (level, value) => { if (level < 2) { if (value > 10) { return 'green'; } return 'blue'; } else { if (value > 20) { return '#cdcdcd'; } return 'red'; } });
注意:
color 属性的回调函数一般返回的单个颜色,因为 G2 中所有的 shape 仅支持单个颜色
color 属性的回调函数也可以返回数组,数组中有多个颜色,但是这时候需要 shape 支持多颜色的解析,详细情况查看 自定义shape。
不同的几何标记有不同的 shape(图形形状)。shape 这个视觉通道受其他几个视觉通道影响,比如:interval 几何标记的 shape 可以是填充的矩形 rect 也可是空心的边框矩形,这个就决定了是将 color 映射到填充色上还是映射到边框颜色上。shape 方法的使用方式比较简单,常用于映射分类数据:
shape(‘field’),将指定的字段映射到内置的 shapes 数组中;
shape(‘field’, shapes),用户自己提供 shapes 数据,来进行数据映射;
shape(‘fields’, callback),使用回调函数获取 shape,用于个性化的 shape 定制,可以根据单个或者多个字段确定;
shape(‘circle’),指定常量,将所有数据值映射到固定的 shape。
另外 G2 提供了自定义 shape 的功能,用户可以自己绘制需要的 shape,详见如何快速地自定义 shape。
使用几何标记实现各种图表类型时,对于每一种几何标记来说,图形在绘制的时候有不同的形状(shape),在几何标记 章节已列出了目前 G2 提供的 geom 默认支持的 shape。
.shape('type', [ 'circle', 'triangle-down', 'square', 'diamond' ])
完整代码:
$.getJSON('/assets/data/series-scatter.json', function(data) { const chart = new G2.Chart({ container: 'c3', forceFit: true, height: 400 }); chart.source(data, { x: { tickInterval: 500 } }); chart.point() .position('x*y') .color('type') .shape('type', [ 'circle', 'triangle-down', 'square', 'diamond' ]) .opacity(0.65) .size(7); // 添加辅助元素 chart.guide().text({ position: [ 250, 550 ], content: '0 - 500', style: { fontSize: 14, textAlign: 'center' } }); chart.guide().text({ position: [ 1000, 550 ], content: '500 - 1500', style: { fontSize: 14, textAlign: 'center' } }); chart.guide().text({ position: [ 1700, 550 ], content: '1500 - 2000', style: { fontSize: 14, textAlign: 'center' } }); chart.guide().region({ start: [ 0, -600 ], end: [ 500, 600 ] }); chart.guide().region({ start: [ 500, -600 ], end: [ 1500, 600 ], style: { fillOpacity: 0.2 } }); chart.guide().region({ start: [ 1500, -600 ], end: [ 2000, 600 ], style: { fillOpacity: 0.3 } }); chart.render(); });
shape 也可以通过字段值来计算,可以在 shape 方法中指定单个或者多个字段,通过回调函数返回指定的 shape。
chart.point() .position('x*y') .shape('value', (value) => { if (value > 10) { return 'circle'; } return 'rect'; }); // 根据是否有子节点和节点是否展开确定shape chart.point() .position('x*y') .shape('children*collapsed', (children, collapsed) => { if (children) { return collapsed ? 'collapsed-node' : 'expand-node'; } return 'leaf'; });
shape 的回调函数中也可以返回数组,G2 根据数组的第一个值来确定 shape,其他值可以作为自定义 shape 的参数,详情查看自定义shape
chart.point().position('name*value')
.size('value')
.color('name')
.shape('url', url => {
return [ 'image', url ]; // 根据具体的字段指定 shape
});
查看自定义气泡的示例
对于不同的几何标记含义不完全一致:
对于 point 点来说,size 对应着点的半径;
对于 line 线来说,size 对应着线的粗细;
对于 interval 柱状图来说,size 对应着柱子的宽度。
所以从可视化的角度分析,大小(size)是一个复杂的视觉通道。
在 G2 中,支持如下几种方式的映射语法:
size(‘field’),指定映射到 size 的字段,使用内置的默认大小范围为 [1, 10];
size(‘field’, [ min, max ]),指定映射到 size 字段外,还提供了 size 的最大值和最小值范围;
size(‘fields’, callback),使用回调函数映射 size,用于个性化的 size 定制,可以使用多个字段进行映射;
size(10) 直接指定像素大小。
在气泡图中,常常使用 size 图形属性映射,用于编码更多维度的数据。如下例,使用气泡图来可视化每个国家人均国内生产总值同人均寿命之间的相关关系,同时将各个国家人口数据映射至气泡的大小。
.size('Population', [ 5, 35 ])
完整代码:
$.getJSON('/assets/data/bubble-population.json', function(data) { const chart = new G2.Chart({ container: 'c4', forceFit: true, height: 400 }); chart.source(data, { 'LifeExpectancy': { alias: '人均寿命(年)' }, 'Population': { type: 'pow', alias: '人口总数' }, 'GDP': { alias: '人均国内生产总值($)', tickCount: 10 }, 'Country': { alias: '国家/地区' } }); chart.axis('GDP', { label: { // 格式化坐标轴的显示 formatter: value => { return (value / 1000).toFixed(0) + 'k'; } } }); chart.tooltip({ showTitle: false // 不显示默认标题 }); chart.legend('Population', false); chart.legend('Country', false); chart.point().position('GDP*LifeExpectancy') .size('Population', [ 5, 35 ]) .color('continent') .opacity(0.65) .shape('circle') .tooltip('Country*Population*GDP*LifeExpectancy'); chart.render(); });
size可以根据数据的字段值通过回调函数计算,可以指定多个字段
chart.point().position('x*y').size('z', z => {
if (z > 10) {
return 20;
}
return z * 0.5;
});
chart.point().position('x*y').size('level*text', (level, text) => {
if (level === 0) {
return 50;
}
return text.length * 10; // 根据文本长度返回长度
});
size 函数可以返回一个数组,特别对应自定义shape时需要计算宽高的图形。
透明度在视觉编码过程中,只能进行定量(连续)数据的映射,作为颜色的一个补充使用,所以提供以下快捷方式:
opacity('field')
,指定透明度映射的字段,透明度默认的范围为 [0, 1];
opacity(0.5)
,直接指定透明度常量;
opacity('field', callback)
,使用回调函数获取透明度。
除了上面指定的视觉通道外,G2 3.0 还在 geom.style() 方法中允许用户使用回调函数,可以对任何图形属性进行映射,但是不会自动生成图例
前面提到过,每种几何标记支持的视觉通道有所差异,数据和视觉通道的映射关系也不完全相同。
下表列出了各个 geom 几何标记对各个图形属性的支持情况:
几何标记 | position | color | size | shape | opacity |
---|---|---|---|---|---|
point | 支持 | 支持 | 支持 | 支持 | 支持 |
path、line | 支持 | 支持 | 支持 | 支持 | 支持 |
area | 支持 | 支持 | 不支持 | 支持 | 支持 |
interval | 支持 | 支持 | 支持 | 支持 | 支持 |
polygon | 支持 | 支持 | 不支持 | 支持 | 支持 |
edge | 支持 | 支持 | 支持 | 支持 | 支持 |
schema | 支持 | 支持 | 支持 | 支持 | 支持 |
contour | 支持 | 支持 | 支持 | 不支持 | 支持 |
heatmap | 支持 | 支持 | 支持 | 不支持 | 不支持 |
坐标系是将两种位置标度结合在一起组成的 2 维定位系统,描述了数据是如何映射到图形所在的平面。
G2 包含了两种类型坐标系(polar、theta、helix 均属于极坐标),目前所有的坐标系均是 2 维的。
G2 默认提供的坐标系类型为笛卡尔坐标系,当不满足用户需求时,可以通过调用下面的语法声明需要使用的坐标系:
chart.coord('coordType'[, cfg]);
coordType | 说明 |
---|---|
rect | 直角坐标系,目前仅支持二维,由 x, y 两个互相垂直的坐标轴构成。 |
polar | 极坐标系,由角度和半径 2 个维度构成。 |
theta | 一种特殊的极坐标系,半径长度固定,仅仅将数据映射到角度,常用于饼图的绘制。 |
helix | 螺旋坐标系,基于阿基米德螺旋线。 |
坐标系可以分为笛卡尔坐标系和非笛卡尔坐标系,非笛卡尔坐标系即极坐标(helix 螺旋坐标系也是极坐标的一种),由角度和半径这两个维度来确定位置。
利用极坐标可生成饼图、玫瑰图和雷达图等,常被用于周期性数据,比如时间和方向数据。
坐标系类型的变换会改变几何标记的形状:在极坐标系中,矩形将变为圆环的一部分,而地图中两点间的最短路径也将不是直线。
例如下图展示的层叠柱状图,在不同坐标系下就变换成了其他的图表类型:
上图左侧为层叠柱状图,中间的饼图则是层叠柱状图在极坐标下对 x y 两个坐标轴进行转置后的结果,其中 x 轴被映射为半径,y 轴被映射成了角度。而最右边的牛眼图则相反,y 轴映射为半径。
极坐标均支持 startAngle
和 endAngle
这两个属性配置。
(1)对于 polar
和 theta
这两种坐标系类型,可以进行如下属性的配置:
chart.coord('polar' || 'theta' || 'helix', {
startAngle: 弧度, // 起始弧度
endAngle: 弧度, // 结束弧度
innerRadius: 0 至 1 范围的数值, // 用于空心部分的半径设置
radius: 0 至 1 范围的数值 // 实心圆的半径大小设置
});
效果如图所示:
chart.coord('theta', { innerRadius: 0.5 });
chart.coord('polar', {
radius: 0.5,
startAngle: -Math.PI / 6,
endAngle: 7 * Math.PI /6
});
chart.coord('helix', {
startAngle: 0.5 * Math.PI,
endAngle: 12.5 * Math.PI,
radius: 0.8
});
这里需要说明的是,G2 极坐标默认的起始角度和结束角度如下图所示:
G2 提供的坐标系支持一系列的变换操作:
rotate: 旋转,默认按照坐标系中心旋转;
scale: 放大、缩小,默认按照坐标系中心放大、缩小;
chart.coord('rect').scale(0.7, 1.2);
以下是 G2 中坐标系变换的使用语法,当需要进行多种坐标系变换时,可以直接进行链式调用,如下代码所示:
chart.coord().rotate(90).scale(1.3, 5).reflect('x').transpose();
G2 的图表坐标轴配置方法如下:
chart.axis(field, {
title: null // 不展示标题
// ...
});
参数 field 为对应的数据维度。
使用 G2 对坐标轴进行配置之前,需要了解 G2 的坐标轴的组成。
G2 生成的坐标轴由如下五部分组成:
标题 title
坐标轴线 line
文本 label
刻度线 tickLine
网格 grid
通常的图表都有 x 轴和 y 轴,默认情况下,x 轴显示在图表的底部,y 轴显示在左侧(多个 y 轴时可以是显示在左右两侧)。通过为坐标轴配置 position
属性可以改变坐标轴的显示位置,具体可详见 api。
默认情况下,我们会为每条坐标轴生成标题,标题名默认为该轴对应数据字段的属性名。通过如下代码,用户可以配置标题的显示样式或者关闭标题显示。在G2 3.0 中由于大多数场景下用户不显示 title 所以我们默认关闭了 title 的显示。
chart.axis('xField', { title: null // 不展示 xField 对应坐标轴的标题 }); chart.axis('xField', { title: {} // 展示 xField 对应坐标轴的标题 }); chart.axis('xField', { title: { textStyle: { fontSize: 12, // 文本大小 textAlign: 'center', // 文本对齐方式 fill: '#999', // 文本颜色 // ... } } });
当需要为坐标轴设置别名时,需要在列定义中为对应数据字段设置 alias
属性,如下所示,更多关于列定义的内容请查看列定义。
chart.scale('xField', {
alias: '这里设置标题的别名'
});
更多关于坐标轴 title 属性的配置请查看API文档相关内容 axis 的 title 属性配置。
在 line
属性上进行坐标轴线的配置。
chart.axis('xField', {
line: {
lineWidth: 2, // 设置线的宽度
stroke: 'red', // 设置线的颜色
lineDash: [ 3, 3 ] // 设置虚线样式
}
});
上述代码效果如下图所示:
chart.axis('xField', {
label: null
});
chart.axis('xField', {
label: {
offset: {number}, // 设置坐标轴文本 label 距离坐标轴线的距离
textStyle: {
textAlign: 'center', // 文本对齐方向,可取值为: start middle end
fill: '#404040', // 文本的颜色
fontSize: '12', // 文本大小
fontWeight: 'bold', // 文本粗细
rotate: 30,
textBaseline: 'top' // 文本基准线,可取 top middle bottom,默认为middle
} || {function}, // 文本样式,支持回调
autoRotate: {boolean} // 是否需要自动旋转,默认为 true
}
});
为 formatter
属性定义回调函数,如下所示:
chart.axis('xField', {
label: {
// 使用 formatter 回调函数
formatter: val => {
return val + '元';
}
}
});
在坐标轴上配置 formatter 仅在坐标轴上的文本有效,如果想要使得 tooltip 和图例上的信息也格式化,需要在列定义时指定格式化函数
chart.source(data, {
xField: {
formatter: val => {
return val + '元';
}
}
});
// 或者
chart.scale('xField', {
formatter: val => {
return val + '元';
}
});
const data = [ { x: 95, y: 95, z: 13.8, name: 'BE', country: 'Belgium' }, { x: 86.5, y: 102.9, z: 14.7, name: 'DE', country: 'Germany' }, { x: 80.8, y: 91.5, z: 15.8, name: 'FI', country: 'Finland' }, { x: 80.4, y: 102.5, z: 12, name: 'NL', country: 'Netherlands' }, { x: 80.3, y: 86.1, z: 11.8, name: 'SE', country: 'Sweden' }, { x: 78.4, y: 70.1, z: 16.6, name: 'ES', country: 'Spain' }, { x: 74.2, y: 68.5, z: 14.5, name: 'FR', country: 'France' }, { x: 73.5, y: 83.1, z: 10, name: 'NO', country: 'Norway' }, { x: 71, y: 93.2, z: 24.7, name: 'UK', country: 'United Kingdom' }, { x: 69.2, y: 57.6, z: 10.4, name: 'IT', country: 'Italy' }, { x: 68.6, y: 20, z: 16, name: 'RU', country: 'Russia' }, { x: 65.5, y: 126.4, z: 35.3, name: 'US', country: 'United States' }, { x: 65.4, y: 50.8, z: 28.5, name: 'HU', country: 'Hungary' }, { x: 63.4, y: 51.8, z: 15.4, name: 'PT', country: 'Portugal' }, { x: 64, y: 82.9, z: 31.3, name: 'NZ', country: 'New Zealand' } ]; const chart = new G2.Chart({ container: 'c1', forceFit: true, height: 350, padding: [ 20, 0, 80, 80 ], plotBackground: { stroke: '#ccc', // 边颜色 lineWidth: 1 // 边框粗细 } // 绘图区域背景设置 }); chart.source(data, { x: { alias: 'Daily fat intake', // 定义别名 tickInterval: 5, // 自定义刻度间距 nice: false, // 不对最大最小值优化 max: 96, // 自定义最大值 min: 62 // 自定义最小是 }, y: { alias: 'Daily sugar intake', tickInterval: 50, nice: false, max: 165, min: 0 }, z: { alias: 'Obesity(adults) %' } }); // 开始配置坐标轴 chart.axis('x', { label: { formatter: val => { return val + ' gr'; // 格式化坐标轴显示文本 } }, grid: { lineStyle: { stroke: '#d9d9d9', lineWidth: 1, lineDash: [ 2, 2 ] } } }); chart.axis('y', { title: { offset: 70, }, label: { formatter: val => { if (val > 0) { return val + ' gr'; } } } }); chart.legend(false); chart.tooltip({ title: 'country' }); chart .point() .position('x*y') .size('z', [ 10, 40 ]) .label('name*country', { offset: 0, // 文本距离图形的距离 textStyle: { fill: '#000', fontWeight: 'bold', // 文本粗细 shadowBlur: 5, // 文本阴影模糊 shadowColor: '#fff' // 阴影颜色 } }) .color('#3182bd') .opacity(0.5) .shape('circle') .tooltip('x*y*z'); chart.guide().line({ start: [ 65, 'min' ], end: [ 65, 'max' ], text: { content: 'Safe fat intake 65g/day', position: 'end', autoRotate: false, style: { textAlign: 'start' } }, }); chart.guide().line({ start: [ 'min', 50 ], end: [ 'max', 50 ], text: { content: 'Safe sugar intake 50g/day', position: 'end', style: { textAlign: 'end' } } }); chart.render();
在一些比较个性化的可视化需求里,通常会使用可视化隐喻,比如会使用人物照片来代替人物名字,使得可视化更直观。
这时可以通过为 label
进行如下配置:
chart.axis('xField', {
label: {
htmlTemplate: value => {
return '<img src="' +imageMap[value] + '" width="30px"/>';
}
}
});
完整代码
const data = [ { name: 'John', vote: 35654 }, { name: 'Damon', vote: 65456 }, { name: 'Patrick', vote: 45724 }, { name: 'Mark', vote: 13654 } ]; const imageMap = { 'John': 'https://zos.alipayobjects.com/rmsportal/mYhpaYHyHhjYcQf.png', 'Damon': 'https://zos.alipayobjects.com/rmsportal/JBxkqlzhrlkGlLW.png', 'Patrick': 'https://zos.alipayobjects.com/rmsportal/zlkGnEMgOawcyeX.png', 'Mark': 'https://zos.alipayobjects.com/rmsportal/KzCdIdkwsXdtWkg.png' } const chart = new G2.Chart({ container : 'c2', width : 600, height : 250 }); chart.source(data, { vote: { min: 0 } }); chart.legend(false); chart.axis('name', { label: { htmlTemplate: value => { return '<img src="' +imageMap[value] + '" style="width:30px;max-width:none;"/>'; } }, tickLine: null }); chart.interval() .position('name*vote') .color('name', [ '#7f8da9', '#fec514', '#db4c3c', '#daf0fd' ]) .size(20) .label('name'); chart.render();
在 tickLine 上可以配置坐标轴刻度线的长短(length)、颜色(stroke)、粗细(lineWidth),或者控制它的展示(tickLine: null,不展示刻度线)。
chart.axis('xField', {
tickLine: {
lineWidth: 2,
length: 10,
stroke: 'red',
alignWithLabel:true
}
});
chart.axis('genre', {
tickLine: {
length: -10
}
});
chart.axis('genre', {
tickLine: {
alignWithLabel: false
}
});
img,[object Object],
通过设置 subTickCount
属性可以为两个主刻度间设置子刻度线的个数,同时通过 subTickLine
设置子刻度线样式。
chart.axis('xField', {
subTickCount: 3,
subTickLine: {
length: 3,
stroke: '#545454',
lineWidth: 1
},
tickLine: {
length: 5,
lineWidth: 2,
stroke: '#000'
}
});
默认情况下,G2 会为 y 坐标轴生成网格线,而 x 轴不展示网格线。网格线的配置属性名为 grid
,只要为坐标轴配置 grid 属性即可展示网格线。
在 grid
属性中配置网格显示的样式,如下代码所示:
chart.axis('xField', {
grid: {
type: 'line',
lineStyle: {
stroke: '#d9d9d9',
lineWidth: 1,
lineDash: [ 4, 4 ]
},
align: 'center' // 网格顶点从两个刻度中间开始
}
});
每一种坐标轴范围的选择都会导致最后可视化结果的不同,坐标轴显示范围的设置需要在列定义中配置:
// 方式 1
chart.source(data, {
xField: {
type: 'linear',
min: 0,
max: 1000
}
});
// 方式 2
chart.scale('xField', {
type: 'linear',
min: 0,
max: 1000
});
默认的坐标轴刻度线个数是 5 个,通过列定义,用户可以自定义坐标轴刻度线的个数。
chart.source(data, {
xField: {
type: 'timeCat', // 声明该数据的类型
tickCount: 9
}
});
对于连续类型的数据,G2 还支持设置坐标轴刻度线的间距(tickInterval
属性),同样需要在列定义中进行配置,但是需要说明的是,tickInterval
为原始数据值的差值,并且 tickCount
和 tickInterval
不可以同时声明。
chart.source(data, {
xField: {
type: 'linear',
tickInterval: 1000
}
});
对于分类数据的坐标轴两边默认会留下一定的空白,连续数据的坐标轴的两端没有空白刻度
是否两端有空白是列定义里面 range 属性决定的,分类列的 range 的默认值是 [ 1 / (count - 1), 1 - 1 / (count - 1) ],可以修改这个值达到改变空白大小的目的。
chart.source(data, {
xField: {
type: 'cat',
range: [ 0, 1 ]
}
});
不同的坐标系下坐标轴的显示不一样,默认的配置项也不同
极坐标下的坐标轴上栅格线有圆形和多边形两种;
theta、helix 坐标系默认不显示坐标轴;
polar 坐标系发生 transpose 时也不显示坐标轴。
图例(legend)是图表的辅助元素,使用颜色、大小、形状区分不同的数据类型,用于图表中数据的筛选。G2 会根据设置图形属性映射以及数据的类型自动生成不同的图例。
shape, color, size 这三个图形属性如果判断接收的参数是数据源的字段时,会自动生成不同的图例;
shape 属性,会根据不同的 shape 类型生成图例;
color 属性, 会赋予不同的图例项不同的颜色来区分图形;
size 属性, 在图例上显示图形的大小。
通过 chart.legend([field, ]false)
可以关闭图例。
调用 chart.legend()
进行图例的配置,使用方法如下所示:
chart.legend({
position: 'bottom', // 设置图例的显示位置
itemGap: 20 // 图例项之间的间距
});
chart.legend('cut', false); // 不显示 cut 字段对应的图例
chart.legend('price', {
title: null // 不展示图例 title
});
chart.legend(false); //所有的图例都不显示
属性名 | 解释 | 默认值 |
---|---|---|
position | 图例的显示位置,支持12个定位位置,配置值 ‘left-top’,‘left-center’,‘left-bottom’,‘right-top’,‘right-top’,‘right-bottom’,‘top-left’,‘top-center’,‘top-bottom’,‘bottom-left’,‘bottom-center’,‘bottom-right’。也可使用’left’(默认为left-bottom’),‘right’(默认为’right-bottom’),‘top’(默认为top-center’),‘bottom’(默认为bottom-center’)定位。 | ‘bottom-center’ |
title | 用于图例标题的显示样式配置,如果值为 null 则不展示。 | 左右两侧图例默认展示标题,上下图例默认不展示标题 |
background | 用于图例背景色的配置 | 默认没有背景色 |
offsetX | 整个图例的水平偏移距离 | – |
offsetY | 整个图例的垂直偏移距离 | – |
width | 图例的整体宽度(用于连续图例) | 20 |
height | 图例的整体高度(用于连续图例) | 156 |
autoWrap | 图例项过多时是否自动换行(用于分类图例) | true,自动换行 |
marker | 配置图例 marker 的显示样式,支持指定 point 几何标记支持的所有 shape(除去 ‘rect’):‘circle’, ‘square’, ‘bowtie’, ‘diamond’, ‘hexagon’, ‘triangle’, ‘triangle-down’, ‘hollowCircle’, ‘hollowSquare’, ‘hollowBowtie’, ‘hollowDiamond’, ‘hollowHexagon’, ‘hollowTriangle’, ‘hollowTriangle-down’, ‘cross’, ‘tick’, ‘plus’, ‘hyphen’, ‘line’ | 不同的几何标记不同的 marker |
attachLast | 是否启用尾部跟随图例(适用于line 、lineStack 、area 、areaStack 图表类型) | false |
clickable | 图例项是否可以点击 | true |
hoverable | 是否默认开启鼠标 hover 到图例项上的交互 | true |
selectedMode | clickable 为 true 时生效,图例的选中模式,单选或者多选 | ‘multiple’ 默认多选 |
onHover | 自定义图例项鼠标 hover 事件,hoverable 为 false 不生效 | – |
onClick | 自定义图例项点击事件, clickable 为 false 不生效 | – |
const data = [ { year: 'Year 1800', region: 'Africa', population: 107 }, { year: 'Year 1900', region: 'Africa', population: 133 }, { year: 'Year 2012', region: 'Africa', population: 1052 }, { year: 'Year 1800', region: 'America', population: 31 }, { year: 'Year 1900', region: 'America', population: 156 }, { year: 'Year 2012', region: 'America', population: 954 }, { year: 'Year 1800', region: 'Asia', population: 635 }, { year: 'Year 1900', region: 'Asia', population: 947 }, { year: 'Year 2012', region: 'Asia', population: 4250 }, { year: 'Year 1800', region: 'Europe', population: 203 }, { year: 'Year 1900', region: 'Europe', population: 408 }, { year: 'Year 2012', region: 'Europe', population: 740 }, { year: 'Year 1800', region: 'Oceania', population: 2 }, { year: 'Year 1900', region: 'Oceania', population: 6 }, { year: 'Year 2012', region: 'Oceania', population: 38 } ]; const chart = new G2.Chart({ container: 'legendMarker', width: 600, height: 350, padding: [ 20, 90, 95, 80 ] }); chart.source(data); chart.coord().transpose(); chart.legend({ title: null, // 不展示图例的标题 marker: 'square' // 设置图例 marker 的显示样式 }); chart.intervalDodge().position('region*population').color('year').label('population'); chart.render();
属性名 | 解释 | 默认值 |
---|---|---|
allowAllCanceled | (分类图例)是否保留一项不能取消勾选,默认为 false,即最后一项不能取消勾选 | false |
unCheckColor | 未选中时marker的颜色 | ‘#bfbfbf’ |
textStyle | 图例项文本的样式配置 | {fill: ‘#3c3c3c’} |
itemWidth | 图例项的宽度,当图例有很多图例项,并且用户想要这些图例项在同一平面内垂直对齐,此时这个属性可帮用户实现此效果 | – |
itemFormatter | 用于格式化图例每项的文本显示,是个回调函数 | – |
在 G2 中,图例分为两种:
分类图例;
连续图例。
对于分类图例的筛选,G2 提供了三种方式:
selectedMode: 'multiple'
多选,默认配置;
selectedMode: 'single'
单选;
clickable: false
禁用筛选。
对于连续图例,声明 slidable: false
关闭筛选操作,默认开启筛选操作。
通过如下代码即可进行配置:
// 对分类图例进行配置
chart.legend({
selectedMode: 'single'
});
const data = [ { 部门: '部门0', 小组: '组名0', 完成人数: 37, 未完成人数: 9 }, { 部门: '部门0', 小组: '组名2', 完成人数: 29, 未完成人数: 10 }, { 部门: '部门0', 小组: '组名8', 完成人数: 59, 未完成人数: 14 }, { 部门: '部门0', 小组: '组名9', 完成人数: 60, 未完成人数: 8 }, { 部门: '部门0', 小组: '组名10', 完成人数: 83, 未完成人数: 14 }, { 部门: '部门0', 小组: '组名12', 完成人数: 67, 未完成人数: 21 }, { 部门: '部门0', 小组: '组名14', 完成人数: 46, 未完成人数: 18 }, { 部门: '部门0', 小组: '组名17', 完成人数: 19, 未完成人数: 27 }, { 部门: '部门0', 小组: '组名19', 完成人数: 74, 未完成人数: 17 }, { 部门: '部门1', 小组: '组名15', 完成人数: 34, 未完成人数: 19 }, { 部门: '部门1', 小组: '组名20', 完成人数: 71, 未完成人数: 25 }, { 部门: '部门1', 小组: '组名26', 完成人数: 28, 未完成人数: 23 }, { 部门: '部门1', 小组: '组名29', 完成人数: 90, 未完成人数: 24 }, { 部门: '部门2', 小组: '组名7', 完成人数: 50, 未完成人数: 5 }, { 部门: '部门2', 小组: '组名11', 完成人数: 86, 未完成人数: 26 }, { 部门: '部门2', 小组: '组名13', 完成人数: 63, 未完成人数: 16 }, { 部门: '部门2', 小组: '组名27', 完成人数: 76, 未完成人数: 2 }, { 部门: '部门2', 小组: '组名28', 完成人数: 13, 未完成人数: 28 }, { 部门: '部门3', 小组: '组名1', 完成人数: 33, 未完成人数: 16 }, { 部门: '部门3', 小组: '组名3', 完成人数: 14, 未完成人数: 1 }, { 部门: '部门3', 小组: '组名4', 完成人数: 43, 未完成人数: 25 }, { 部门: '部门3', 小组: '组名16', 完成人数: 45, 未完成人数: 13 }, { 部门: '部门3', 小组: '组名18', 完成人数: 50, 未完成人数: 21 }, { 部门: '部门3', 小组: '组名22', 完成人数: 43, 未完成人数: 7 }, { 部门: '部门3', 小组: '组名23', 完成人数: 38, 未完成人数: 6 }, { 部门: '部门3', 小组: '组名24', 完成人数: 33, 未完成人数: 24 }, { 部门: '部门3', 小组: '组名25', 完成人数: 13, 未完成人数: 27 }, { 部门: '部门4', 小组: '组名5', 完成人数: 98, 未完成人数: 4 }, { 部门: '部门4', 小组: '组名6', 完成人数: 88, 未完成人数: 12 }, { 部门: '部门4', 小组: '组名21', 完成人数: 52, 未完成人数: 9 } ]; const DataView = DataSet.DataView; const dv = new DataView(); dv.source(data) .transform({ type: 'sort', callback: (obj1, obj2) => { return obj1['部门'] > obj2['部门'] ? 1 : -1; } }) .transform({ type: 'map', callback: obj => { obj['完成人数'] *= -1; // 将完成人数转换成负数 return obj; } }) .transform({ type: 'fold', fields: ['完成人数', '未完成人数'], key: '完成状态', value: '人数' }); const chart = new G2.Chart({ container: 'c1', forceFit: true, height: 400, padding: [ 5, 100, 80 ] }); chart.source(dv); chart.filter('部门', val => { return val === '部门0'; }); chart.coord().transpose(); // 关键代码:设置对应图例的选择模式 chart.legend('部门', { selectedMode: 'single', position: 'right-bottom', hoverable: false }); chart.legend('完成状态', false); chart.axis('人数', { label: { formatter: value => { value = parseInt(value); return Math.abs(value); // 将负数格式化成正数 } } }); chart.interval() .position('小组*人数') .color('部门') .shape('完成状态', [ 'rect', 'hollowRect' ]) .style({ lineWidth: 1 }); chart.render();
G2 支持使用 html 渲染图例,方式非常简单,只要声明 useHtml: true
即可。
chart.legend({
useHtml: true,
containerTpl: {string}, // 可选,默认容器模板不满足要求时使用
itemTpl: {string} | {function}, // 可选,用户设置的图例项 html 模板,默认提供的模板不满足要求时使用
});
const data = [ { country: 'Lithuania', litres: 501.9 }, { country: 'Czech Republic', litres: 301.9 }, { country: 'Ireland', litres: 201.1 }, { country: 'Germany', litres: 165.8 }, { country: 'Australia', litres: 139.9 }, { country: 'Austria', litres: 128.3 }, { country: 'UK', litres: 99 }, { country: 'Belgium', litres: 60 }, { country: 'The Netherlands', litres: 50 } ]; const ds = new DataSet(); const dv = ds.createView() .source(data) .transform({ type: 'percent', field: 'litres', dimension: 'country', as: 'percent' }); const chart = new G2.Chart({ container: 'c2', width: 500, height: 500, padding: [ 20, 120, 20, 160 ] }); chart.source(dv, { percent: { formatter: val => { val = (val * 100).toFixed(2) + '%'; return val; } }, nice: false }); chart.coord('theta', { innerRadius: 0.3, radius: 1 }); chart.tooltip({ showTitle: false, // 不展示标题 itemTpl: '<li data-index={index}><span style="color:{color}">{name}:</span>{value}</li>' }); chart.legend({ useHtml: true, position: 'right', containerTpl: '<div class="g2-legend">' + '<table class="g2-legend-list" style="list-style-type:none;margin:0;padding:0;"></table>' + '</div>', itemTpl: (value, color, checked, index) => { const obj = dv.rows[index]; checked = checked ? 'checked' : 'unChecked'; return '<tr class="g2-legend-list-item item-' + index + ' ' + checked + '" data-value="' + value + '" data-color=' + color + ' style="cursor: pointer;font-size: 14px;">' + '<td width=150 style="border: none;padding:0;"><i class="g2-legend-marker" style="width:10px;height:10px;display:inline-block;margin-right:10px;background-color:' + color + ';"></i>' + '<span class="g2-legend-text">' + value + '</span></td>' + '<td style="text-align: right;border: none;padding:0;">' + obj.litres + '</td>' + '</tr>'; }, offsetX: 15, 'g2-legend': { marginLeft: '100px', marginTop: '-107px' }, 'g2-legend-list': { border: 'none' } }); chart.filter('country', val => { return val !== 'UK'; }); chart.intervalStack() .position('percent') .color('country', [ '#67b7dc', '#84b761', '#fdd400', '#cc4748', '#cd82ad', '#2f4074', '#448e4d', '#b7b83f', '#b9783f' ]) .label('percent', { formatter: (val, item) => { return item.point.country + ': ' + val; } }) .style({ lineWidth: 2, stroke: '#fff' }); chart.render();
chart.legend(false); // 隐藏全部图例
chart.legend('x', false); // 只隐藏 x 维度对应的图例
chart.legend('x', {
position: 'bottom'
}); // 只更改 x 维度对应的图例的显示位置
调整 padding 值(padding 的介绍详见创建图表之图表样式)。
提示信息(tooltip),是指当鼠标悬停在图表上的某点时,以提示框的形式展示该点的数据,比如该点的值,数据单位等。tooltip 内显示的信息完全可以通过格式化函数动态指定;通过调用 chart.tooltip(false)
即可不启用提示信息功能。
在 G2 中提供了个层次的配置 tooltip 的方法,
设置在 chart 对象上的tooltip 样式、功能相关的配置,
设置在每个几何标记对象上的 tooltip 配置,具体如下:
(1) chart 上的 tooltip 方法
chart.tooltip(true, cfg); // 开启 tooltip,并设置 tooltip 配置信息
chart.tooltip(cfg); // 省略 true, 直接设置 tooltip 配置信息
chart.tooltip(false); // 关闭 tooltip
常用的 tooltip 配置信息如下,注意,G2 的 tooltip 是使用 html 进行渲染的。
chart.tooltip({ triggerOn: 'mousemove' | 'click' | 'none', // tooltip 的触发方式,默认为 mousemove showTitle: {boolean}, // 是否展示 title,默认为 true crosshairs: { type: 'rect' || 'x' || 'y' || 'cross', style: { // 图形样式 } }, // tooltip 辅助线配置 offset: 10, // tooltip 距离鼠标的偏移量 containerTpl: '<div class="g2-tooltip">' + '<div class="g2-tooltip-title" style="margin:10px 0;"></div>' + '<ul class="g2-tooltip-list"></ul></div>', // tooltip 容器模板 itemTpl: '<li data-index={index}><span style="background-color:{color};width:8px;height:8px;border-radius:50%;display:inline-block;margin-right:8px;"></span>{name}: {value}</li>', // tooltip 每项记录的默认模板 inPlot: true, // 将 tooltip 展示在指定区域内 follow: true, // tooltip 是否跟随鼠标移动 shared: true || false, // 默认为 true, false 表示只展示单条 tooltip position: 'left' || 'right' || 'top' || 'bottom' // 固定位置展示 tooltip });
更详细的配置请查看 tooltip api。
(2)geom 对象上的 tooltip 配置
chart.<geom>.tooltip('field1*field2...*fieldN');
这个时候 tooltip 的显示内容如下:
title 标题,默认tooltip 的标题取第一项的 title
name 标题
value 值
color 图例项对应的颜色
index 索引值
所以在回调函数中可以通过修改这几个值,达到自定义tooltip 的目的
chart.<geom>.tooltip('a*b', (a, b) => {
return {
name: a,
value: b
};
});
chart.tooltip(false)
关闭 tooltip 外,还可以在 geom 上关闭 tooltip。配置方法如下:chart.point().tooltip(false);
tooltip 的目的是为了展示数据点相关的数据,具体展示的内容完全可以通过多种灵活的方式来实现。
如果 G2 默认生成的 tooltip 展示内容不满足需求,用户可以通过调用几何标记的 tooltip 方法手动指定要显示的 tooltip 内容。
const data = [ { month: 0, tem: 7, city: 'tokyo' }, { month: 1, tem: 6.9, city: 'tokyo' }, { month: 2, tem: 9.5, city: 'tokyo' }, { month: 3, tem: 14.5, city: 'tokyo' }, { month: 4, tem: 18.2, city: 'tokyo' }, { month: 5, tem: 21.5, city: 'tokyo' }, { month: 6, tem: 25.2, city: 'tokyo' }, { month: 7, tem: 26.5, city: 'tokyo' }, { month: 8, tem: 23.3, city: 'tokyo' }, { month: 9, tem: 18.3, city: 'tokyo' }, { month: 10, tem: 13.9, city: 'tokyo' }, { month: 11, tem: 9.6, city: 'tokyo' } ]; const chart = new G2.Chart({ container: 'c0', width: 800, height: 300 }); const defs = { 'month':{ type: 'cat', alias: '月份', // 别名,如果没有别名显示成字段名 month values: [ '一月', '二月', '三月', '四月', '五月', '六月', '七月', '八月', '九月', '十月', '十一月', '十二月' ] }, 'tem': { alias: '温度' } }; chart.source(data,defs); chart.tooltip(true, { showTitle: false // 默认标题不显示 }); chart.line().position('month*tem').tooltip('month*tem'); chart.render();
当需要格式化 tooltip 的显示内容时,有两种方式:
大部分场景下,可以使用 geom.tooltip('x*y*z', callback)
同 chart.tooltip({ itemTpl: 'xxx'})
的方式。
对于复杂的场景,可以监听 chart 对象上的 tooltip:change
事件。这个事件会返回如下参数:
{
x: 当前鼠标的 x 坐标,
y: 当前鼠标的 y 坐标,
tooltip: 当前的 tooltip 对象
items: 数组对象,当前 tooltip 显示的每条内容
}
每一项的内容
title 标题,默认tooltip 的标题取第一项的 title
name 标题
value 值
color 图例项对应的颜色
index 索引值
通过修改 items 的内容就可以修改 tooltip 的展示内容了。
这种方式通常需要同 chart.tooltip() 结合使用。
//自定义模板,自定义tooltip展示
chart.tooltip({
itemTpl: '<li>{x}: {y}</li>'
});
chart.line().position('x*y').tooltip('x*y', (x, y) => {
return {
x,
y
}; // 返回的参数名对应 itemTpl 中的变量名
);
const data = [ { name: 'Microsoft Internet Explorer', value: 30 }, { name: 'Chrome', value: 20 }, { name: 'Firefox', value: 10 }, { name: 'Safari', value: 10 }, { name: 'Opera', value: 15 }, { name: 'Others', value: 15 } ]; const chart = new G2.Chart({ container: 'c1', forceFit: true, height: 400 }); chart.source(data); chart.coord('theta', { innerRadius: 0.6, radius: 0.8 }); chart.tooltip({ showTitle: false, itemTpl: '<li>{name}: {value}</li>' }); chart.intervalStack() .position('value') .color('name') .tooltip('name*value', (name, value) => { return { name: name, value: value + '%' }; }); chart.render();
const data = [ // 数据 { time: 1428163200000, start: 469, end: 480 }, { time: 1428163203600, start: 480, end: 430 }, { time: 1428163207200, start: 430, end: 410 }, { time: 1428163210800, start: 410, end: 420 }, { time: 1428163214400, start: 420, end: 440 }, { time: 1428163218000, start: 440, end: 460 }, { time: 1428163221600, start: 460, end: 410 }, { time: 1428163225200, start: 410, end: 440 }, { time: 1428163228800, start: 440, end: 490 } ]; const DataView = DataSet.DataView; const dv = new DataView(); dv.source(data).transform({ type: 'map', callback: obj => { obj.range = [ obj.start, obj.end ]; obj.trend = (obj.start <= obj.end) ? '上涨' : '下跌'; return obj; } }); const chart = new G2.Chart({ container: 'c2', width: 800, height: 400, padding: [ 20, 50, 95, 80 ] }); chart.source(dv, { 'time': { // 设置日期类型 type: 'time', mask: 'YYYY-MM-DD HH:MM:ss' }, 'trend': { alias: '趋势' } }); chart.interval() .position('time*range') .color('trend', [ '#1bbd19', '#fa513a' ]) .size(20); chart.render(); chart.on('tooltip:change', function(ev) { const items = ev.items; // tooltip显示的项 const origin = items[0]; // 将一条数据改成多条数据 const range = origin.point._origin.range; items.splice(0); // 清空 items.push(Object.assign({ name: '开始值', marker: true, value: range[0] }, origin)); items.push(Object.assign({ name: '结束值', marker: true, value: range[1] }, origin)); });
G2 也支持使用自定义的 html 展示 tooltip。配置方法如下:
chart.tooltip(true, {
containerTpl: '<div class="g2-tooltip">'
+ '<div class="g2-tooltip-title" style="margin:10px 0;"></div>'
+ '<ul class="g2-tooltip-list"></ul></div>',
itemTpl: '<li data-index={index}><span style="background-color:{color};width:8px;height:8px;border-radius:50%;display:inline-block;margin-right:8px;"></span>{name}: {value}</li>'
});
containerTpl
tooltip 容器模板 ,注意一定要包含以下 class:
<div class="g2-tooltip">
<!-- tooltip 标题 -->
<div class="g2-tooltip-title" style="margin:10px 0;"></div>
<!-- tooltip 内容列表容器 -->
<ul class="g2-tooltip-list"></ul>
</div>
itemTpl
tooltip 每项记录的默认模板:
<li data-index={index}>
<!-- 每项记录的 marker -->
<span style="background-color:{color};width:8px;height:8px;border-radius:50%;display:inline-block;margin-right:8px;"></span>
{name}: {value}
</li>
对于 tooltip 的显示样式的配置,用户可以:
在自定义模板时使用内联的方式直接定义;
在 html 页面的 style 标签内,为对应的 dom 标签设置样式;
在 chart.tooltip(cfg)
中设置属性,如下,具体的说明详见 API
chart.tooltip({
'g2-tooltip': {
position: 'absolute',
visibility: 'hidden',
border : '1px solid #efefef',
backgroundColor: 'white',
color: '#000',
opacity: "0.8",
padding: '5px 15px',
'transition': 'top 200ms,left 200ms'
}, // 设置 tooltip 的 css 样式
'g2-tooltip-list': {
margin: '10px'
}
});
默认线图和区域图会显示辅助线、柱状图会显示辅助框,当用户需要显示辅助线(框)时,可以通过配置 crosshairs
属性设置,crosshairs 支持四种展示形式:
crosshairs: {
type: 'rect' || 'x' || 'y' || 'cross',
style: {
// 图形样式
}
}, // tooltip 辅助线配置
crosshairs.type 说明:
rect: 矩形框
x: 水平辅助线
y: 垂直辅助线
cross: 十字辅助线
‘line’, ‘area’, ‘path’ 默认会展示垂直辅助线;‘interval’, 默认会展示矩形背景框。
通过配置 triggerOn
参数来改变 tooltip 的触发方式,可配置值为:
mousemove
: 鼠标移动至目标区域触发,默认方式;
click
: 鼠标点击目标区域触发
none
: 不触发 tooltip,由用户调用 chart.showTooltip(point)
和 chart.hideTooltip()
来控制提示框的显示隐藏。
当然在任何触发方式下,用户都可以通过调用 chart.showTooltip(point)
可以控制在固定的位置显示提示信息,参数 point
为画布上的坐标点,格式如下:
const point = {
x: 23,
y: 30
};
另外还提供了 chart.getXY({xField: value, yField: value})
方法,用于获取数据对应在画布空间的坐标。
const data = [ { time: '2016-10-25 00:00:00', runCount: 4, type: 2, runTime: 2 }, { time: '2016-10-25 00:30:00', runCount: 2, type: 6, runTime: 3 }, { time: '2016-10-25 01:00:00', runCount: 13, type: 2, runTime: 5 }, { time: '2016-10-25 01:30:00', runCount: 9, type: 9, runTime: 1 }, { time: '2016-10-25 02:00:00', runCount: 5, type: 2, runTime: 3 }, { time: '2016-10-25 02:30:00', runCount: 8, type: 2, runTime: 1 }, { time: '2016-10-25 03:00:00', runCount: 13, type: 1, runTime: 2 }, { time: '2016-10-25 03:30:00', runCount: 4, type: 2, runTime: 2 }, { time: '2016-10-25 04:00:00', runCount: 2, type: 6, runTime: 3 }, { time: '2016-10-25 04:30:00', runCount: 13, type: 2, runTime: 5 }, { time: '2016-10-25 05:00:00', runCount: 9, type: 9, runTime: 1 }, { time: '2016-10-25 05:30:00', runCount: 5, type: 2, runTime: 3 } ]; const chart = new G2.Chart({ container: 'c3', forceFit: true, height: 300, padding: [ 50, 80 ] }); chart.source(data); chart.scale('time',{ type: 'timeCat', mask: 'HH:MM', tickCount:12, nice:true, }); chart.scale('runCount', { alias: '运行数量', min: 0 }); chart.scale('runTime', { alias: '运行时间(ms)' }); chart.tooltip({ triggerOn: 'click' // 鼠标点击触发 tooltip }); // 关闭 tooltip chart.legend(false); // 不显示图例 chart.line() .position('time*runTime') .color('#5ed470') .size(2) .shape('smooth'); // 绘制曲线图 chart.point() .position('time*runTime') .color('#5ed470') .size(5) .shape('circle') .style({ cursor: 'pointer' }); // 绘制点图 chart.render(); // 初始化到最新一个点 const lastPoint = chart.get('plotRange').br; chart.showTooltip(lastPoint);
Guide 作为 G2 图表的辅助元素,主要用于在图表上标识额外的标记注解。
G2 目前支持 9 种辅助标记类型:
line:辅助线(可带文本),例如表示平均值或者预期分布的直线;
image:辅助图片,在图表上添加辅助图片;
text:辅助文本,指定位置添加文本说明;
region:辅助框,框选一段图区,设置背景、边框等;
regionFilter:区域着色,将图表中位于矩形选区中的图形元素提取出来,重新着色;
html:辅助 html,指定位置添加自定义 html,显示自定义信息;
arc:辅助弧线。
dataMarker:特殊数据点标注,多用于折线图和面积图
dataRegion:特殊数据区间标注,多用于折线图和面积图
下面列出了各个 guide 辅助标记类型的使用,更详细的配置项参见 Guide API。
guide 辅助标记用于标示位置的方式主要有两种
原始数据的值 如: {time: ‘2010-01-01’, value: 100} 或者 [‘2010-01-01’, 100]
图表绘图区域的相对位置,从左上角计算 如:[‘50%’, ‘50%’]
注意
:
原始数据和百分比的方式不能混用,不支持[‘2010-01-01’, ‘50%’]
不同 guide 中标示位置的参数不完全相同,主要是 start, end, position
chart.guide().line({ start: {object} | {function} | {array}, // 辅助线起始位置,值为原始数据值,支持 callback end: {object} | {function}|| {array},// 辅助线结束位置,值为原始数据值,支持 callback lineStyle: { stroke: '#999', // 线的颜色 lineDash: [ 0, 2, 2 ], // 虚线的设置 lineWidth: 3 // 线的宽度 }, // 图形样式配置 text: { position: 'start' | 'center' | 'end' | '39%' | 0.5, // 文本的显示位置 autoRotate: {boolean}, // 是否沿线的角度排布,默认为 true style: { // 文本图形样式配置 }, content: {string}, // 文本的内容 offsetX: {number}, // x 方向的偏移量 offsetY: {number} // y 方向的偏移量 } // 文本配置 });
const DataView = DataSet.DataView; $.getJSON('/assets/data/diamond.json', function(data) { const dv = (new DataView()).source(data); const caratAvg = dv.mean('carat'); // 计算克拉数均值 const priceAvg = dv.mean('price'); // 计算价格均值 const chart = new G2.Chart({ // 创建图表 container : 'c1', forceFit: true, height: 450 }); chart.source(data); // 设置数据源 chart.point().position('carat*price'); chart.guide().line({ start: [ caratAvg, 0 ], // 使用数组格式 end: [ caratAvg, 20000 ], text: { position: 'end', autoRotate: false, content: '克拉数均值:' + caratAvg } }); chart.guide().line({ start: { carat: 0, price: priceAvg }, // 使用对象格式 end: { carat: 4, price: priceAvg }, text: { position: 'end', autoRotate: false, content: '价格均值:' + priceAvg, style: { textAlign: 'end' } } }); chart.render(); // 图表渲染 });
// 辅助图片 image,只是指定了 start,则该点表示图片左上角坐标 chart.guide().image({ top: {boolean}, // 指定 giude 是否绘制在 canvas 最上层,默认为 false, 即绘制在最下层 zIndex: {number}, start: {object} | {function} | {array}, // 图片起始位置, 值为原始数据值,支持 callback src: {string}, // 图片路径 width: {number}, height: {number}, offsetX: {number}, // x 方向的偏移量 offsetY: {number} // y 方向偏移量 }); // 辅助图片 image,通过指定 start 和 end 确定图片的位置和宽高 chart.guide().image({ top: {boolean}, // 指定 giude 是否绘制在 canvas 最上层,默认为 false, 即绘制在最下层 start: {array} | {function} | {array}, // 图片起始位置, 值为原始数据值,支持 callback end: {array} | {function} | {array}, // 图片结束位置, 值为原始数据值,支持 callback src: {string}, // 图片路径 offsetX: {number}, // x 方向的偏移量 offsetY: {number} // y 方向偏移量 });
const DataView = DataSet.DataView; $.getJSON('/assets/data/diamond.json', function(data) { const dv = new DataView(); dv.source(data).transform({ type: 'bin.histogram', field: 'depth', binWidth: 0.5, as: [ 'depth', 'count' ], }); const chart = new G2.Chart({ container: 'c2', forceFit: true, height: 450 }); chart.source(dv); chart.tooltip({ crosshairs: false }); chart.interval().position('depth*count').shape('hollowRect'); chart.guide().image({ start: [ 55, 200 ], src: 'https://os.alipayobjects.com/rmsportal/IUYwZOlOpysCUsl.png', width: 60, height: 100 }); chart.render(); });
chart.guide().text({
top: {boolean}, // 指定 giude 是否绘制在 canvas 最上层,默认为 false, 即绘制在最下层
zIndex: {number},
position: {object} | {function} | {array}, // 文本的起始位置,值为原始数据值,支持 callback
content:
style: {
fill: '#666', // 文本颜色
fontSize: '12', // 文本大小
fontWeight: 'bold' // 文本粗细
rotate: 30 // 旋转角度
}, // 文本的图形样式属性
offsetX: {number}, // x 方向的偏移量
offsetY: {number} // y 方向偏移量
});
const colors = G2.Global.colors; $.getJSON('/assets/data/diamond.json', function(data) { const chart = new G2.Chart({ // 创建图表 container : 'c3', forceFit: true, height: 450, padding: [ 20, 90, 60, 80 ] }); const defs = { 'cut': { type: 'cat', values:[ 'Ideal', 'Premium', 'Very-Good', 'Good', 'Fair' ] } }; chart.source(data, defs); // 设置数据源 chart.legend(false); chart.pointJitter().position('cut*depth').color('cut'); chart.guide().text({ position: [ 'Ideal', 67 ], content: '越完美的钻石切割工艺越集中', style: { fill: colors[0], textAlign: 'center', fontSize: 14 } }); chart.guide().text({ position: [ 'Fair', 63 ], content: '越差的钻石切割工艺越分散', style: { fill: colors[4], textAlign: 'center', fontSize: 14 } }); chart.render(); // 图表渲染 });
chart.guide().region({
top: {boolean}, // 指定 giude 是否绘制在 canvas 最上层,默认为 false, 即绘制在最下层
start: {object} | {function} | {array}, // 辅助框起始位置,值为原始数据值,支持 callback
end: {object} | {function} | {array},// 辅助框结束位置,值为原始数据值,支持 callback
style: {
lineWidth: 0, // 辅助框的边框宽度
fill: '#f80', // 辅助框填充的颜色
fillOpacity: 0.1, // 辅助框的背景透明度
stroke: '#ccc' // 辅助框的边框颜色设置
} // 辅助框的图形样式属性
});
const data = [ { month: 0, tem: 7, city: 'tokyo' }, { month: 1, tem: 6.9, city: 'tokyo' }, { month: 2, tem: 9.5, city: 'tokyo' }, { month: 3, tem: 14.5, city: 'tokyo' }, { month: 4, tem: 18.2, city: 'tokyo' }, { month: 5, tem: 21.5, city: 'tokyo' }, { month: 6, tem: 25.2, city: 'tokyo' }, { month: 7, tem: 26.5, city: 'tokyo' }, { month: 8, tem: 23.3, city: 'tokyo' }, { month: 9, tem: 18.3, city: 'tokyo' }, { month: 10, tem: 13.9, city: 'tokyo' }, { month: 11, tem: 9.6, city: 'tokyo' }, { month: 0, tem: -0.2, city: 'newYork' }, { month: 1, tem: 0.8, city: 'newYork' }, { month: 2, tem: 5.7, city: 'newYork' }, { month: 3, tem: 11.3, city: 'newYork' }, { month: 4, tem: 17, city: 'newYork' }, { month: 5, tem: 22, city: 'newYork' }, { month: 6, tem: 24.8, city: 'newYork' }, { month: 7, tem: 24.1, city: 'newYork' }, { month: 8, tem: 20.1, city: 'newYork' }, { month: 9, tem: 14.1, city: 'newYork' }, { month: 10, tem: 8.6, city: 'newYork' }, { month: 11, tem: 2.5, city: 'newYork' }, { month: 0, tem: -0.9, city: 'berlin' }, { month: 1, tem: 0.6, city: 'berlin' }, { month: 2, tem: 3.5, city: 'berlin' }, { month: 3, tem: 8.4, city: 'berlin' }, { month: 4, tem: 13.5, city: 'berlin' }, { month: 5, tem: 17, city: 'berlin' }, { month: 6, tem: 18.6, city: 'berlin' }, { month: 7, tem: 17.9, city: 'berlin' }, { month: 8, tem: 14.3, city: 'berlin' }, { month: 9, tem: 9, city: 'berlin' }, { month: 10, tem: 3.9, city: 'berlin' }, { month: 11, tem: 1, city: 'berlin' } ]; const chart = new G2.Chart({ container: 'c4', forceFit: true, height: 450 }); chart.source(data); chart.line().position('month*tem').color('city'); chart.guide().region({ start: [ 5, 'min' ], end: [ 7, 'max' ] }); // 6月 - 8月最低温度 chart.render();
chart.guide().html({
position: {object} | {function} | {array}, // html 的中心位置, 值为原始数据值,支持 callback
alignX: 'left' | 'middle' | 'right',
alignY: 'top' | 'middle' | 'bottom',
offsetX: {number},
offsetY: {number},
html: {string}, // html 代码,也支持callback,可能是最大值、最小值之类的判定
zIndex: {number}
});
const DataView = DataSet.DataView; $.getJSON('/assets/data/diamond.json', function(data) { const dv = (new DataView()).source(data); const caratAvg = dv.mean('carat'); // 计算克拉数均值 const priceAvg = dv.mean('price'); // 计算价格均值 const chart = new G2.Chart({ container: 'c5', forceFit: true, height: 450 }); chart.source(data); chart.point().position('carat*price'); // 坐标点 const point = [ 3.5, 12000 ]; //html字符串 const tooltipHtml = "<div style='border: 2px solid #0f8de8;width: 50px;height: 26px;color: #0f8de8;position: relative;'>" + "<span style='color:#63c6c2;font-size:15px'>异常值</span>" + "<div style='width: 0;height: 0;border-bottom: 8px solid #0f8de8;border-right:10px solid transparent;position: absolute;top: 16px;left: 46px;'></div>" + "</div>"; chart.guide().html({ position: point, html: tooltipHtml, alignX: 'right', alignY: 'bottom', offsetX: 10 }); chart.render(); // 图表渲染 });
将图表中位于矩形选区中的图形元素提取出来,重新着色,可以用于区域筛选、图表分段着色。
chart.guide().regionFilter({
top: {boolean}, // 指定 giude 是否绘制在 canvas 最上层,默认为 true, 即绘制在最上层
start: {object} | {function} | {array}, // 辅助框起始位置,值为原始数据值,支持 callback
end: {object} | {function} | {array},// 辅助框结束位置,值为原始数据值,支持 callback
color:'#ccc' //染色色值
apply:{array} //可选,设定regionFilter只对特定geom类型起作用,如apply:['area'],默认regionFilter的作用域为整个图表
});
const data = [ { year: '1991', value: 15468 }, { year: '1992', value: 16100 }, { year: '1993', value: 15900 }, { year: '1994', value: 17409 }, { year: '1995', value: 17000 }, { year: '1996', value: 31056 }, { year: '1997', value: 31982 }, { year: '1998', value: 32040 }, { year: '1999', value: 33233 } ]; const chart = new G2.Chart({ container: 'c6', forceFit: true, height: 450 }); chart.source(data); chart.scale({ value: { min: 10000 }, year: { range: [ 0, 1 ] } }); chart.axis('value', { label: { formatter: val => { return (val / 10000).toFixed(1) + 'k'; } } }); chart.tooltip({ crosshairs: { type: 'line' } }); chart.line().position('year*value').size(2); chart.guide().regionFilter({ start: [ '1991', 'min' ], end: [ '1995', 'max' ], color: '#178fff' }); chart.guide().regionFilter({ start: [ '1995', 'min' ], end: [ '1999', 'max' ], color: '#2ec15a' }); chart.render();
chart.guide().arc({
top: true | false, // 指定 giude 是否绘制在 canvas 最上层,默认为 false, 即绘制在最下层
start: {object} | {function} | {array}, // 辅助框起始位置,值为原始数据值,支持 callback
end: {object} | {function} | {array},// 辅助框结束位置,值为原始数据值,支持 callback
style: {} // 图形样式属性
});
注意
:
对图表中的某个特殊数据点进行标注。默认状态的特殊数据标注点由point、line、text三部分组成,同时开放接口对各部分是否显示及显示样式等进行设置。
chart.guide().dataMarker({ top:true | false, // 指定 giude 是否绘制在 canvas 最上层,默认为true, 即绘制在最上层 position: {object} | {function} | {array}, // 标注点起始位置,值为原始数据值,支持 callback , content: {string}, // 显示的文本内容 style: { text: {object}, point:{object}, line:{object} },//可选,文本/point/line样式 display:{ text:{boolean}, point:{boolean}, line:{boolean} },//可选,是否显示文本/point/line,默认为全部显示 lineLength:{number},//可选,line长度,default为30 direction:'upward' | 'downward' //可选,朝向,默认为upwaard });
注意
:
var data = [{ year: '1991',value: 3}, { year: '1992',value: 4}, { year: '1993',value: 3.5}, { year: '1994',value: 5}, { year: '1995',value: 4.9}, { year: '1996',value: 6}, { year: '1997',value: 7}, { year: '1998',value: 9}, { year: '1999',value: 13}]; var chart = new G2.Chart({ container: 'c7', forceFit: true, height: window.innerHeight }); chart.source(data); chart.scale('value', { min: 0 }); chart.scale('year', { range: [0, 1] }); chart.line().position('year*value'); chart.guide().dataMarker({ position: [ '1997', 7 ], content: '特殊数据标注点' }); chart.render();
对图表中的某个特殊数据区间进行标注。
chart.guide().dataRegion({ top:true | false, // 指定 giude 是否绘制在 canvas 最上层,默认为 true, 即绘制在最上层 start: {object} | {function} | {array}, // 标注点起始位置,值为原始数据值,支持 callback , end: {object} | {function} | {array}, // 标注点结束位置,值为原始数据值,支持 callback , content: {string}, // 显示的文本内容 style: { text: {object}, point:{object}, line:{object} },//可选,文本/point/line样式 display:{ text:{boolean}, point:{boolean}, line:{boolean} },//可选,是否显示文本/point/line,默认为全部显示 lineLength:{number},//可选,line长度,default为30 direction:'upward' | 'downward' //可选,朝向,默认为upwaard });
注意
:
var data = [{ year: '1991',value: 3}, { year: '1992',value: 4}, { year: '1993',value: 3.5}, { year: '1994',value: 5}, { year: '1995',value: 4.9}, { year: '1996',value: 6}, { year: '1997',value: 7}, { year: '1998',value: 9}, { year: '1999',value: 13}]; var chart = new G2.Chart({ container: 'c8', forceFit: true, height: window.innerHeight }); chart.source(data); chart.scale('value', { min: 0 }); chart.scale('year', { range: [0, 1] }); chart.line().position('year*value'); chart.guide().dataRegion({ start: [ '1994', 5 ], end: [ '1996', 6 ], content: '数据区间标注', lineLength: 50 }); chart.render();
辅助标记接受的位置信息的参数都是原始数据值,辅助标记一旦生成后就是固定了位置,如果数据发生改变,辅助标记就需要删除掉重新创建
// 清除图表
chart.clear();
// 重新声明图形语法
chart.point().position('carat*price');
chart.guide().html([ newX, newY ], htmlstring);
chart.render();
如果数据是动态更新的那么这个过程需要频繁进行,基于这种场景 guide 提供两种计算动态位置的:
可以使用’min’, ‘median’, ‘max’ 字符串代表原始值的最小值、平均值、最大值,例如: [0, ‘min’] 表示 x 轴上数值为 0,y 轴位置在数值的最小值上;
表示位置的数组可以换成回调函数,函数原型: function(xScale, yScale) {return [];}
values
包含了所有的分类,连续度量常用的是 min, maxconst data = []; const time = Math.floor((new Date()).getTime() / 1000) * 1000; for (let i = -19; i <= 0; i++) { data.push({ time: time + i * 3 * 1000, value: Math.random() + .25 }); } // 查找最大值 function findMax() { let maxValue = 0; let maxObj = null; data.forEach(obj => { if (obj.value > maxValue) { maxValue = obj.value; maxObj = obj; } }); return maxObj; } const chart = new G2.Chart({ // 创建图表 container: 'c9', forceFit: true, height: 450 }); chart.source(data, { time: { type: 'time', mask: 'HH:mm:ss' } }); chart.line().position('time*value'); // 添加一条虚线 chart.guide().line({ start: [ 'min', 0.25 ], end: [ 'max', 0.25] }); chart.guide().text({ position() { const obj = findMax(); return [ obj.time, obj.value ]; }, content: '最大值' }); chart.render(); setInterval(function() { data.shift(); data.push({ time: new Date().getTime(), value: Math.random() + .25 }); chart.changeData(data); }, 3000);
分面,将一份数据按照某个维度分隔成若干子集,然后创建一个图表的矩阵,将每一个数据子集绘制到图形矩阵的窗格中。
总结起来,分面其实提供了两个功能:
按照指定的维度划分数据集;
对图表进行排版。
对于探索型数据分析来说,分面是一个强大有力的工具,能帮你迅速地分析出数据各个子集模式的异同。
chart.facet(type, { fileds: [field1, field2...], showTitle: true, // 显示标题 autoSetAxis: true,// 自动设置坐标轴的文本,避免重复和遮挡 padding: 10, // 每个view 之间的间距 /** * 创建每个分面中的视图 * @param {object} view 视图对象 * @param {object} facet facet中有行列等信息,常见属性:data rows cols rowIndex colIndex rowField colField * @return {null} */ eachView(view, facet) {}, // 列标题 colTitle: { offsetY: -15, style: { fontSize: 14, textAlign: 'center', fill: '#444' } }, // 行标题 rowTitle: { offsetX: 15, style: { fontSize: 14, textAlign: 'center', rotate: 90, fill: '#444' } } })
说明:
第一个参数 type
用于指定分面的类型;
fileds
属性用于指定数据集划分依据的字段;
eachView
回调函数中创建各个视图的图表类型;
也可以设置每个分面之间的间距 padding
chart.facet('list', {
fileds: [ 'cut', 'carat' ],
padding: 20 // 各个分面之间的间距,也可以是数组 [top, right, bottom, left]
});
更多配置信息,请查阅 Facet API。
G2 支持的分面类型如下表所示:
分面类型 | 说明 |
---|---|
rect | 默认类型,指定 2 个维度作为行列,形成图表的矩阵。 |
list | 指定一个维度,可以指定一行有几列,超出自动换行。 |
circle | 指定一个维度,沿着圆分布。 |
tree | 指定多个维度,每个维度作为树的一级,展开多层图表。 |
mirror | 指定一个维度,形成镜像图表。 |
matrix | 指定一个维度,形成矩阵分面。 |
rect 矩形分面是 G2 的默认分面类型。支持按照一个或者两个维度的数据划分,按照先列后行的顺序。
chart.facet('rect', {
fields: [ 'cut', 'clarity' ],
eachView(view) {
view.point().position('carat*price').color('cut');
}
});
分面矩阵每列按照 cut
字段划分,每行按照 clarity
字段划分。
$.getJSON('/assets/data/diamond.json', function(data) { const chart = new G2.Chart({ container: 'c1', forceFit: true, height: 600, padding: [ 30, 80, 80, 80 ] }); chart.source(data, { carat: { sync: true }, price: { sync: true }, cut: { sync: true } }); chart.facet('rect', { fields: [ 'cut', 'clarity' ], eachView(view) { view.point().position('carat*price').color('cut'); } }); chart.render(); });
说明:
fields
字段中表示行和列的字段名时,可以设置行或者列为 null
,会变成单行或者单列的分面该类型分面可以通过设置 cols
属性来指定每行可显示分面的个数,超出时会自动换行。
$.getJSON('/assets/data/diamond.json', function(data) { const chart = new G2.Chart({ container: 'c2', width: 800, height: 400, padding: [ 30, 90, 80, 80 ] }); chart.source(data, { carat: { sync: true }, price: { sync: true }, cut: { sync: true } }); chart.facet('list', { fields: [ 'cut' ], cols: 3, // 超过3个换行 padding: 30, eachView(view) { view.point().position('carat*price').color('cut'); } }); chart.render(); });
const DataView = DataSet.DataView; $.getJSON('/assets/data/diamond.json',function (data) { const chart = new G2.Chart({ container: 'c3', width: 600, height: 600, animate: false, padding: [ 20, 20, 70, 20 ] }); chart.source(data, { mean: { sync: true }, cut: { sync: true } }); chart.coord('polar'); chart.axis(false); chart.facet('circle', { fields: [ 'clarity' ], padding: 0, eachView(view, facet) { const data = facet.data; const dv = new DataView(); dv.source(data).transform({ type: 'aggregate', fields: [ 'price' ], operations: [ 'mean' ], as: [ 'mean' ], groupBy: [ 'cut' ] }); view.source(dv); view.interval().position('cut*mean').color('cut'); } }); // 分面设置 chart.render(); });
树形分面一般用于展示存在层次结构的数据,展示的是整体和部分之间的关系
提供了 line
和 lineSmooth
两个属性,用于配置连接各个分面的线的样式,其中:
line,用于配置线的显示属性。
lineSmooth,各个树节点的连接线是否是平滑的曲线,默认为 false。
下图展示了树形多层级的分面。
const data = [ { gender: '男', count: 40, class: '一班', grade: '一年级' }, { gender: '女', count: 30, class: '一班', grade: '一年级' }, { gender: '男', count: 35, class: '二班', grade: '一年级' }, { gender: '女', count: 45, class: '二班', grade: '一年级' }, { gender: '男', count: 20, class: '三班', grade: '一年级' }, { gender: '女', count: 35, class: '三班', grade: '一年级' }, { gender: '男', count: 30, class: '一班', grade: '二年级' }, { gender: '女', count: 40, class: '一班', grade: '二年级' }, { gender: '男', count: 25, class: '二班', grade: '二年级' }, { gender: '女', count: 32, class: '二班', grade: '二年级' }, { gender: '男', count: 28, class: '三班', grade: '二年级' }, { gender: '女', count: 36, class: '三班', grade: '二年级' } ]; const DataView = DataSet.DataView; const chart = new G2.Chart({ container: 'c4', width: 800, height: 400, animate: false, padding: [ 0, 90, 80, 80 ] }); chart.source(data); chart.coord('theta'); chart.tooltip({ showTitle: false }); chart.facet('tree', { fields: [ 'grade','class' ], line: { stroke: '#00a3d7' }, lineSmooth: true, eachView(view, facet) { const data = facet.data; const dv = new DataView(); dv.source(data).transform({ type: 'percent', field: 'count', dimension: 'gender', as: 'percent' }); view.source(dv, { percent: { formatter(val) { return (val * 100).toFixed(2) + '%'; } } }); view.intervalStack().position('percent').color('gender'); } }); chart.render();
镜像分面一般用于对比两类数据的场景,例如 男女的比例、正确错误的对比等
通过配置 transpose
属性为 true,可以将镜像分面翻转。
$.getJSON('/assets/data/population.json', function(data) { const tmp = []; const dates = []; const selEl = $('#selYear'); data.male.values.forEach(function(obj) { if (dates.indexOf(obj.date) === -1) { dates.push(obj.date); } obj.age_groups.forEach(function(subObject) { subObject.gender = 'male'; subObject.date = obj.date; tmp.push(subObject); }); }); data.female.values.forEach(function(obj) { obj.age_groups.forEach(function(subObject) { subObject.gender = 'female'; subObject.date = obj.date; tmp.push(subObject); }); }); dates.forEach(date => { $('<option value="' + date + '">' + new Date(date * 1000).getFullYear() + '</option>').appendTo(selEl); }); const ds = new DataSet({ state: { date: dates[0] } }); const dv = ds.createView() .source(tmp) .transform({ type: 'filter', callback(row) { // 判断某一行是否保留,默认返回true return new Date(row.date * 1000).getFullYear() === new Date(ds.state.date * 1000).getFullYear(); } }); const chart = new G2.Chart({ container: 'c5', forceFit: true, height: 600 }); chart.source(dv, { age: { sync: true, tickCount: 11 }, total_percentage: { sync: true, formatter(v) { return v + '%'; } }, gender: { sync: true } }); chart.facet('mirror', { fields: [ 'gender' ], transpose: true, eachView(view) { view.interval().position('age*total_percentage') .color('gender', [ 'rgb(113,192,235)', 'rgb(246,170,203)' ]); } }); chart.render(); selEl.on('change', function() { const val = selEl.val(); const date = parseInt(val); ds.setState('date', date); }); });
矩阵分面主要对比数据中多个字段之间的关系,例如常见的散点矩阵图
const DataView = DataSet.DataView; $.getJSON('/assets/data/iris.json', function(data) { const chart = new G2.Chart({ container: 'c6', forceFit: true, height: 600 }); chart.source(data, { Species: { sync: true } }); chart.facet('matrix', { fields: [ 'SepalLength', 'SepalWidth', 'PetalLength', 'PetalWidth' ], eachView(view, facet) { if (facet.rowIndex === facet.colIndex) { const dv = new DataView(); dv.source(facet.data) .transform({ type: 'bin.histogram', field: facet.colField, // 对应数轴上的一个点 bins: 30, // 分箱个数 as: [ facet.colField, 'count' ], groupBy: [ 'Species' ] }); view.source(dv.rows); view.intervalStack() .position(facet.colField + '*count') .color('Species', [ '#880000', '#008800', '#000088' ]); } else { view.point() .position([ facet.colField, facet.rowField ]) .color('Species', [ '#880000', '#008800', '#000088' ]); } } }); chart.render(); });
在图表样式上,G2 提供了丰富的自定义配置选项,既可从全局设置,也支持 Chart 级别的主题设置和局部设置数据层级的设置。
G2 默认提供了两种图表主题: default、dark。
新增主题可以使用 Global 上的 registerTheme 接口。
const { Global } = G2; // 获取 Global 全局对象
Global.registerTheme('newTheme', {
colors: [ 'red', 'blue', 'yello' ]
}); // 传入两个参数,一个参数是主题的名称,另一个参数是主题配置项
这样就可以在全局切换这个主题或者在 chart 新建的时候指定设置的主题了。
直接传入主题名
const { Global } = G2; // 获取 Global 全局对象
Global.setTheme('dark'); // 传入值为 'default'、'dark' 的一种,如果不是,那么使用 default 主题。
G2 图表样式的配置项都是设置到全局变量 G2.Global
上,可以通过如下两种方式进行局部的样式设置:
(1)方式一: 直接赋值给全局对象 Global,但是不推荐
G2.Global.animate = false ; // 关闭默认动画
G2.Global.colors = [ 'red', 'blue', 'yellow' ]; // 更改默认的颜色
(2) 方式二: 使用 Global.setTheme 方法。推荐使用这种方式,使用方法如下:
const theme = G2.Util.deepMix({
animate: false,
colors: {...},
shapes: {...}
// 具体的配置项详见 api/global.html
}, G2.Theme);
G2.Global.setTheme(theme); // 将主题设置为用户自定义的主题
对于数据级别或者更细粒度的样式设置,可以通过 geom 对象上的 color 图形属性方法或者各个 chart 配置项上的图形属性设置。
更多 Global 上关于主题的配置属性,可以直接查看 G2.Global
的返回值。
const Util = G2.Util; const theme = Util.deepMix({ shape: { polygon: { stroke: '#213c51', // 地图轮廓线颜色 lineWidth: 1 // 地图轮廓线宽度 }, hollowPoint: { fill: '#21273b', // 点的填充颜色 lineWidth: 2, // 点的边框宽度 radius: 3 // 点的半径 }, interval: { fillOpacity: 1 // 填充透明度设置 } }, axis: { bottom: { label: { textStyle: { fill: '#999'} // 底部标签文本的颜色 } }, left: { label: { textStyle: { fill: '#999'} // 左部标签文本的颜色 } }, right: { label: { textStyle: { fill: '#999'} // 右部标签文本的颜色 } } } }, G2.Global); G2.Global.setTheme(theme); $.getJSON('/assets/data/world.geo.json', function(mapData) { const userData = []; const features = mapData.features; for(let i=0; i<features.length; i++) { const name = features[i].properties.name; userData.push({ "name": name, "value": Math.round(Math.random()*1000) }); } // 绘制地图背景 const ds = new DataSet(); const bgDataView = ds.createView('back') .source(mapData, { type: 'GeoJSON' }) .transform({ type: 'geo.projection', projection: 'geoMercator' }); const userPolygonDv = ds.createView() .source(userData) .transform({ geoDataView: bgDataView, field: 'name', type: 'geo.region', as: [ 'longitude', 'latitude' ] }); const chart = new G2.Chart({ container: 'c1', forceFit: true, height: 400, padding: 0 }); chart.source(userPolygonDv); chart.coord().reflect(); chart.tooltip({ showTitle: false }); chart.axis(false); chart.legend(false); chart.polygon() .position('longitude*latitude') .color('value','#39ccf4-#20546b') .style({ lineWidth: 1, stroke: '#999' }); chart.render(); const data = [ { time: '10:10', call: 4, waiting: 2, people: 2 }, { time: '10:15', call: 2, waiting: 6, people: 3 }, { time: '10:20', call: 13, waiting: 2, people: 5 }, { time: '10:25', call: 9, waiting: 9, people: 1 }, { time: '10:30', call: 5, waiting: 2, people: 3 }, { time: '10:35', call: 8, waiting: 2, people: 1 }, { time: '10:40', call: 13, waiting: 1, people: 2 } ]; const dv = new DataSet.DataView(); dv.source(data).transform({ type: 'fold', fields: [ 'call','waiting' ], key: 'type', value: 'count', retains: [ 'time', 'people' ] }); const chart2 = new G2.Chart({ container: 'c2', forceFit: true, height: 250 }); chart2.source(dv, { 'count': { alias: '话务量(通)', min: 0 }, 'people': { alias: '人数(人)', min: 0 } }); chart2.legend(false);// 不显示图例 chart2.intervalStack().position('time*count').color('type', [ '#348cd1', '#43b5d8' ]); // 绘制层叠柱状图 chart2.line().position('time*people').color('#5ed470').size(4).shape('smooth'); // 绘制曲线图 chart2.point().position('time*people').color('#5ed470').tooltip(false); // 绘制点图 chart2.render(); });
Global 上可以配置的信息:
const Global = { version: '3.2.0-beta.3', renderer2d: 'canvas', // renderer2d: 'svg', trackable: true, animate: true, snapArray: [ 0, 1, 2, 4, 5, 10 ], // 指定固定 tick 数的逼近值 snapCountArray: [ 0, 1, 1.2, 1.5, 1.6, 2, 2.2, 2.4, 2.5, 3, 4, 5, 6, 7.5, 8, 10 ], widthRatio: { // 宽度所占的分类的比例 column: 1 / 2, // 一般的柱状图占比 1/2 rose: 0.9999999, // 玫瑰图柱状占比 1 multiplePie: 1 / 1.3 // 多层的饼图、环图 }, // 折线图、区域图、path 当只有一个数据时,是否显示成点 showSinglePoint: false, connectNulls: false, scales: { } };
更多的查看:https://github.com/antvis/g2/blob/master/src/global.js
同一个上下文现在支持多种主题共存,上述两个图表,通过给第二个图表指定主题,可以切换其主题:
const chart2 = new G2.Chart({
container: 'c2',
forceFit: true,
height: 250,
theme: 'dark'
});
const Util = G2.Util; const theme = Util.deepMix({ shape: { polygon: { stroke: '#213c51', // 地图轮廓线颜色 lineWidth: 1 // 地图轮廓线宽度 }, hollowPoint: { fill: '#21273b', // 点的填充颜色 lineWidth: 2, // 点的边框宽度 radius: 3 // 点的半径 }, interval: { fillOpacity: 1 // 填充透明度设置 } }, axis: { bottom: { label: { textStyle: { fill: '#999'} // 底部标签文本的颜色 } }, left: { label: { textStyle: { fill: '#999'} // 左部标签文本的颜色 } }, right: { label: { textStyle: { fill: '#999'} // 右部标签文本的颜色 } } } }, G2.Global); G2.Global.setTheme(theme); $.getJSON('/assets/data/world.geo.json', function(mapData) { const userData = []; const features = mapData.features; for(let i=0; i<features.length; i++) { const name = features[i].properties.name; userData.push({ "name": name, "value": Math.round(Math.random()*1000) }); } // 绘制地图背景 const ds = new DataSet(); const bgDataView = ds.createView('back') .source(mapData, { type: 'GeoJSON' }) .transform({ type: 'geo.projection', projection: 'geoMercator' }); const userPolygonDv = ds.createView() .source(userData) .transform({ geoDataView: bgDataView, field: 'name', type: 'geo.region', as: [ 'longitude', 'latitude' ] }); const chart = new G2.Chart({ container: 'c3', forceFit: true, height: 400, padding: 0 }); chart.source(userPolygonDv); chart.coord().reflect(); chart.tooltip({ showTitle: false }); chart.axis(false); chart.legend(false); chart.polygon() .position('longitude*latitude') .color('value','#39ccf4-#20546b') .style({ lineWidth: 1, stroke: '#999' }); chart.render(); const data = [ { time: '10:10', call: 4, waiting: 2, people: 2 }, { time: '10:15', call: 2, waiting: 6, people: 3 }, { time: '10:20', call: 13, waiting: 2, people: 5 }, { time: '10:25', call: 9, waiting: 9, people: 1 }, { time: '10:30', call: 5, waiting: 2, people: 3 }, { time: '10:35', call: 8, waiting: 2, people: 1 }, { time: '10:40', call: 13, waiting: 1, people: 2 } ]; const dv = new DataSet.DataView(); dv.source(data).transform({ type: 'fold', fields: [ 'call','waiting' ], key: 'type', value: 'count', retains: [ 'time', 'people' ] }); const chart2 = new G2.Chart({ container: 'c4', forceFit: true, height: 250, theme: 'dark' }); chart2.source(dv, { 'count': { alias: '话务量(通)', min: 0 }, 'people': { alias: '人数(人)', min: 0 } }); chart2.legend(false);// 不显示图例 chart2.intervalStack().position('time*count').color('type', [ '#348cd1', '#43b5d8' ]); // 绘制层叠柱状图 chart2.line().position('time*people').color('#5ed470').size(4).shape('smooth'); // 绘制曲线图 chart2.point().position('time*people').color('#5ed470').tooltip(false); // 绘制点图 chart2.render(); });
恰当的文本标注可以提高可视化图表的可读性。除了提供文本标签标注的功能之外,G2 还支持文本的格式化以及自定义 html 文本标签的功能。
在每个几何标记 geom 上调用 label 方法,指定需要显示的数据维度即可:
// 指定显示文本标签 chart.point().position('x*y').label('x'); // 格式化文本标签的显示内容 chart.interval().position('x*y').label('x', { offset: {number}, // 设置坐标轴文本 label 距离坐标轴线的距离 textStyle: { textAlign: 'center', // 文本对齐方向,可取值为: start middle end fill: '#404040', // 文本的颜色 fontSize: '12', // 文本大小 fontWeight: 'bold', // 文本粗细 textBaseline: 'top' // 文本基准线,可取 top middle bottom,默认为middle } || {function}, // 支持回调 rotate: 30, autoRotate: {boolean} // 是否需要自动旋转,默认为 true formatter: {function}, // 回调函数,用于格式化坐标轴上显示的文本信息 htmlTemplate: {function}, // 使用 html 自定义 label });
const data = [ { genre: 'Sports', sold: 275 }, { genre: 'Strategy', sold: 115 }, { genre: 'Action', sold: 120 }, { genre: 'Shooter', sold: 350 }, { genre: 'Other', sold: 150 } ]; const chart = new G2.Chart({ container: 'c0', height: 300, forceFit: true, padding: [ 40, 20, 95, 80 ] }); chart.source(data, { genre: { alias: '游戏种类' // 列定义,定义该属性显示的别名 }, sold: { alias: '销售量' } }); chart.interval().position('genre*sold').color('genre').label('sold'); chart.render();
更多配置项请查看 label api。
如果默认提供的 label 显示形式不满足需求时,可以在 label 中定义 formatter 回调函数。
chart.interval().position('x*y').label('x', {
/**
* 文本格式化函数
* @param {string} text 每条记录 x 属性的值
* @param {object} item 映射后的每条数据记录,是一个对象,可以从里面获取你想要的数据信息
* @param {number} index 每条记录的索引
* @return {string} 返回格式化后的文本
*/
formatter: (text, item, index) => {}
});
完整代码如下:
const data = [ { name: 'Microsoft Internet Explorer', value: 56.33 }, { name: 'Chrome', value: 24.03 }, { name: 'Firefox', value: 10.38 }, { name: 'Safari', value: 4.77 }, { name: 'Opera', value: 0.91 }, { name: 'Proprietary or Undetectable', value: 0.2 } ]; const dv = new DataSet.DataView(); dv.source(data).transform({ type: 'percent', field: 'value', dimension: 'name', as: 'percent' }); const chart = new G2.Chart({ container: 'c1', width: 800, height: 400 }); chart.source(dv); // 重要:绘制饼图时,必须声明 theta 坐标系 chart.coord('theta', { radius: 0.8 // 设置饼图的大小 }); chart.tooltip({ showTitle: false }); chart.intervalStack() .position('percent') .color('name') .tooltip('name*percent', (name, percent) => { return { name, value: (percent * 100).toFixed(2) + '%' }; }) .label('name', { formatter: (text, item, index) => { const point = item.point; // 每个弧度对应的点 let percent = point['percent']; percent = (percent * 100).toFixed(2) + '%'; return text + ' ' + percent; } }); chart.render();
chart.interval().position('x*y').label('x', {
/**
* 创建 html 文本
* @param {string} text 每条记录 x 属性的值
* @param {object} item 映射后的每条数据记录,是一个对象,可以从里面获取你想要的数据信息
* @param {number} index 每条记录的索引
* @return {string} 返回 html 字符串
*/
htmlTemplate: (text, item, index) => {}
});
label 除了可以格式化文本的显示,也支持使用 html 自定义显示的样式。只需要定义 htmlTemplate 格式化文本的回调函数即可,如下例所示:
完整代码:
const data = [ { name: '示例 A', value: 38.8 }, { name: '示例 B', value: 9.15 }, { name: '示例 C', value: 26.35 }, { name: '示例 D ', value: 22.6 }, { name: '示例 E', value: 3.1 } ]; const dv = new DataSet.DataView(); dv.source(data).transform({ type: 'percent', field: 'value', dimension: 'name', as: 'percent' }); const chart = new G2.Chart({ container: 'c2', width: 800, height: 400 }); chart.source(dv); // 重要:绘制饼图时,必须声明 theta 坐标系 chart.coord('theta', { radius: 0.8 // 设置饼图的大小 }); chart.tooltip({ showTitle: false }); chart.intervalStack() .position('percent') .color('name') .tooltip('name*percent', (name, percent) => { return { name: name, value: (percent * 100).toFixed(2) + '%' }; }) .label('name', { labelLine: false, // 不显示文本的连接线 offset: 30, // 文本距离图形的距离 htmlTemplate: (text, item, index) => { const point = item.point; // 每个弧度对应的点 let percent = point['percent']; percent = (percent * 100).toFixed(2) + '%'; return '<span class="title" style="display: inline-block;width: 50px;">' + text + '</span><br><span style="color:' + point.color + '">' + percent + '</span>'; // 自定义 html 模板 } }); chart.render();
chart 对象提供了各种事件支持,以响应用户的操作,方便用户扩展交互。开发者可以监听这些事件,然后通过回调函数做相应的处理,比如跳转到一个地址,或者弹出对话框,或者做数据下钻等等。
G2 中的事件用法如下:
chart.on('eventType', fn); // 绑定事件
chart.off('eventType', fn); // 移除事件
其中 eventType 对应事件名称,均使用小写。
对于事件的移除,chart.off('eventType', fn)
其中如果 fn 不指定,表示删除所有 eventType 事件,如果 eventType 和 fn 都不指定,则表示删除 chart 上所有的事件。
在 G2 中,我们将事件分为如下事件:
chart.on('mousedown', ev => {});
chart.on('mousemove', ev => {});
chart.on('mouseleave', ev => {});
chart.on('mouseup', ev => {});
chart.on('click', ev => {});
chart.on('dblclick', ev => {});
chart.on('touchstart', ev => {});
chart.on('touchmove', ev => {});
chart.on('touchend', ev => {});
chart.on('plotenter', ev => {});
chart.on('plotmove', ev => {});
chart.on('plotleave', ev => {});
chart.on('plotclick', ev => {});
chart.on('plotdblclick', ev => {});
chart.on('tooltip:show', ev => {}); // tooltip 展示
chart.on('tooltip:hide', ev => {}); // tooltip 隐藏
chart.on('tooltip:change', ev => {}); // tooltip 内容发生变化的时候
chart.on('point:click', ev => {});
chart.on('axis-label:click', ev => {});
img,[object Object],
下图展示了图表各个组件的名称:
详细的使用详见 api。
先来看一个简单的点击饼图后跳转至相应页面的例子。
通过监听 interval:click
事件,然后根据 ev
参数中的 data 字段的 _origin
属性值获取被点击区域的原始数据,以获取对应浏览器的名称。
完整代码:
const data = [ { name: 'IE', value: 56.33 }, { name: 'Chrome', value: 24.03 }, { name: 'Firefox', value: 10.38 }, { name: 'Safari', value: 4.77 }, { name: 'Opera', value: 0.91 }, { name: 'Unknown', value: 0.2 } ]; const DataView = DataSet.DataView; const dv = new DataView(); dv.source(data).transform({ type: 'percent', field: 'value', dimension: 'name', as: 'percent' }); const chart = new G2.Chart({ container: 'c1', forceFit: true, height: 400 }); chart.source(dv); // 重要:绘制饼图时,必须声明 theta 坐标系 chart.coord('theta', { radius: 0.8 // 设置饼图的大小 }); chart.tooltip({ showTitle: false }); chart.intervalStack() .position('percent') .color('name') .tooltip('name*percent', (name, percent) => { return { name, value: (percent * 100).toFixed(2) + '%' }; }) .style({ cursor: 'pointer' }) .label('name'); chart.render(); chart.on('interval:click', ev => { const data = ev.data; if (data) { const name = data._origin['name']; window.open('http://www.baidu.com/s?wd=' + name); } });
说明:
通过 interval:click 监听饼图的点击事件
通过 style 方法中设置 cursor: ‘pointer’ 改变鼠标形状
通过监听 tooltip:change
事件,可以做到动态得改变 tooltip 的显示信息,以完成 tooltip 的高度个性化定制。
tooltip:change
事件的参数格式如下:
{
items: array, // tooltip 上显示的记录信息
tooltip: object, // 当前 tooltip 对象
x: number, // 鼠标点击的 x 坐标点
y: number // 鼠标点击的 y 坐标点
}
通过 ev.items[0]
获取 tooltip 上的第一条记录数据,重复复制该记录的 value
属性。
完整代码如下:
const data = [ { name: '示例 A', value: 38.8 }, { name: '示例 B', value: 9.15 }, { name: '示例 C', value: 26.35 }, { name: '示例 D ', value: 22.6 }, { name: '示例 E', value: 3.1 } ]; const dv = new DataSet.DataView(); dv.source(data).transform({ type: 'percent', field: 'value', dimension: 'name', as: 'percent' }); const chart = new G2.Chart({ container: 'c2', width: 800, height: 400 }); chart.source(dv); // 重要:绘制饼图时,必须声明 theta 坐标系 chart.coord('theta', { radius: 0.8 // 设置饼图的大小 }); chart.tooltip({ showTitle: false }); chart.intervalStack() .position('percent') .color('name'); chart.render(); chart.on('tooltip:change', ev => { const item = ev.items[0]; // 获取tooltip要显示的内容 item.value = '格式化-' + (item.value * 100).toFixed(2) + '%'; });
G2 默认内置的交互包括:
active 激活;
select 选中。
开启以及关闭 shape 对于鼠标 hover 时的响应效果,G2 默认为各个 shape 内置了 active 效果。
geom.active(false); // 关闭默认响应
geom.active(true); // 开启默认响应
各个几何标记 geom 选中的模式包含如下三种:
不可选中;
单选;
多选;
选中是否可取消选中。
选中模式的设置方式如下:
geom.select(false); // 关闭
geom.select(true); // 打开
geom.select([true,] {
mode: 'single' || 'multiple', // 选中模式,单选、多选
style: {}, // 选中后 shape 的样式
cancelable: true | false, // 选中之后是否允许取消选中,默认允许取消选中
animate: true | false // 选中是否执行动画,默认执行动画
});
默认情况下,G2 中只有饼图支持选中交互,其他 geom 的选中模式默认情况下都是关闭的。
下面通过一个实例来演示选中 select(enable, cfg)
方法的使用。
本例中的地图 GeoJSON 数据请访问该地址获取:
<script src="https://a.alipayobjects.com/g/datavis/china-geojson/1.0.0/index.js"></script>
或者 github。
let provinceChart; function processData(mapData) { // 构造虚拟数据 const userData = []; const features = mapData.features; for (let i = 0; i < features.length; i++) { const name = features[i].properties.name; userData.push({ name: name, value: Math.round(Math.random() * 1000), }); } const ds = new DataSet(); const geoDataView = ds.createView().source(mapData, { type: 'GeoJSON', }); // geoJSON 经纬度数据 // 用户数据 const dvData = ds.createView().source(userData); dvData.transform({ type: 'geo.region', field: 'name', geoDataView: geoDataView, as: ['longitude', 'latitude'], }); return dvData; } function renderProvinceChart(name) { const provinceData = ChinaGeoJSON[name]; provinceChart && provinceChart.destroy(); provinceChart = null; if (!provinceData) { return; } const dv = processData(provinceData); // start: 计算地图的最佳宽高 const longitudeRange = dv.range('longitude'); const latitudeRange = dv.range('latitude'); const ratio = (longitudeRange[1] - longitudeRange[0]) / (latitudeRange[1] - latitudeRange[0]); let width; let height; if (ratio > 1) { width = 450; height = width / ratio; } else { width = 350 * ratio; height = 350; } // end: 计算地图的最佳宽高 provinceChart = new G2.Chart({ container: 'province', width, height, padding: 0 }); provinceChart.source(dv); provinceChart.axis(false); provinceChart.tooltip({ showTitle: false, }); provinceChart .polygon() .position('longitude*latitude') .label('name', { textStyle: { fill: '#fff', fontSize: 10, shadowBlur: 2, shadowColor: 'rgba(0, 0, 0, .45)' }, }) .style({ stroke: '#fff', lineWidth: 1, }) .color('value', '#BAE7FF-#1890FF-#0050B3'); provinceChart.render(); } const mapData = ChinaGeoJSON['China']; const chinaDv = processData(mapData); const longitudeRange = chinaDv.range('longitude'); const latitudeRange = chinaDv.range('latitude'); const ratio = (longitudeRange[1] - longitudeRange[0]) / (latitudeRange[1] - latitudeRange[0]); const chart = new G2.Chart({ container: 'china', width: 250, height: 250 / ratio, padding: 0, animate: false }); chart.source(chinaDv); chart.tooltip({ showTitle: false, }); chart.axis(false); chart .polygon() .position('longitude*latitude') .tooltip('name') .style({ stroke: '#bfbfbf', lineWidth: 1, fill: '#e3e3e3', globalAlpha: 0.85, cursor: 'pointer', // 设置鼠标手势 }) .select({ // 设置是否允许选中以及选中样式 mode: 'single', // 多选还是单选 style: { fill: '#1890ff', // 选中的样式 }, }); chart.render(); const shapes = chart.getAllGeoms()[0].getShapes(); for (let i = 0, len = shapes.length; i < len; i++) { const shape = shapes[i]; const origin = shape.get('origin')['_origin']; const name = origin.name; if (name === '浙江') { renderProvinceChart(name); chart.getAllGeoms()[0].setShapeSelected(shape); } } chart.on('plotclick', function(ev) { const shape = ev.shape; if (!shape || !shape.name) { return false; } if (shape.get('selected')) { const item = shape.get('origin'); const data = item['_origin']; const name = data.name; renderProvinceChart(name); } else { provinceChart && provinceChart.clear(); } });
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。