Echarts折线图自定义框选并高亮选中曲线,计算平均值(含源代码)

背景

最近在做一个工程相关的项目,需要用折线图展示传感器采集到的数据,并对采集到的数据可以圈定某一部分计算出平均值。现在将这个功能单独抽出来做一个示例项目,并进行记录。

示例项目最后展示效果:

示例项目传送门:echartsDemo: Echarts折线图自定义框选并高亮选中曲线,计算平均值 (gitee.com)

大致思路

  1. 监听折线图的mousedown、mousemove、mouseup事件;
  2. mousedown 记录鼠标按下的初始位置,并监听 mousemove、mouseup事件;
  3. mousemove 对选择框进行绘制;
  4. mouseup 记录鼠标松开的位置,并对数据源循环计算出在位置内的坐标点,以及对连续区间内的线进行高亮,取消move、up事件的监听。

用到的插件

  1. vue/cli 利用脚手架搭建一个vue项目
  2. vue2
  3. echarts 画折线图

数据格式

具体的完成数据在示例项目的 public/echartsData.txt 内。

less 复制代码
x轴: ['2024-03-09 17:07:00', '2024-03-09 17:06:59',......, n]
y轴: [73.3, 72.9,......, n]

实现步骤

只粘贴出主要代码,文末会有示例项目的gitee链接。

1. 创建一个基本的echarts项目

xml 复制代码
<template>
   <div style="width: 1000px;height: 600px;" ref="echartsId" id="echartsId"></div>
</template>

<script>
export default {
    methods: {
        init() {
            ......
            this.echartsInstance = echarts.init(document.querySelector('#echartsId'));
            this.echartsInstance.setOption(this.option);
            ......
        }
    }
}
</script>

2. 画框的基本语法

画框三步骤:按下、移动、放开,吧唧就画了一个框。

如果直接利用折线图实例去监听(on)鼠标事件,只会在点击到图形处才生效。

但当前要实现的框选功能,需要在折线图内任何地方都可以触发,所以参考文档,可知需要用到 this.echartsInstance.getZr() 监听 zrender事件,才能监听到空白区域的鼠标事件。

直接在template中增加选择框

xml 复制代码
<template>
  <div id="app">
    <div style="position: relative;">
      <div style="width: 1000px;height: 600px;" ref="echartsId" id="echartsId"></div>
      <div ref="boxSelect" class="box-select"></div>
    </div>
  </div>
</template>

<script>
......
mounted() {
  this.selectionDiv = this.$refs.boxSelect
}
......
</script>

按下 mousedown

kotlin 复制代码
init() {
    ......
    this.echartsInstance.setOption(this.option);
    this.boxSelectEvent()
}
// 该方法在实例化折线图后调用
boxSelectEvent() {
  this.echartsGetZr = this.echartsInstance.getZr() // 为了监听折线图空白区域鼠标事件
  setTimeout(() => {
    this.echartsGetZr.on('mousedown', (e) => {
      // 获取鼠标按下坐标点
      const {offsetX, offsetY} = e;
      this.selectionPoint = {offsetX, offsetY};
      // 并设置选择框 初始位置、宽高
      this.selectionDiv.style.left = offsetX + "px";
      this.selectionDiv.style.top = offsetY + "px";
      this.selectionDiv.style.width = "0px";
      this.selectionDiv.style.height = "0px";
      // 对move、up事件进行监听
      this.echartsGetZr.on("mousemove", this.mousemove);
      this.echartsGetZr.on("mouseup", this.mouseup);
    })
  }, 1000)
},

移动 mousemove

ini 复制代码
mousemove(e) {
  const x = e.offsetX
  const y = e.offsetY
  const width = Math.abs(this.selectionPoint.offsetX - x);
  const height = Math.abs(this.selectionPoint.offsetY - y);
  // 根据移动坐标 和 初始坐标,计算处宽高
  this.selectionDiv.style.width = width + "px";
  this.selectionDiv.style.height = height + "px";
  this.selectionDiv.style.visibility = "visible";
  // 如果移动的坐标 比 初始坐标小,需要切换选择框的位置
  x < this.selectionPoint.offsetX && (this.selectionDiv.style.left = x + "px");
  y < this.selectionPoint.offsetY && (this.selectionDiv.style.top = y + "px");
},

