JavaScript获取echart曲线上任意点位的值详解

作者:石云升 时间:2024-05-02 16:57:52 

需求背景

智慧农业里有一个很重要的功能是控制温室生长环境,让农作物生长的更好。于是,我们需要在曲线上根据不同农作物设置不同的环境数据。为了方便用户修改这些数据,我们需要支持用户在曲线上进行拖拽,并且拖拽后还需要把拖拽后的关键点位返回给后端。后面温度传感器收到温度数据后,会发给后端和前端。两端都需要判断传过来的温度是否在曲线之间。如果超出或者下沉需要触发相应的事件。

需求调研

本来以为这个需求很简单,导入echarts,设置两条曲线,并支持拖动。拖动。这些都属于echarts开放的功能,但等到要查询某个时间点曲线上的值时,才发现echarts根本就没开放这个功能,查看源码发现他是用贝塞尔算法生成的曲线,好吧,既然你不支持,那就只能自己实现了。

后面找了下资料,发现一个贝塞尔的js开源库,看了下它的说明,发现有get(t)方法获取到曲线上Y轴的值。参数t表示两个关键点之间x轴值。由于我们传入的x轴是时间,所以这个t就是x轴的时间。

所以,理论上,如果两个使用的算法是一致的,那么从曲线上获得的值就应该是一样的。

实现步骤

第一步,安 * ezier-js

npm install bezier-js

第二步,初始化贝塞尔对象

constructor(input = {points: [20, 30, 40], ts: [0, 60000 * 60 * 24]}) {
   this.line = null
   this.keyPoints = []//根据输入input,生成关键的点坐标数组    [   x1坐标,y1坐标,  x2坐标,y2坐标     ]
   this.tsStart = 0
   this.tsEnd = 0
   this.init(input)
   this.input = input
}
init(input) {
   try {
       if (![input].$has('ts')) input.ts = [0, 60000 * 60 * 24]//如果没有ts时间,默认赋值为1天的时间周期
       if (![input].$has('points', 'ts') || input.points.length < 2 || input.ts.length !== 2) {
           console.log('【bezier】错误!实例化曲线时参数错误')
           return//最小2个点
       }
       this.tsStart = Number(input.ts[0])
       this.tsEnd = Number(input.ts[1])
       let tsLen = this.tsEnd - this.tsStart
       if (tsLen < 0) {
           console.log('【bezier】错误!实例化曲线时参数错误,时间长度错误')
           return
       }
       let stepLen = tsLen / (input.points.length - 1)
       let points = []
       let x = input.ts[0]
       input.points.forEach(y => {
           points.push({x: x})
           points.push({y: y})
           x += stepLen
       })
       points.splice(points.length - 2, 2)//删除最后一组坐标(2个元素)
       points.push({x: input.ts[1]})//再用input的值填充,避免因为平均值导致精度的问题
       points.push({y: input.points[input.points.length - 1]})
       this.keyPoints = points
       this.line = new Bezier(points)  // points =  [   x1坐标,y1坐标,  x2坐标,y2坐标     ]
       console.log('生成贝塞尔曲线的数据', this.line, points)
   } catch (e) {
       console.log('【bezier】错误!在实例化对象时错误', e)
   }
}

第三步,我们曲线上看到的是时间是&ldquo;时+分&rdquo;格式,传入的要转化为时间戳

// 获取指定时间戳的y值数据
// ts横坐标,时间戳
// 输出 {x,y} ,x就是 ts
get(ts = 0) {
   let duration
   if (ts <= this.tsStart) {
       duration = 0
   } else if (ts <= this.tsEnd) {
       duration = ts - this.tsStart
   } else {
       duration = this.tsEnd - this.tsStart
   }
   let b = duration / (this.tsEnd - this.tsStart)
   return this.line.get(b) // b取值0~1,小数.0表示曲线第一个点,1表示最后1个点
}

第四步,服务端给我们的关键点位可能有几十个,如果我通过几十个点生成贝塞尔对象,在通过get值去查询时候,由于它内部的算法,除了收尾两个关键点外,其他关键点位根据时间查询出的Y值会有偏差。这跟我们在echarts曲线上看到的值并不一样。我猜原因在于echarts的平滑曲线并不是由首尾两个点生成的,而是由两个两个关键点生成一段一段平滑曲线组合而成的。

