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)

项目简单,请多指正。

相关推荐
qq_589568102 小时前
node.js web框架koa的使用
笔记·信息可视化·echarts
王小王和他的小伙伴3 小时前
解决 vue3 中 echarts图表在el-dialog中显示问题
javascript·vue.js·echarts
游王子6 小时前
Vue.js组件(6):echarts组件
前端·vue.js·echarts
甜味橘阳9 小时前
echarts地图可视化展示
前端·javascript·echarts
图表制作解说(目标1000个图表)1 天前
ECharts散点图-气泡图,附视频讲解与代码下载
echarts·统计分析·数据可视化·散点图·大屏可视化
NiNg_1_2341 天前
Echarts连接数据库,实时绘制图表详解
前端·数据库·echarts
qq_589568102 天前
Echarts的高级使用,动画,交互api
前端·javascript·echarts
qq_589568102 天前
数据可视化echarts学习笔记
学习·信息可视化·echarts
m0_748244962 天前
echarts画风向杆
前端·数据库·echarts
学前端的小朱2 天前
Echarts实现大屏可视化
websocket·echarts·nodejs·vue3·vite·koa·cors