放开 mouseup

kotlin 复制代码
mouseup() {
  this.selectionDiv.style.visibility = "hidden";
  // 取消监听事件
  this.echartsGetZr.off("mousemove", this.mousemove);
  this.echartsGetZr.off("mouseup", this.mouseup);
},

3. 如何知道Echarts某个点的像素坐标

主要是用到 convertToPixel 语法。

kotlin 复制代码
    this.echartsInstance.convertToPixel('grid', [x轴坐标, y轴坐标])

在该示例项目中,x轴为时间,y轴为数值。所以具体的代码如下

javascript 复制代码
    this.echartsInstance.convertToPixel('grid', [某一个时间点, Number(yValue))])

4. 计算坐标点是否在画的框内

需要先对绘制的选择框判断,是否宽高都大于0。然后得到选择框最后的左上角坐标和宽高,便于计算。

kotlin 复制代码
mouseup(e) {
  const {offsetX, offsetY} = e;
  const width = Math.abs(this.selectionPoint.offsetX - offsetX);
  const height = Math.abs(this.selectionPoint.offsetY - offsetY);
  this.selectionDiv.style.visibility = "hidden";
  this.echartsGetZr.off("mousemove", this.mousemove);
  this.echartsGetZr.off("mouseup", this.mouseup);
  if (width > 0 && height > 0) {
  // 对鼠标放开坐标和初始坐标进行判断,看谁小,谁就是左上角的坐标
    this.boxSelectOfPoint({
      offsetX: offsetX < this.selectionPoint.offsetX ? offsetX : this.selectionPoint.offsetX,
      offsetY: offsetY < this.selectionPoint.offsetY ? offsetY : this.selectionPoint.offsetY,
      width,
      height,
    });
  }
},

循环数据源,对坐标点进行像素转换,然后判断坐标是否在框选范围内。最后利用 echarts的visualMap语法,让选中的区域进行高亮显示。 配置项中使用 lt(小于,less than),gt(大于,greater than),lte(小于等于 less than or equals),gte(大于等于,greater than or equals)来表达边界

javascript 复制代码
boxSelectOfPoint(result) {
  const {offsetX, offsetY, width, height} = result;
  // type dataItem = { x: string, y: string|number, index: number }
  // dataList: Array<dataItem>
  //  对这些数据进行收集,方便处理。 实际项目中比这个复杂很多,这里是简化过的
  const dataList = []

  this.dataSource.forEach((item, index) => {
    const x = item.createTime
    const y = item.value

    // 得到一个转换的坐标
    let p = this.echartsInstance.convertToPixel('grid', [x, Number(y)])
    //
    const flag = p[0] > offsetX && p[0] < offsetX + width && p[1] > offsetY && p[1] < offsetY + height;

    if (flag) {
      dataList.push({
        x, y, index
      })
    }
  })

  if (dataList.length > 0) {
    this.selectResult = dataList
    this.echartsInstance.setOption({
      // 在实际项目中是多条曲线的,如果让多条曲线高亮,则数组
      visualMap: [
        {
          type: "piecewise",
          show: false,
          dimension: 0,
          seriesIndex: 0, // 这里需要和之前的  series 对上
          piecewise: true,
          outOfRange: {
            // color: ['#00000', 'red'][index]
            color: ['#00000'][0] // 这里如果多条曲线的话 则需要一一对应非框选中的颜色,
          },
          pieces: [
            {
              gt: dataList[0].index, // 
              lt: dataList[dataList.length - 1].index,
              color: "rgba(41,11,236,0.68)",//大于0小于12为蓝色
            },
          ]
        }
      ]
    })
  }
},

5. 给线段加上曲线高亮

代码上第4点中也有提到。主要是通过计算得到在范围内的坐标点,然后设置 visualMap 的配置项使某一段连续区间能够单独设置颜色。