为了验证我的猜想,我写了个demo,通过6个关键点在echarts上生成了二条曲线。同时,我通过bezier-js的get(t)方法获取更多点位,然后放到echars上,看两者的曲线是否重合。

事实证明,我的猜想是对的。两者曲线是重合的。

echarts 根据六个点生成的曲线

JavaScript获取echart曲线上任意点位的值详解

下图是根据两个关键点之间计算多个节点组合成的曲线

JavaScript获取echart曲线上任意点位的值详解

所以,我们在计算某个时间轴的Y轴时,要先判断查询的X轴时间属于哪两个点之间,然后再用get(t)方法查Y值

/**
* 查询曲线上某个时间点曲线上具体的值,这里查询的是上限。
*/
getPointValueByTime() {
   // 把时间转换成毫秒
   let time = this.timeToSec(this.defaultTime)
   // 判断这个时间在哪两个关键点之间
   // 先获取曲线上所有时间点的数组
   let preData = []
   for (var i = 0; i < upperLimit.length; i++) {
       let itemTime = this.timeToSec(upperLimit[i][0])
       // 如果查询的时间小于当前的item,这表示在这个item和上一个item之间。则通过这两个item生成贝塞尔对象,然后用get方法查询time值
       if (time <= itemTime && i != 0) {
           // 查询的点就是preData 和item的数组
           let pointArray = []
           pointArray.push(preData[1])
           pointArray.push(upperLimit[i][1])
           const bezier = new BezierLine({points: pointArray, ts: [preData[0], itemTime]})
           let data = bezier.get(time)
           console.log('查询到当前时间' + time + '的值为', data)
           this.searchValueByTime = parseFloat(data.y).toFixed(2)
           break
       } else {
           // 上一个不符合的对象
           preData = [itemTime, upperLimit[i][1]]
       }
   }
},

到此,我们就可以通过任意一个X轴时间来获得对应的Y轴value值了。当然,文章里只是把实现思路讲了,贴出来的代码不是全部,只是供大家理解。完整版代码我已经上传到码云,如果有什么bug,大家可以留言告知一下。 下载地址:GetEchartsCurveData

来源:https://juejin.cn/post/7148777191021477901

标签:JavaScript,echart,曲线点位值
0
投稿

猜你喜欢

  • python3处理含有中文的url方法

    2021-04-10 02:42:45
  • Python中三种花式打印的示例详解

    2022-06-27 06:51:11
  • 解决python 打包成exe太大的问题

    2021-09-22 11:33:59
  • 下拉列表两级连动的新方法(二)

    2009-06-04 18:22:00
  • Python 在字符串中加入变量的实例讲解

    2023-01-27 10:51:21
  • Python semaphore evevt生产者消费者模型原理解析

    2021-11-14 12:52:39
  • vue实现价格日历效果

    2023-07-02 17:01:14
  • Python 如何实现访问者模式

    2021-08-10 20:49:53
  • 对python 树状嵌套结构的实现思路详解

    2022-02-04 15:45:06
  • Python基础教程之tcp socket编程详解及简单实例

    2021-04-18 12:04:29
  • python人工智能深度学习算法优化

    2023-04-21 15:46:40
  • 在SQL Server中将数据导出为XML和Json的方法

    2024-01-12 22:10:17
  • Python pandas处理缺失值方法详解(dropna、drop、fillna)

    2023-04-03 03:45:36
  • T-SQL问题解决集锦 数据加解密全集

    2012-07-11 15:34:08
  • 《CSS禅意花园》学习笔记

    2008-10-20 12:43:00
  • go语言静态库的编译和使用方法

    2024-05-09 09:40:05
  • 定格动画浅析(一)

    2009-07-30 12:50:00
  • WEB2.0网页制作标准教程(6)XHTML代码规范

    2007-12-13 13:03:00
  • 戴尔是如何设计新官网首页的

    2008-07-08 19:02:00
  • 使用js实现数据格式化

    2024-05-03 15:05:32
  • asp之家 网络编程 m.aspxhome.com