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'] let xdata = ['2024-1-1', '2024-1-2', '2024-1-3', '2024-1-4', '2024-1-5'] const data = [ { name: 'line1', data: [100, 200, 300, 400, 500] }, { name: 'line2', data: [100, 200, 300, 400, 500].map((v) => v * 1.2) } ] 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)) { 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)) { Dataitem.data[key] = val + dif; } }) }) } let onmousedownY = 0; let oldDif = 0; const seriesDataToGraphic = (series) => { let dom = document.getElementById('container'); 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('container'); 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('container'); let myChart = echarts.init(dom); 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(batchSelected); }) // ! 点击取消 取消选中节点 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([]); } }) } initEchart();