背景
在实现拖拽曲线的功能基础上,官方例子存在部分问题:连续拖拽掉点,视图更新导致拖拽不平滑,超出坐标轴报错等问题。因此,在进行多次调试后,通过以下方式来解决对应的问题。
问题一:连续拖拽掉点
定义变量preIndex记录上次改变的点,preSeconds记录上次改变的时间戳。若两次改变的时间戳在0.1s内,则认为中间的值均为改变值
const index = myChart.convertFromPixel('grid', [dx.offsetX, dx.offsetY])[0];if (preIndex) {// 快速划过时,存在点丢失的情况if (Math.abs(preIndex - index) > 1 && moment().millisecond() - preSeconds < 100) {const max = preIndex - index > 0 ? preIndex : index;const min = preIndex - index < 0 ? preIndex : index;for (let i = min; i < max + 1; i++) {fun(i);}preIndex = index;preSeconds = moment().millisecond();return;}}preSeconds = moment().millisecond();preIndex = index;
问题二:视图更新导致拖拽不平滑
视图更新(setOption)会引起页面的回流,导致页面性能变低,因此,通过防抖的方法,控制setOption的频率来实现页面不卡顿,而实现平滑拖拽。
const fun = (index) => {// 通用方法if (dataChart[index] && myChart.convertFromPixel('grid', [dx.offsetX, dx.offsetY])) {const value = myChart.convertFromPixel('grid', [dx.offsetX, dx.offsetY])[1].toFixed(2);if (max) {dataChart[index][1] = max < value ? max : value;} else {dataChart[index][1] = value;}}clearTimeout(clock);clock = setTimeout(() => {myChart.setOption({series: [{id,type: 'line',data: dataChart,},],});}, 0);};
总结
主要由于Echarts提供的例子在点数少的时候,卡顿不会明显,但由于系统大部分采用一天96点展示的方法,导致一个折线图会展示大部分节点,对浏览器GUI线程造成较大压力,所以通过防抖的方法,可以很好的解决以上办法。
封装方法
/** @File: src/* @Author: hunter* @Date: 2020-09-10 09:31:10* @LastEditors: hunter* @LastEditTime: 2020-09-16 20:59:25*/import { getMetaPointList } from '@/utils/getMetaPointList';import echarts from 'echarts';import moment, { Moment } from 'moment';const tvMeta = {startDateTime: '2020-01-01 00:00:00',delta: 15,deltaUnit: 1,size: 96,};const dateArr = getMetaPointList(tvMeta)?.map((p: Moment) => p.format('HH:mm'));let preIndex;let preSeconds = moment().millisecond();let clock;// 拖拽图表// 拖拽const updatePosition = ({ myChart, dataChart }: any) => {myChart &&myChart.setOption({graphic: echarts.util.map(dataChart, function (item) {return {position: myChart.convertToPixel('grid', item),};}),});};const onPointDragging = ({ myChart, dataChart, dx, id, max }) => {const fun = (index) => {// 通用方法if (dataChart[index] && myChart.convertFromPixel('grid', [dx.offsetX, dx.offsetY])) {const value = myChart.convertFromPixel('grid', [dx.offsetX, dx.offsetY])[1].toFixed(2);if (max) {dataChart[index][1] = max < value ? max : value;} else {dataChart[index][1] = value;}}clearTimeout(clock);clock = setTimeout(() => {myChart.setOption({series: [{id,type: 'line',data: dataChart,},],});}, 0);};const index = myChart.convertFromPixel('grid', [dx.offsetX, dx.offsetY])[0];if (preIndex) {// 快速划过时,存在点丢失的情况if (Math.abs(preIndex - index) > 1 && moment().millisecond() - preSeconds < 100) {const max = preIndex - index > 0 ? preIndex : index;const min = preIndex - index < 0 ? preIndex : index;for (let i = min; i < max + 1; i++) {fun(i);}preIndex = index;preSeconds = moment().millisecond();return;}}preSeconds = moment().millisecond();preIndex = index;if (dataChart.length > index && index >= 0) {// 普通的重定位fun(index);}};// echartexport const echartsAnimate = ({ myChart, saveData, id, max }: any) => {let dataChart;myChart &&myChart.getOption().series.map((item) => {if (item.id === id) {dataChart = item.data.map((item, index) => {return [dateArr[index], item];});}});setTimeout(() => {myChart &&myChart.setOption({graphic: echarts.util.map(dataChart, function (item, dataIndex) {return {type: 'circle',position: myChart.convertToPixel('grid', item),shape: {cx: 0,cy: 0,r: 16,},invisible: true, // 调试设置为falsedraggable: true,ondrag: echarts.util.curry((dataIndex, dx) => {onPointDragging({ myChart, dataChart, dx, id, max });}, dataIndex),ondragend: echarts.util.curry((dataIndex, dx) => {preIndex = undefined;saveData(dataChart);}, dataIndex),z: 100,};}),});}, 0);window.addEventListener('resize', updatePosition);myChart.on('dataZoom', () => {updatePosition({ myChart, dataChart });});};
使用例子
<ReactEchartsstyle={{height: '400px',border: '1px solid rgb(204 204 204 / 0.5)',paddingTop: '10px',borderRadius: '6px',}}option={getConfig()}ref={(e) => {// 拖拽图表的核心if (e) {const myChart = e.getEchartsInstance();echartsAnimate({ myChart, saveData, id: 'test' });}}}/>
