ECharts - echarts鼠标框选数据-数据视觉映射

657 阅读5分钟

ECharts 框选

实现在 ECharts 散点图中使用鼠标框选数据的功能,借助 ECharts 提供的数据视觉映射(Visual Mapping)和ECharts 的 graphic 组件,事件处理机制

示例

示例中,我们监听 mousedownmousemovemouseup 事件,通过 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 散点数据优化

当散点数据太多时,为了提高显示效果和性能,你可以考虑以下一些优化策略:

  1. 数据聚合: 对大量的散点数据进行聚合,将相邻的点合并成一个更大的点,以降低点的数量。这样可以保留趋势,同时减少数据量。
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);

  1. 采样显示: 从大量的数据中随机抽样一部分数据来显示,这样可以降低数据密度,提高性能。ECharts 提供了 sampling 配置项,可以用于抽样显示数据。 "sampling", "average";
symbolSize: 2, // 设置散点的大小 
sampling: 'lttb', // 采样算法,'lttb' 表示 Largest Triangle Three Buckets
  1. 分级显示: 将数据分为多个级别,根据缩放级别选择性地显示数据。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);

  1. 使用热力图: 如果数据分布不均匀,可以考虑使用热力图来表现数据密度,而不是散点图。热力图可以更好地展示数据的分布情况。
  2. 分块加载数据: 将大量的数据划分成多个块,仅加载可视区域内的数据。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);
});

问题

  1. 坐标系转换问题: 确保 startend 是在图表坐标系中正确的坐标。在之前的代码中,我们使用了 convertFromPixel 方法将屏幕坐标转换为图表坐标。请确保这一步转换是正确的。
  2. 数据是否符合条件: 遍历数据点时,确保每个数据点的坐标都在预期的范围内。你可以在 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]