最近在實現一個需求,需要改造echarts的折線圖,在其基礎上實現點的拖拽及點擊操作,效果如下:
實現方案
拖拽點
這個方法網上有說,原理:就是在折線的點位置上新增對應的拖拽點,並把拖拽點覆蓋在原點上面,設置其visible爲false,做到以假亂真的效果。
// 初始化echarts
let sideView = this.$refs.sideView;
if (!this.chart) {
this.chart = echarts.init(sideView);
this.chart.setOption(this.option);
this.chart.getZr().on('click', (params) => {
this.lineClickHandler(params)
})
this.chart.on('dataZoom', (params) => {
this.getDragedPointer()
})
}
// 新增圖形:透明的可操作的點
self.chart.setOption({
graphic: data.map((item, dataIndex) => {
// 獲取對應的物理像素點
let positionPoint = self.chart.convertToPixel({ seriesIndex: 0 }, item);
// 記錄原始的點信息 相對的點信息
self.lastCircleInfo[dataIndex] = positionPoint;
return {
type: 'circle',
position: positionPoint,
shape: {
cx: 0,
cy: 0,
r: self.symbolSize
},
invisible: true, // 設置隱藏
draggable: true,
z: 1000,
ondrag: function () {
let [lastX, lastY] = self.lastCircleInfo[dataIndex];
// Y不變 更改
data[dataIndex] = self.chart.convertFromPixel({ seriesIndex: 0 }, [this.x, lastY]);
self.chart.setOption({
series: [{
id: 'line',
data: data
}]
});
// 修改了Z需要放到數據中 ...業務處理
self.lastCircleInfo[dataIndex] = [this.x, lastY];
},
onmousedown: function () {
self.mouseDownFlag = true;
},
onmouseup: function () {
self.chartMouseUp();
},
onmousemove: function () {
self.chart.dispatchAction({
type: 'showTip',
seriesIndex: 0,
dataIndex: dataIndex
});
},
onmouseout: function () {
self.chart.dispatchAction({
type: 'hideTip'
});
}
};
})
})
新增點
原理:從點擊位置找到它的前後點,根據前後點把新增點放到對應的點index座標中。由於我的業務場景更復雜,還需要通過獲取到的點信息,轉換出其他的點信息
// 點擊折線新增點信息
lineClickHandler (params) {
let self = this;
const { target, topTarget } = params;
// 判斷必須是點擊在線上
if (self.addPoint && (target?.type === 'ec-polyline' || topTarget?.type === 'ec-polyline')) {
// 獲取在chart上的像素座標點
const pointInPixel = [params.offsetX, params.offsetY];
// 獲取點擊的點的前後點
let { points3d, points } = this.vm.currentDrawData;
// 獲取當前折線上有的點信息 lastCircleInfo 是在繪製點的時候記錄的點信息
let circleInfo = []
for (let i in this.lastCircleInfo) {
let circle = this.lastCircleInfo[i];
let [cicleX, circleY] = circle;
circleInfo.push([cicleX, circleY, Number(i)]);
}
// 獲取點擊的點到折線點的最短距離,距離最短的可以理解爲是點在線上
let prePoint = null;
let nextPoint = null;
circleInfo.find((item, itemIndex) => {
let flag = false;
circleInfo.find((unit, unitIndex) => {
// 相鄰的點纔有線 距離短纔是在線上的
if (itemIndex !== unitIndex && Math.abs(itemIndex - unitIndex) === 1) {
let dis = this.getShortestDistance([params.offsetX, params.offsetY], [unit[0], unit[1]], [item[0], item[1]]);
if (dis === 0) {
flag = true;
prePoint = item;
nextPoint = unit;
return true;
}
}
})
if (flag) {
return true;
}
})
// 獲取前後點後 將新增的點放到對應的位置上
if (prePoint && nextPoint) {
let prePointIndex = prePoint[2];
let nextPointIndex = nextPoint[2];
// 根據像素點獲取折線上的的點 注意折線上的點記錄的是3D的z 和 2D的y
prePoint = points3d[prePointIndex];
nextPoint = points3d[nextPointIndex];
let [z, yy] = this.chart.convertFromPixel({ seriesIndex: 0 }, pointInPixel);
// 需要獲取到3D的xy,這裏根據 【插值法】通過z 計算出x 和 y 的值
const prez1 = Math.abs(prePoint[2]);
const nextz1 = Math.abs(nextPoint[2]);
const startPrecent = Math.abs((z - prez1) / (nextz1 - prez1));
const x = prePoint[0] * (1 - startPrecent) + nextPoint[0] * startPrecent;
const y = prePoint[1] * (1 - startPrecent) + nextPoint[1] * startPrecent;
// 插入3D點
points3d.splice(prePointIndex + 1, 0, [x, y, z]);
// 獲取2D的點
let prePoint2D = points[prePointIndex];
let nextPoint2D = points[nextPointIndex];
const x2d = prePoint2D[0] * (1 - startPrecent) + nextPoint2D[0] * startPrecent;
const y2d = prePoint2D[1] * (1 - startPrecent) + nextPoint2D[1] * startPrecent;
// 插入2D
points.splice(prePointIndex + 1, 0, [x2d, y2d]);
// 更新視圖
this.updateCurrentDrawData();
}
}
},
關於插值法的說明