饼图(Pie Chart)作为最常见的统计图表之一,用于可视化数据中不同分类的占比情况。使用 G2,在不进行任何定制的情况下,用户可以得到如下饼图:除去基本的图形元素外,还提供了 label 文本标注以及图例的绘制能力。同时还提供了 tooltip 提示信息、图例筛选等交互行为。
但是通常,用户更倾向于以下饼图的设计及交互:
在定制之前,我们可以看看这两种饼图可视化方式的区别在哪里:
绘制层
- 使用空心环代替实心扇形;
- 图例的个性化定制,将原本 label 的内容同图例内容合并。
交互层
- 图形元素响应鼠标的状态发生改变:从默认的描边变为图形的放大突出;
- 图例的点击筛选变为点击突出图形元素
下面就让我们来看下,如何基于默认的饼图代码,一步一步快速、简单得定制吧!
默认饼图代码:
import { Chart } from '@antv/g2';
const data = [
{ item: '事例一', count: 40, percent: 0.4 },
{ item: '事例二', count: 21, percent: 0.21 },
{ item: '事例三', count: 17, percent: 0.17 },
{ item: '事例四', count: 13, percent: 0.13 },
{ item: '事例五', count: 9, percent: 0.09 },
];
const chart = new Chart({
container: 'container',
autoFit: true,
height: 500,
});
chart.coordinate('theta', {
radius: 0.75,
});
chart.data(data);
chart.scale('percent', {
formatter: (val) => {
val = val * 100 + '%';
return val;
},
});
chart.tooltip({
showTitle: false,
showMarkers: false,
});
chart
.interval()
.position('percent')
.color('item')
.label('percent', {
content: (data) => {
return `${data.item}: ${data.percent * 100}%`;
},
})
.adjust('stack');
chart.interaction('element-active');
chart.render();
线上示例链接:https://g2.antv.vision/zh/examples/pie/basic
定制坐标系
扇形变圆环,只需一步!
定制图例
我们可以使用 G2 的自定义图例来实现预期的图例样式,在 G2 4.0 的图例中,我们开放了 itemName
和 itemName
属性,用于图例内容及样式的配置。
图例属性对应关系
具体的定制代码如下:
// 声明需要进行自定义图例字段: 'item'
chart.legend('item', {
position: 'right', // 配置图例显示位置
custom: true, // 关键字段,告诉 G2,要使用自定义的图例
items: data.map((obj, index) => {
return {
name: obj.item, // 对应 itemName
value: obj.percent, // 对应 itemValue
marker: {
symbol: 'square', // marker 的形状
style: {
r: 5, // marker 图形半径
fill: chart.getTheme().colors10[index], // marker 颜色,使用默认颜色,同图形对应
},
}, // marker 配置
};
}),
itemValue: {
style: {
fill: '#999',
}, // 配置 itemValue 样式
formatter: val => `${val * 100}%` // 格式化 itemValue 内容
},
});
定制交互
在 G2 V3 版本,自定义图例的交互需要用户操作图形/数据进行实现,而在 G2 4.0 版本,如果无意改变图形元素的响应样式,我们只需要声明需要的交互即可:
chart.removeInteraction('legend-filter'); // 图例过滤操作默认内置,如不需要可以移除
chart.interaction('element-active'); // 添加 element-active 交互:鼠标 hover 激活图形 active 状态
但是我们需要的是图形元素方法效果,所以我们需要配置下图形元素 active 状态的样式:
chart
.interval()
.state({
active: {
style: element => {
const shape = element.shape;
return {
lineWidth: 10,
stroke: shape.attr('fill'),
strokeOpacity: shape.attr('fillOpacity'),
};
}, // 配置 active 样式,通过加粗边框实现放大效果
},
});
至此,三个步骤,个性化饼图的定制就结束了!
扩展
基于以上定制,我们还可以作进一步扩展,利用圆环空心部分的面积显示鼠标击中图形元素的详细信息,以代替 tooltip。当然这也并不麻烦,我们只需要:
关闭 Tooltip
chart.tooltip(false);
监听事件,更新 annotation 内容
// 绘制 annotation
let lastItem;
function updateAnnotation(data) {
if (data.item !== lastItem) {
chart.annotation().clear(true);
chart
.annotation()
.text({
position: ['50%', '50%'],
content: data.item,
style: {
fontSize: 20,
fill: '#8c8c8c',
textAlign: 'center',
},
offsetY: -20,
})
.text({
position: ['50%', '50%'],
content: data.count,
style: {
fontSize: 28,
fill: '#8c8c8c',
textAlign: 'center',
},
offsetX: -10,
offsetY: 20,
})
.text({
position: ['50%', '50%'],
content: '台',
style: {
fontSize: 20,
fill: '#8c8c8c',
textAlign: 'center',
},
offsetY: 20,
offsetX: 20,
});
chart.render(true);
lastItem = data.item;
}
}
// 清空 annotation
function clearAnnotation() {
chart.annotation().clear(true);
chart.render(true);
lastItem = null;
}
// 监听图例项点击事件
chart.on('legend-item:click', (ev) => {
const delegateObject = ev.gEvent.shape.get('delegateObject');
const targetData = data.filter(obj => obj.item === delegateObject.item.name);
if (targetData.length) {
updateAnnotation(targetData[0]);
}
});
// 监听图例项 mouseenter 事件
chart.on('legend-item:mouseenter', (ev) => {
const delegateObject = ev.gEvent.shape.get('delegateObject');
const targetData = data.filter(obj => obj.item === delegateObject.item.name);
if (targetData.length) {
updateAnnotation(targetData[0]);
}
});
// 监听图形元素 mouseenter 事件
chart.on('element:mouseenter', (ev) => {
if (ev.data) {
updateAnnotation(ev.data.data);
}
});
chart.on('element:mouseleave', (ev) => {
clearAnnotation();
});
chart.on('legend-item:mouseleave', (ev) => {
clearAnnotation();
});