ECharts 框选
实现在 ECharts 散点图中使用鼠标框选数据的功能,借助 ECharts 提供的数据视觉映射(Visual Mapping)和ECharts 的 graphic 组件,事件处理机制
示例
示例中,我们监听 mousedown、mousemove 和 mouseup 事件,通过 convertFromPixel 方法将鼠标坐标转换为散点图上的坐标。在 mousemove 事件中,如果鼠标左键按下且正在进行框选,则将当前坐标添加到 selectedPoints 数组中。在 mouseup 事件中,停止框选。
方法是通过 chart.convertToPixel 方法将散点图上的数据转换为屏幕上的像素坐标,然后再进行判断。
遍历数据,判断点是否在框选区域内(两种方式:)
series.getData() 返回一个数据对象,该对象包含了该系列的数据以及与数据相关的一些方法。在 ECharts 中,数据对象通常具有以下属性和方法:
count(): 返回数据中的数据项(data item)的数量。getItemLayout(index): 返回指定索引处数据项的布局信息。通常包含数据项的坐标等位置信息。getItemModel(index): 返回指定索引处数据项的配置项(model)。可以通过这个方法获取该数据项的一些配置信息。getValues(dimensions, index): 返回指定索引处数据项的值。可以用于获取某个维度上的数值。each(...):迭代数据项,执行指定操作。
const selectedPoints = this.chart
.getModel()
.getSeries()[0]
.getData()
.getLayout('points')
?.map((point, index) => {
const x = point[0];
const y = point[1];
if (x >= start[0] && x <= end[0] && y >= start[1] && y <= end[1]) {
return [x, y];
} return null;
})
.filter(point => point !== null);
2.在 ECharts 中,getModel 方法实际上是属于 echarts 实例对象的方法
const series = this.chart.getModel().getSeries()[0];
if (series) {
const data = series.option.data;
// 确保数据存在
if (data) {
const selectedPoints = [];
for (let i = 0; i < data.length; i++) {
const point = data[i];
// 确保数据项布局信息存在
if (point) {
const x = point[0];
const y = point[1];
if(start[0]<=end[0]&&start[1]<=end[1]) {
if (x >= start[0] && x <= end[0] && y >= start[1] && y <= end[1]) {
selectedPoints.push([x, y]);
}
} else {
if (x >= start[0] && x <= end[0] && y<= start[1] && y >= end[1]) {
selectedPoints.push([x, y]);
}
}
}
}
this.selectedPoints = selectedPoints;
echarts 散点数据优化
当散点数据太多时,为了提高显示效果和性能,你可以考虑以下一些优化策略:
- 数据聚合: 对大量的散点数据进行聚合,将相邻的点合并成一个更大的点,以降低点的数量。这样可以保留趋势,同时减少数据量。
const option = {
xAxis: {},
yAxis: {},
series: [
{
type: 'scatter',
data: data, // 你的原始散点数据
symbolSize: 2, // 设置散点的大小
markPoint: {
symbol: 'circle', // 设置聚合点的标记形状
symbolSize: 10, // 设置聚合点的大小
data: [
{
type: 'average', // 使用平均值作为聚合方式,也可以使用其他统计值
name: 'average',
label: {
show: true,
formatter: 'Average: {c}', // 显示平均值
},
},
],
},
},
],
};
// 使用 setOption 方法将配置应用到图表实例
this.chart.setOption(option);
- 采样显示: 从大量的数据中随机抽样一部分数据来显示,这样可以降低数据密度,提高性能。ECharts 提供了
sampling配置项,可以用于抽样显示数据。 "sampling", "average";
symbolSize: 2, // 设置散点的大小
sampling: 'lttb', // 采样算法,'lttb' 表示 Largest Triangle Three Buckets
- 分级显示: 将数据分为多个级别,根据缩放级别选择性地显示数据。ECharts 支持通过事件监听来获取图表的缩放级别,可以根据缩放级别来动态调整数据显示的密度。
const option = {
xAxis: {},
yAxis: {},
visualMap: {
// 定义颜色映射范围
min: 0,
max: 100,
// 选择颜色范围,可以使用渐变色
inRange: {
color: ['blue', 'red'],
},
// 显示 visualMap 组件
show: true,
// 设置 visualMap 位置
orient: 'horizontal',
left: 'center',
bottom: 20,
},
series: [
{
type: 'scatter',
data: data, // 你的原始散点数据
symbolSize: 10, // 设置散点的大小
encode: {
// 定义数据映射到视觉通道的维度
tooltip: 2, // 显示 tooltip 的维度
itemStyle: 2, // 映射到颜色的维度
},
},
],
};
// 使用 setOption 方法将配置应用到图表实例
this.chart.setOption(option);
- 使用热力图: 如果数据分布不均匀,可以考虑使用热力图来表现数据密度,而不是散点图。热力图可以更好地展示数据的分布情况。
- 分块加载数据: 将大量的数据划分成多个块,仅加载可视区域内的数据。ECharts 支持通过事件监听获取可视区域的变化,根据可视区域来加载数据。
// 初始化图表配置
const option = {
xAxis: {},
yAxis: {},
series: [
{
type: 'scatter',
data: [], // 初始为空数组,后续动态添加数据
symbolSize: 2,
sampling: 'lttb',
markPoint: {
symbol: 'circle',
symbolSize: 10,
data: [
{
type: 'average',
name: 'average',
label: {
show: true,
formatter: 'Average: {c}',
},
},
],
},
},
],
};
// 使用 setOption 方法将配置应用到图表实例
this.chart.setOption(option);
// 在需要的时候动态加载数据
function loadData(start, end) {
// 模拟异步加载数据
setTimeout(() => {
const newData = generateDataInRange(start, end); // 生成指定范围的数据
this.chart.appendData({ seriesIndex: 0, data: newData });
}, 500);
}
// 监听图表的数据视图变化事件,例如滚动
this.chart.getZr().on('dataZoom', (params) => {
const zoomOption = params.batch[0].start;
// 根据缩放比例确定加载的数据范围
const totalDataCount = 100000; // 假设总数据量为 10 万
const visibleDataCount = Math.round(totalDataCount * (1 - zoomOption.end + zoomOption.start));
// 加载数据
const startIdx = Math.round(totalDataCount * zoomOption.start);
const endIdx = startIdx + visibleDataCount;
loadData(startIdx, endIdx);
});
问题
- 坐标系转换问题: 确保
start和end是在图表坐标系中正确的坐标。在之前的代码中,我们使用了convertFromPixel方法将屏幕坐标转换为图表坐标。请确保这一步转换是正确的。 - 数据是否符合条件: 遍历数据点时,确保每个数据点的坐标都在预期的范围内。你可以在
map函数中添加一些调试输出,检查每个数据点是否都被遍历到。
追加
多legend状态
tempSelsected: {}
let selectedLendData = {}
res.data.result.legend.data.forEach(item=>{
selectedLendData[item] = true})
//{ '1990': true, '2015': false, '2018': true, '2020': false, '2023': true }
this.tempSelsected = selectedLendData
this.option.legend = {
top:'3&',
left:"10%",
data: res.data.result.legend.data,
selected: selectedLendData
}
this.myChart.on('legendselectchanged',obj=>{
this.tempSelsected = obj.selected
})
//判断
for(const tempData of this.alldata.series){}
this.tempSelsected[tempData.name]