Echarts对散点图已经实现了自带的框选的功能,不过是通过toolbox的形式。echarts自带的散点图框选功能,并不能够直接满足我在实际生产中对散点图框选功能的要求。因此我们决定对echarts的源码进行一个更深入的分析,实现一个更灵活的散点图散点框选功能。
通过分析echarts的源码可知,echarts实现对散点图里的点进行框选,主要依赖于它内部的消息机制。即当用户点击框选功能toolbox的时候,echarts会自动发出一条type为taskGlobalCursor且key为brush的消息,调整echarts的状态。使得echarts能够在用户鼠标按下的时候开始进行框选,而用户鼠标移动的时候同步变更框选范围,最后在用户鼠标松开的时候,绘制出一个完整的框来实现框选功能。
框选有两个比较关键的参数,一个是brushType,这个决定了是什么样的区域选择方式。这个值可以是横向框选、纵向框选、多边形区域选择等多种类型。我们的项目中,设置的是rect,以矩形的方式圈定一定的区域。另一个则是brushMode了,可选的值是代表单框选的single和代表多框选的multiple。在我们的项目中,我们使用的是multiple。
既然echarts内部使用的是消息机制来控制区域的选择,那么我们也是可以使用这种方式来达到相同的目的。事实上,我们也确实是这么做的。下面详细的介绍,我们如何使用echarts内部的消息机制来实现散点图的框选功能。
首先,我们需要先初始化一个echarts的图表,在我们的项目中,我们直接init了一个新的图表:
const chart = echarts.init(dom, undefined, {
renderer: 'canvas',
useDirtyRect: false,
});
紧接着,我们需要检查一下这个echarts内部是否有默认的toolbox,如果有默认的toolbox,需要找到这个默认的toolbox里面的brush,并将brushMode改成多选模式。
function setMultipleBrush(chart: echarts.ECharts) {
const _chart = chart as any;
const componentsViews: any = _chart._componentsViews;
for (let i in componentsViews) {
if (componentsViews[i].type == 'toolbox') {
const view = componentsViews[i];
if (view._features && view._features.brush) {
view._features.brush._brushMode = 'multiple';
}
}
}
}
确保了charts中即使有默认的区域选择,也是多选,而不是单选之后。我们要通过echarts的机制,使用api向echarts实例发送一个新的事件,这个事件将图表的模式切换为区域选择的模式:
function startSelect() {
const _chart = advanceTFChart as any;
_chart._api.dispatchAction({
type: 'takeGlobalCursor',
key: 'brush',
brushOption: {
brushType: 'rect',
brushMode: 'multiple'
}
});
}
通过以上的操作,echarts的图表就可以很好的实现框选功能了。但这还是不够的,因为我们每次框选结束,我们是需要得到相应的数据并进行一系列的操作的。在我们的项目中,还需要根据这些点做出一些转换,生成另外一张新的表。前面我们提到,echarts是通过消息机制来控制区域选择这个功能的。既然是消息机制,那么就必然有发送消息和接受消息的两方。我们可以通过发送消息达到调用echarts的一些功能的目的,同样的,我们也可以通过监听消息,达到捕获echarts的一些事件。而框选结束的事件brushEnd,就是我们要处理的事件。因此我们需要监听brushEnd事件,并在这个事件发生变化的时候,执行一系列的代码。
下面就是我们监听brushEnd事件,并在brushEnd结束后切换框选颜色,绘制一张新表的实现代码。
// 监听brushEnd事件
advanceTFChart.on('brushEnd', function (params: any) {
let index = params.areas.length;
const chart = advanceTFChart as any;
const componentsViews: any = chart._componentsViews;
// 每次框选结束后,都重新调整一下框选的颜色
// 确保每个框子的颜色都不一样
for (let i in componentsViews) {
if (componentsViews[i].type == 'brush') {
const brushController = componentsViews[i]._brushController;
if (index >= 10) {
index = 9;
}
brushController._brushOption.brushStyle.fill = BRUSH_STYLES[index].fill;
brushController._brushOption.brushStyle.stroke = BRUSH_STYLES[index].stroke;
}
}
...
for (let i in params.areas) {
// 处理参数中带回来的数据
...
}
})
通过使用echarts内部的消息机制,我们就实现了对散点的框选以及框选后事件的处理。同样的,我们如果想要清除所有的框子,也可以通过echarts内部的消息机制来实现。具体的方法是发送两条消息,一条是axisAreaSelect,另一条则是命令为clear的brush消息。具体的实现代码如下:
function clearSelect() {
const _chart = advanceTFChart as any;
const api = _chart._api;
api.dispatchAction({
type: 'axisAreaSelect',
intervals: []
});
api.dispatchAction({
type: 'brush',
command: 'clear',
// Clear all areas of all brush components.
areas: []
});
brushAreas.value = []
}
不过总得来说,这是一种hacker式的方式。这种方式通过调用echarts内部的api,让我们对图表具备更强的操作能力。但是同样的,将来如果echarts版本升级或内部机制发生了一些变动,我们调整起来也许会有一定的麻烦。