原生HTML + JavaScript版本
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>曲线形式的统计图示例</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/echarts/4.9.0-rc.1/echarts.min.js"></script>
<!-- <script src="https://cdnjs.cloudflare.com/ajax/libs/echarts/4.2.1/echarts.min.js"></script> -->
<style>
* {
margin: 0;
padding: 0;
}
width: 1400px;
height: 600px;
box-shadow: 0px 0px 10px
margin: 10px auto;
}
</style>
</head>
<body>
<div id="ecDiv"></div>
<button id="buttonid">保存数据</button>
</body>
<script type="text/javascript">
function deepClone(obj) {
if (obj === null || typeof obj !== 'object') {
return obj; // 如果是原始值或函数,直接返回
}
let clone;
if (Array.isArray(obj)) {
clone = []; // 如果是数组,创建一个空数组
for (let i = 0; i < obj.length; i++) {
clone[i] = deepClone(obj[i]); // 递归深拷贝数组的每个元素
}
} else {
clone = {}; // 如果是对象,创建一个空对象
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
clone[key] = deepClone(obj[key]); // 递归深拷贝对象的每个属性
}
}
}
return clone; // 返回深拷贝后的对象或数组
}
let color = ['#0071db', '#ff4868', 'yellow', 'green']
let xdata = [
"2024-07-10 00:00:00",
"2024-07-10 00:15:00",
"2024-07-10 00:30:00",
"2024-07-10 00:45:00",
"2024-07-10 01:00:00",
"2024-07-10 01:15:00",
"2024-07-10 01:30:00",
"2024-07-10 01:45:00",
"2024-07-10 02:00:00",
"2024-07-10 02:15:00",]
const data = [
{
name: '预测功率',
data: [
1.415,
2.685,
4.237,
5.993,
7.869,
9.818,
12.677,
16.624,
20.0,
20.0,
]
},
{
name: '手动修正',
data: [
1.415,
2.685,
4.237,
5.993,
7.869,
9.818,
12.677,
16.624,
20.0,
20.0,
]
},
{
name: '置信上限',
data: [1.698,
3.222,
5.085,
7.191,
9.443,
11.782,
15.213,
19.949,
24.0,
24.0,]
},
{
name: '置信下限',
data: [1.132,
2.148,
3.39,
4.794,
6.296,
7.854,
10.142,
13.299,
16.0,
16.0,]
}
]
const lineStyle = {
type: 'scatter',
smooth: true,
coordinateSystem: 'cartesian2d'
}
let batchSelected = [];
const computedSeries = (Data, dif = 0) => {
let selected = batchSelected;
let items = [];
Data.map((item, key) => {
let point = {
name: item.name,
data: item.data.map((val, i) => {
let dataValue = {
value: val,
symbolSize: 1,
}
return dataValue
}),
...lineStyle
}
items.push(point);
})
selected.map((item) => {
let seriesIndex = item.seriesIndex;
let findItem = items.find((v, k) => {
if (item.seriesIndex === k) {
return v;
}
});
findItem && findItem.data.map((val, key) => {
if (item.dataIndex.includes(key) && seriesIndex === 1) {
val['symbolSize'] = 10;
val['value'] = val.value + dif;
}
})
})
let lines = items.map((v, k) => {
let itemData = v.data.map((v) => v.value);
return {
z: 1,
type: 'line',
name: v.name,
data: itemData
}
})
items.push(...lines)
return items;
}
const echartDataSetData = (Data, dif = 0) => {
if (dif == 0) return;
let selected = batchSelected;
selected.map((item) => {
let seriesIndex = item.seriesIndex;
let Dataitem = Data[seriesIndex];
Dataitem && Dataitem.data.map((val, key) => {
if (item.dataIndex.includes(key) && seriesIndex === 1) {
Dataitem.data[key] = val + dif;
}
})
})
}
let onmousedownY = 0;
let oldDif = 0;
const seriesDataToGraphic = (series) => {
let dom = document.getElementById('ecDiv');
let myChart = echarts.getInstanceByDom(dom);
let graphic = [];
series.map((item, sindex) => {
if (item.type === 'scatter') {
item.data.map((dt, tk) => {
//! 等于10 标识选中
if (dt.symbolSize && dt.symbolSize === 10) {
let dataIndex = tk;
let position = myChart.convertToPixel({ seriesIndex: sindex }, [dataIndex, dt.value]);
let graphicItem = {
type: 'circle',
position: position,
shape: {
r: 5
},
invisible: true,
draggable: true,
onmousedown: echarts.util.curry((e) => {
onmousedownY = e.offsetY;
// myChart.dispatchAction({
// type: 'restore'
// })
myChart.dispatchAction({
type: 'takeGlobalCursor',
key: null,
})
myChart.dispatchAction({
type: 'brush',
areas: []
})
}),
ondrag: echarts.util.curry((dataI, e) => {
let onmousedownYToValue = myChart.convertFromPixel({ seriesIndex: sindex }, [dataI, onmousedownY])[1];
let ondragYToValue = myChart.convertFromPixel({ seriesIndex: sindex }, [dataI, e.offsetY])[1];
let dif = onmousedownYToValue - ondragYToValue;
let seriesData = computedSeries(data, -dif);
let graphics = seriesDataToGraphic(seriesData);
myChart.setOption({
series: seriesData,
graphic: graphics
})
oldDif = dif;
}, dataIndex),
ondragend: echarts.util.curry(() => {
echartDataSetData(data, -oldDif);
setSelectTitle(batchSelected)
}, dataIndex),
z: 100,
}
graphic.push(graphicItem);
}
})
};
})
return graphic;
}
const setSelectTitle = (selected) => {
let dom = document.getElementById('ecDiv');
let myChart = echarts.getInstanceByDom(dom);
let title = ''
selected.map((item) => {
if (!item.dataIndex.length) {
return;
}
let seriesName = item.seriesName;
let dataIndexList = item.dataIndex.map((i) => {
return `{x|${xdata[i]}数值:${data[item.seriesIndex].data[i]}}\n`
})
let line = `{name|${seriesName}}${dataIndexList}`;
title += '\n' + line;
})
// myChart.setOption({
// title: {
// text: '已选中:\n' + title,
// right: 20,
// top: 40,
// textStyle: {
// rich: {
// name: {
// color: '#333'
// },
// x: {
// color: 'red'
// }
// }
// }
// }
// })
}
const initEchart = () => {
let dom = document.getElementById('ecDiv');
let myChart = echarts.init(dom);
let dataIndexs = [];
let seriesIndexs = [];
let series = computedSeries(data, []);
let option = {
color,
xAxis: [
{
type: 'category',
data: xdata
}
],
yAxis: [
{
type: 'value'
}
],
grid: {
width: "80%",
containLabel: true,
left: 30,
top: 50,
right: 30,
bottom: 20
},
legend: {
show: true,
data: data.map((v) => v.name)
},
brush: {
xAxisIndex: 'all',
throttleType: 'debounce',
transformable: false,
removeOnClick: true,
brushMode: 'single',
throttleDelay: 0,
brushStyle: {
borderWidth: 1,
color: 'rgba(120,140,180,0.1)',
borderColor: 'rgba(120,140,180,0.1)'
},
inBrush: {
symbolSize: 10
},
outOfBrush: {
colorAlpha: 1,
opacity: 1
},
},
animation: false,
series
}
myChart.setOption(option);
myChart.on('brushselected', (params) => {
if (!params.batch[0].areas.length) {
return;
};
let batch = deepClone(params.batch[0]);
let selected = batch.selected;
batchSelected = selected;
let seriesData = computedSeries(data);
let graphics = seriesDataToGraphic(seriesData);
myChart.setOption({
series: seriesData,
graphic: graphics
})
setSelectTitle(selected);
})
// ! 点击取消 取消选中节点
myChart.on('brush', (params) => {
if (params?.command === 'clear') {
batchSelected.length = 0;
let seriesData = computedSeries(data);
let graphics = seriesDataToGraphic(seriesData);
myChart.setOption({
series: seriesData,
graphic: graphics
})
setSelectTitle([]);
}
})
}
const handleCli = () => {
console.log("1123", data)
setSelectTitle([]);
}
document.querySelector("#buttonid").onclick = handleCli;
initEchart();
</script>
</html>
</html>