一、需求描述
最近遇到了一个需求,需要接收实时数据绘制曲线图,并且能够根据选中项绘制多个图表,固定区间使用红色线段。
摸着石头过河,尝试了许多方式,踩了许多坑。记录一下。
二、效果展示
三、解决方法
1、确定需要连接的topic,以及容器的准备
使用可以多选的下拉选择,直接遍历选中的数据,即可将绘制图形的容器准备好。
// 确定需要连接的topic,以及容器的准备
//使用可以多选的下拉选择,直接遍历选中的数据,即可将绘制图形的容器准备好
<div class="topic-select">
<span>topic选择</span>
<el-select
v-model="topic"
:multiple="true"
collapse-tags
@change="changeTopic"
style="width: 30%"
placeholder="请选择topic"
>
<el-option
v-for="item in topicList"
:key="item.value"
:label="item.label"
:value="item.value"
></el-option>
</el-select>
<el-button @click="connectMqtt">确定</el-button>
</div>
<div
class="chart-container"
:id="`chart-container-${item}`"
v-for="(item, index) in topic"
:key="index"
></div>
2、连接mqtt,接收数据
// 初始化分段区间的二维数组
this.pieces = new Array(this.topic.length).fill([]);
// 循环select的选中项进行建立连接
this.topic.map((item, index) => {
for (let i = 0; i < this.topicList.length; i++) {
if (item == this.topicList[i].value) {
//三个参数 订阅的主题、当前主题的分段区间、当前主题的下标,用来存储每个图表的数据
this.createMqtt(item,this.topicList[i].threshold index);
}
}
});
3、接受数据,处理区间
createMqtt(topic, threshold, index) {
this.chartData[index] = [];
this.pieces[index] = [];
//threshold区间的格式例如 [[36,45],[88,98]]
// 需要将区间扩充到对与整个线段 进行分段
// [[0,36],[36,45],[45,88],[88,98],[98,∞]] 类似这种形式
// 使用echarts中的visualMap中的pieces进行将整个折线进行分段
// 下面这种方式并不高明,但是解决了问题
for (let i = 0; i < threshold.length; i++) {
if (threshold.length == 1) {
this.pieces[index].push({
gt: 0,
lte: threshold[i][0],
color: "green",
});
this.pieces[index].push({
gt: threshold[i][0],
lte: threshold[i][1],
color: "red",
});
this.pieces[index].push({
gt: threshold[i][1],
color: "green",
});
}
if (i == 0) {
this.pieces[index].push({
gt: 0,
lte: threshold[i][0],
color: "green",
});
this.pieces[index].push({
gt: threshold[i][0],
lte: threshold[i][1],
color: "red",
});
} else if (i == threshold.length - 1) {
this.pieces[index].push({
gt: threshold[i - 1][1],
lte: threshold[i][0],
color: "green",
});
this.pieces[index].push({
gt: threshold[i][0],
lte: threshold[i][1],
color: "red",
});
this.pieces[index].push({
gt: threshold[i][1],
color: "green",
});
} else {
this.pieces[index].push({
gt: threshold[i - 1][1],
lte: threshold[i][0],
color: "green",
});
this.pieces[index].push({
gt: threshold[i][0],
lte: threshold[i][1],
color: "red",
});
}
}
//创建链接,接收数据
mqttTopic = "xxx-" + topic;
mqtt[index] = new mqttHandle(mqttTopic);
client = mqtt[index].createConnect();
// 初始化图表
this.initChart(topic, index);
client.on("message", (topic, message) => {
// this.chartData的数据格式
//[
//[[time,data],[time,data]],//图1
//[[time,data],[time,data]],//图2
//]
try {
let receiveNews = JSON.parse(message.toString());
this.chartData[index].push([receiveNews.time, receiveNews.data]);
} catch (error) {}
});
},
4、echarts的配置项
initChart(container, index) {
let chartDom = document.getElementById(`chart-container-${container}`);
let myChart = echarts.init(chartDom);
let option = {
title: {
text: container,
textStyle: {
fontSize: 20,
color: "#fff",
},
},
tooltip: {
trigger: "axis",
axisPointer: {
animation: false,
},
},
xAxis: {
// type 一定要设置为category,才能分段成功 !!!!!!
type: "category",
splitLine: {
show: false,
},
name: "时间",
boundaryGap: false,
axisLabel: {
// rotate: 45,
color: "#fff",
},
axisLine: {
lineStyle: {
color: "#fff",
},
},
nameTextStyle: {
fontSize: 16,
color: "#fff",
},
},
dataZoom: {
type: "slider",
},
visualMap: {
show: false,
dimension: 0,
//处理好的分段区间 index对应每个图表的分段数据
pieces: this.pieces[index],
},
yAxis: {
type: "value",
show: true,
boundaryGap: [0, "100%"],
splitLine: {
show: false,
},
// min:150,
axisLabel: {
color: "#fff",
},
axisLine: {
lineStyle: {
color: "#fff",
},
},
},
series: [
{
name: "实时数据",
type: "line",
showSymbol: false,
data: this.chartData[index],
},
],
};
myChart.setOption(option);
setInterval(() => {
myChart.setOption({
series: [
{
data: this.chartData[index],
itemStyle: {
show: true, // 显示标记点
color: "#1890ff", //标记点颜色
},
},
],
});
}, 500);
},
四、官方示例
可以直接复制展示
function randomData() {
now = new Date(+now + oneDay);
value = value + Math.random() * 21 - 10;
return {
name: now.toString(),
value: [
[now.getFullYear(), now.getMonth() + 1, now.getDate()].join('/'),
Math.round(value)
]
};
}
let data = [];
let now = new Date(1997, 9, 3);
let oneDay = 24 * 3600 * 1000;
let value = Math.random() * 1000;
for (var i = 0; i < 1000; i++) {
data.push(randomData());
}
option = {
title: {
text: 'Dynamic Data & Time Axis'
},
tooltip: {
trigger: 'axis',
formatter: function (params) {
params = params[0];
var date = new Date(params.name);
return (
date.getDate() +
'/' +
(date.getMonth() + 1) +
'/' +
date.getFullYear() +
' : ' +
params.value[1]
);
},
axisPointer: {
animation: false
}
},
dataZoom:{
},
visualMap: {
show: false,
dimension: 0,
pieces: [
{
gt:0,
lte:300,
color:"green"
},{
gt:300,
lte:600,
color:"red"
},{
gt:300,
color:"green"
}
]
},
xAxis: {
type: 'category',
splitLine: {
show: false
}
},
yAxis: {
type: 'value',
boundaryGap: [0, '100%'],
splitLine: {
show: false
}
},
series: [
{
name: 'Fake Data',
type: 'line',
showSymbol: false,
data: data
}
]
};
setInterval(function () {
for (var i = 0; i < 5; i++) {
data.shift();
data.push(randomData());
}
myChart.setOption({
series: [
{
data: data
}
]
});
}, 1000);
五、总结
成功画出动态数据图表的分段绘制。这里有一点需要注意,visualMap中的pieces中 的gt,lte对应的数值实际上是你在x轴上的下标。也就意味着 如果区间是[40,50],那么分段只会在数据上的第40个点开始变色,而不是x轴横坐标的40。
个人记录,如有可供参考,非常荣幸。如有错误欢迎指正。