echarts 两个曲线之间填充 可以用两条曲线 ,第一条填充白色 ,然后 第2条填充想要的颜色 ,如下面的代码
option = {title: {text: '堆叠区域图'},tooltip : {trigger: 'axis'},legend: {data:['最小值','最大值']},toolbox: {feature: {saveAsImage: {}}},grid: {left: '3%',right: '4%',bottom: '3%',containLabel: true},xAxis : [{type : 'category',boundaryGap : false,data : ['周一','周二','周三','周四','周五','周六','周日']}],yAxis : [{type : 'value'}],series : [{name:'最小值',type:'line',// stack: '总量',areaStyle: {color:'#8DA2E4',opacity:1,origin:"start"},data:[320, 332, 301, -40, -30, 330, 320]},{name:'最大值',type:'line',// stack: '总量',label: {normal: {show: true}},areaStyle: {color:'#ffffff',opacity:1,origin:"start"},data:[10, 30, 40, -60, -50, 40, 50]}]
};
这样有个弊端,当你 想要绘制markarea 区域的时候 ,由于下面的 曲线填充是白色的会被遮住
所以想了个办法 ,分两片区域绘制 分上半部分和下半部分 ,代码如下
import { useMemo, useState } from "react";
import ReactECharts from "echarts-for-react";
import dayjs from "dayjs";const areaStyle = {opacity: 0.8,color: {type: "linear",x: 0,y: 0,x2: 0,y2: 1,colorStops: [{offset: 0,color: "rgb(0, 221, 255)",},{offset: 1,color: "rgb(77, 119, 255)",},],},
};
const getCommonSeries = (name, data, markAreaData, areaStyle = undefined) => {return {name,areaStyle,data,type: "line",smooth: true,lineStyle: { width: 0 },showSymbol: false,markArea: {itemStyle: {color: "rgba(255, 0, 0, 0.2)",},data: markAreaData,},};
};export const AlarmCurve = ({pointCurveData = [], // 点曲线数据alarmData = [], // 报警数据max = 0, // 最大值min = 0, // 最小值predictData = [], // 点预测数据
}) => {const pointCurveMaxData = useMemo(() => {return pointCurveData.map((item) => [item[0], item[1] + max]);}, [pointCurveData, max]);const pointCurveMinData = useMemo(() => {return pointCurveData.map((item) => [item[0], item[1] - min]);}, [pointCurveData, min]);const markAreaData = useMemo(() => {// 获取 alarmData 中连续的1的区间,记录的是 的开始和结束item[0]let result = [];let start = null;for (let i = 0; i < alarmData.length; i++) {if (alarmData[i][1] === 1) {if (start === null) {start = alarmData[i][0];}if (i === alarmData.length - 1 || alarmData[i + 1][1] === 0) {result.push([{ xAxis: dayjs(start).format("MM-DD HH:mm:ss") },{ xAxis: dayjs(alarmData[i][0]).format("MM-DD HH:mm:ss") },]);start = null;}}}console.log("result", result);return result;}, [alarmData]);const opt = useMemo(() => {if (pointCurveData.length === 0) {return {};}// line optreturn {title: {text: "报警曲线",// align centerleft: "center",},tooltip: {trigger: "axis",axisPointer: {type: "cross",label: {backgroundColor: "#6a7985",},},},grid: {top: 30,left: "3%",right: "4%",bottom: "3%",containLabel: true,},xAxis: [{type: "category",boundaryGap: false,data: pointCurveData.map((item) =>dayjs(item[0]).format("MM-DD HH:mm:ss"),),},],yAxis: {type: "value",},series: [{name: "Line 1-max",type: "line",stack: "max",smooth: true,lineStyle: {width: 0,},showSymbol: false,data: pointCurveData.map((item) => item[1]),},{name: "Line 1-base",type: "line",stack: "max",smooth: true,lineStyle: {width: 0,},showSymbol: false,emphasis: {focus: "series",},areaStyle: {opacity: 0.3,color: "rgb(13,185,88)",},data: pointCurveData.map((item) => max),},{...getCommonSeries("实时值",pointCurveData.map((item) => item[1]),undefined,),lineStyle: {width: 2,},},{name: "Line 1-min",type: "line",stack: "min",smooth: true,lineStyle: {width: 0,},showSymbol: false,emphasis: {focus: "series",},data: pointCurveData.map((item) => item[1] - min),},{name: "Line 3",type: "line",stack: "min",smooth: true,lineStyle: {width: 0,},showSymbol: false,areaStyle: {opacity: 0.3,color: "rgb(13,185,88)",},data: pointCurveData.map((item) => min),},{...getCommonSeries("预测值",predictData.map((item) => item[1]),markAreaData,),lineStyle: {width: 2,},},],};}, [pointCurveData,alarmData,max,min,predictData,pointCurveMaxData,pointCurveMinData,]);return <ReactECharts option={opt} />;
};
模拟数据调用
import dayjs from "dayjs";
import { AlarmCurve } from "./alarm-curve";export const movingAverage = (data, numberOfPricePoints) => {return data.map((row, index, total) => {const start = Math.max(0, index - numberOfPricePoints);const subset = total.map((x) => x[1]).slice(start, index + 1);const sum = subset.reduce((a, b) => a + b, 0);return [row[0], sum / subset.length];});
};export const FuXianAlarmCurve = () => {const now = dayjs();const max = 20;const min = 10;// 模拟一个1000个点的数据1秒const pointCurveData = movingAverage(Array.from({ length: 1000 }, (_, i) => [now.add(i, "second").valueOf(),Math.random() * 100,]),20,);// 模拟一个1000个点的数据1秒const predictData = movingAverage(Array.from({ length: 1000 }, (_, i) => [now.add(i, "second").valueOf(),Math.random() * 100,]),100,);// 遍历pointCurveData和 predictData,如果predictData 对应的值在min和max之间, 则返回0 否则返回1const alarmData = pointCurveData.map((item, index) => {const predictItem = predictData[index];if (predictItem[1] > item[1] + max || predictItem[1] < item[1] - min) {return [item[0], 1];}return [item[0], 0];});return (<AlarmCurvepointCurveData={pointCurveData}predictData={predictData}alarmData={alarmData}max={max}min={min}/>);
};
效果