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)
}
}
第三步,我们曲线上看到的是时间是“时+分”格式,传入的要转化为时间戳
// 获取指定时间戳的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 根据六个点生成的曲线
下图是根据两个关键点之间计算多个节点组合成的曲线
所以,我们在计算某个时间轴的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
猜你喜欢
- Java的idea在更新2020.1时就更新了官方汉化,当时Pycharm还没用出现汉化,但这两天提示我更新2020.1.1的时候,我发现p
- 人生苦短,我用python!为什么很多人喜欢用python,因为包多呀,各种调包。但是调包有的时候也调的闹心,因为安装包不是失败就是很慢,很
- preface:做着最近的任务,对数据处理,做些简单的提特征,用机器学习算法跑下程序得出结果,看看哪些特征的组合较好,这一系列流程必然要用到
- 前言:我们平常会使用很多社交媒体,如微信、微博、抖音等等,在这些平台上面,我们会关注某些KOL,同时自己身边的亲朋好友也会来关注我们,成为我
- 一基础理解: var e = document.getElementById("selectId");e. option
- 本文实例为大家分享了Python QQBot库的QQ聊天机器人的具体代码,供大家参考,具体内容如下项目地址:https://github.c
- 一、多线程同步由于CPython的python解释器在单线程模式下执行,所以导致python的多线程在很多的时候并不能很好地发挥多核cpu的
- 在一些问答平台,经常会遇到一类关于Python的问题:“学习Python,应该选择哪款开发工具?”如果,对于有一定经验的开发者而言,肯定会对
- 如何制作关联的下拉菜单?看看代码:<form name=f1 METHOD="POST">
- 动态添加表单项iview的动态添加表单很简单,只需设置好表单项为一个array,添加新项目的时候就push一个默认好的值,剩下的iview会
- 本人机器环境:Windows 2008 R2MySQL 5.6以“Window下忘记Mysql的root密码”百度,找到一大堆解决方案。大多
- python中最基本的数据类型如下:Number(数字)String(字符串)List(列表)Tuple(元组)Dictionary(字典)
- 前言如果我们和面试官聊到事务的问题,怎么回答呢?先说下事务是什么,因为我们业务是比较复杂的,不可能一个sql就能解决的,涉及多个sql就组成
- 函数的概念,函数是将具有独立功能的代码块组织成为一个整体,使其具有特殊功能的代码集函数的作用,使用函数可以加强代码的复用性,提高程序编写的效
- 1) 使用字典dict()循环遍历出一个可迭代对象中的元素,如果字典没有该元素,那么就让该元素作为字典的键,并将该键赋值为1,如果存在就将该
- 一、项目概述本次项目目标是实现对自动生成的带有各种噪声的车牌识别。在噪声干扰情况下,车牌字符分割较困难,此次车牌识别是将车牌7个字符同时训练
- 前言: 上一篇讲了Python排序问题中比较经典的三个方法,(链接:关于Python排
- DATAFRAME中使用iat[1,0]和iloc[0,1]对元素进行修改。a = [("hahaha",1),(&qu
- 分支结构的应用场景迄今为止,我们写的Python代码都是一条一条语句顺序执行,这种结构的代码我们称之为顺序结构。然而仅有顺序结构并不能解决所
- 本文实例为大家分享了windows下mysql 8.0.12安装步骤及使用教程,供大家参考,具体内容如下1.到官网下载下载SQL。(1.1)