php 复制代码
 this.echartsInstance.setOption({
      // 在实际项目中是多条曲线的,如果让多条曲线高亮,则数组
      visualMap: [
        {
          type: "piecewise",
          show: false,
          dimension: 0,
          seriesIndex: 0, // 这里需要和之前的  series 对上
          piecewise: true,
          outOfRange: {
            // color: ['#00000', 'red'][index]
            color: ['#00000'][0] // 这里如果多条曲线的话 则需要一一对应非框选中的颜色,
          },
          pieces: [
            {
              gt: dataList[0].index, // 
              lt: dataList[dataList.length - 1].index,
              color: "rgba(41,11,236,0.68)",//大于0小于12为蓝色
            },
          ]
        }
      ]
    })

6. 计算平均值

啊哈,都得到选中数据了,计算平均值当然一口闷掉。

javascript 复制代码
<template>
    <div v-show="selectResult.length > 0" style="text-align: center;" v-html="selectResultStr()"></div>
</template>

getAvgValue(list) {
  if (list.length > 1) {
    return (list.reduce((acc, cur) => acc + cur, 0) / list.length).toFixed(2)
  }
},
selectResultStr() {
  if (this.selectResult.length === 0) return ''
  return `<span style='margin-right: 30px;'>框选时间: <span style="font-size: 16px;font-weight: bold;">${this.selectResult[0].x} - ${this.selectResult[this.selectResult.length - 1].x}</span>
            平均值: <span style="font-size: 16px;font-weight: bold;">${this.getAvgValue(this.selectResult.map(item => item.y))}</span></span>`
},

7. 选择框的交互优化

因为用的是鼠标左键进行点击选择,导致和echarts的拖拽事件冲突,交互不好。所以就使用鼠标右键进行选择,保留echarts默认功能。

kotlin 复制代码
boxSelectEvent() {
  this.echartsGetZr = this.echartsInstance.getZr() // 为了监听折线图空白区域鼠标事件
  // 对右键默认事件阻止
  this.echartsGetZr.on('contextmenu', () => {
    event.preventDefault()
  })
  setTimeout(() => {
    this.echartsGetZr.on('mousedown', (e) => {
      // 获取鼠标按下坐标点
      if (e.which === 3) {
        const {offsetX, offsetY} = e;
        this.selectionPoint = {offsetX, offsetY};
        // 并设置选择框 初始位置、宽高
        this.selectionDiv.style.left = offsetX + "px";
        this.selectionDiv.style.top = offsetY + "px";
        this.selectionDiv.style.width = "0px";
        this.selectionDiv.style.height = "0px";
        // 对move、up事件进行监听
        this.echartsGetZr.on("mousemove", this.mousemove);
        this.echartsGetZr.on("mouseup", this.mouseup);
      }
      
    })
  }, 1000)
},

结语

echartsDemo: Echarts折线图自定义框选并高亮选中曲线,计算平均值 (gitee.com)

项目简单,请多指正。

相关推荐
Book_熬夜!3 天前
Python基础(六)——PyEcharts数据可视化初级版
开发语言·python·信息可视化·echarts·数据可视化
范特西是只猫4 天前
echarts 自定义标注样式&自定义tooltip弹窗样式
前端·javascript·echarts
范特西是只猫5 天前
echarts map地图动态下钻,自定义标注,自定义tooltip弹窗【完整demo版本】
前端·javascript·echarts
人工智能的苟富贵5 天前
微信小程序中实现类似于 ECharts 的图表渲染及优化
微信小程序·小程序·echarts
GHUIJS5 天前
【Echarts】vue3打开echarts的正确方式
前端·vue.js·echarts·数据可视化
Peanuts.6 天前
VUE使用echarts编写甘特图(组件)
开发语言·javascript·echarts
GHUIJS8 天前
【Echarts】使用多横坐标轴展示近十五天天气预报
javascript·echarts
暖锋丫9 天前
echarts实现湖南省地图并且定时轮询
前端·javascript·echarts
时光匆匆岁月荏苒,转眼我们已不是当年9 天前
【前端echarts】echarts双饼图与easyui列表联动
前端·echarts·easyui
SnowMan199310 天前
高级 ECharts 技巧:自定义图表主题与样式
信息可视化·数据分析·echarts