import * as fabric from "fabric" //'../../../components/fabric';
|
import {
|
v4 as uuidv4
|
} from 'uuid';
|
import {
|
Base64
|
} from '../../../comm/base64.js';
|
import {
|
hasSelfIntersection
|
} from '../../../comm/line.js';
|
import {
|
hexToRGBA,
|
} from '../../../comm/utils.js';
|
import {
|
throttle
|
} from 'lodash-es';
|
import {
|
debounce
|
} from "lodash-es";
|
import {
|
Result
|
} from "ant-design-vue";
|
|
import {
|
session,
|
} from "../../../comm/utils.js"
|
|
// import okIcon from '../../../static/images/confirm.svg';
|
// import cancelIcon from '../../../static/images/remove.svg';
|
|
export default {
|
data() {
|
return {
|
canvasId: "",
|
canvas: null,
|
eleWidth: 0,
|
eleHeight: 0,
|
touchStartPoint: {
|
x: 0,
|
y: 0
|
},
|
isDrawing: false,
|
drawType: "",
|
drawSvg: "",
|
initialDistance: null,
|
initialZoom: 1,
|
isDragging: false,
|
lastPosX: 0,
|
lastPosY: 0,
|
agvObj: null,
|
initFlag: false,
|
editMode: false,
|
objEditing: null,
|
onOjectMoving: debounce(this.objectMoving, 200),
|
mapInfo: {
|
proportion: 1,
|
img_proportion: 1,
|
max_x: 1,
|
max_y: 1,
|
min_x: 0,
|
min_y: 0,
|
img_x: 1,
|
img_y: 1
|
},
|
pressObjTimer: 0,
|
selectable: true,
|
}
|
},
|
mounted() {
|
this.init()
|
},
|
|
methods: {
|
async init(ip) {
|
try {
|
|
const _this = this
|
fabric.Object.prototype.setControlsVisibility({
|
mt: false, // 中间上
|
mb: false, // 中间下
|
ml: false, // 中间左
|
mr: false, // 中间右
|
mtr: false, // 旋转控制
|
|
tl: false, //是否显示中间横杠
|
bl: false, //是否显示中间横杠
|
tr: false, //是否显示中间横杠
|
br: false, //是否显示中间横杠
|
})
|
// 全局样式调整
|
fabric.Object.prototype.set({
|
borderColor: '#1890FF',
|
cornerColor: '#fff',
|
cornerStrokeColor: '#1890FF',
|
cornerSize: 36,
|
// touchCornerSize: 48,
|
transparentCorners: false,
|
cornerStyle: 'circle',
|
borderScaleFactor: 2,
|
padding: 5,
|
|
});
|
this.canvasId = `canvas_${uuidv4()}`
|
const cantainerEl = document.getElementById("canvasMap")
|
let canvas = document.getElementById(this.canvasId)
|
canvas = document.createElement("canvas")
|
canvas.setAttribute("id", this.canvasId)
|
canvas.setAttribute("type", "2d")
|
cantainerEl.appendChild(canvas)
|
|
this.canvas = new fabric.Canvas(this.canvasId, {
|
allowTouchScrolling: true, // 允许触摸滚动
|
selection: true,
|
isTouchDevice: true, // 关键配置
|
stopContextMenu: true, // 禁止长按菜单
|
fireRightClick: true,
|
fireMiddleClick: true,
|
isTouchSupported: true,
|
enableRetinaScaling: true,
|
renderOnAddRemove: false,
|
imageSmoothingEnabled: true,
|
perPixelTargetFind: true, // 点击图形自身才选中
|
targetFindTolerance: 5 // 增大触摸容差
|
})
|
|
const originalShouldClearSelection = this.canvas._shouldClearSelection;
|
|
// 重写方法
|
this.canvas._shouldClearSelection = function(e) {
|
// 只有当点击在对象上,并且是我们期望的条件(如边框)时,才执行默认的清除逻辑
|
// 点击空白处直接返回 false,不清除选择
|
if (e.target && e.subTargets) {
|
// 这里可以添加更精细的判断,例如结合后面的边框判断
|
return originalShouldClearSelection.call(this, e);
|
}
|
return false;
|
};
|
this.canvas.clear()
|
this.eleWidth = cantainerEl.clientWidth
|
this.eleHeight = cantainerEl.clientHeight
|
this.canvas.setWidth(this.eleWidth);
|
this.canvas.setHeight(this.eleHeight);
|
this.workSpace = new fabric.Rect({
|
id: "workspace",
|
eleType: "workspace",
|
left: 0,
|
top: 0,
|
width: this.eleWidth,
|
height: this.eleHeight,
|
selectable: false,
|
hasControls: false,
|
fill: "#FFFFFF20"
|
})
|
this.canvas.add(this.workSpace)
|
this.patchFabricForUniApp(this.canvas)
|
this.initFlag = true
|
await this.$nextTick();
|
this.canvasEventListener()
|
|
} catch (ex) {
|
this.showError(ex)
|
}
|
},
|
destroyCanvas() {
|
console.log("destroyCanvas")
|
if (this.canvas) {
|
// 2. 移除所有事件监听器
|
this.removeAllEventListeners();
|
|
// 3. 清空所有对象(分批处理避免阻塞)
|
this.clearObjects();
|
|
// 4. 销毁Fabric.js实例
|
this.canvas.dispose();
|
|
// 6. 清除所有变量引用
|
this.cleanupReferences();
|
this.canvas = null
|
// 通知Vue层销毁完成
|
this.$ownerInstance.callMethod('receiveRenderData', {
|
method: "destroy_complete",
|
param: true
|
});
|
}
|
},
|
patchFabricForUniApp(canvas) {
|
// 保存原始方法
|
const originalSetActiveObject = canvas.setActiveObject.bind(canvas);
|
|
// 重写setActiveObject
|
canvas.setActiveObject = function(object, e) {
|
originalSetActiveObject(object, e);
|
if (object) {
|
object.canvas = this;
|
this._activeObject = object;
|
}
|
};
|
|
// 确保对象初始化时设置正确的canvas引用
|
fabric.Object.prototype.initialize = function(options) {
|
const originalInit = fabric.util.getOriginalMethod(this, 'initialize');
|
originalInit.call(this, options);
|
|
if (this.canvas && this.canvas._activeObject === this) {
|
this.canvas._activeObject = this;
|
}
|
return this;
|
};
|
},
|
editCancelObject(eventData, transform, x, y) {
|
const target = transform.target;
|
const canvas = target.canvas;
|
this.$ownerInstance.callMethod('receiveRenderData', {
|
method: "edit_finish",
|
cmd: "cancel",
|
type: target.eleType,
|
data: target.data,
|
station: target.data,
|
});
|
|
this.canvas.requestRenderAll()
|
},
|
editOkObject(eventData, transform, x, y) {
|
const target = transform.target;
|
const canvas = target.canvas;
|
this.$ownerInstance.callMethod('receiveRenderData', {
|
method: "edit_finish",
|
cmd: "ok",
|
type: target.eleType,
|
data: target.data,
|
station: target.data,
|
});
|
this.canvas.requestRenderAll()
|
},
|
|
closeOkCancelControl() {
|
let list = this.canvas.getObjects() || []
|
list = list.filter((a) => a.eleType == "cmd")
|
list.forEach((obj) => {
|
this.canvas.remove(obj)
|
})
|
this.objEditing = null
|
|
},
|
createOkCancelControl(obj) {
|
// 创建删除按钮的图片元素
|
var scale = this.canvas.getZoom()
|
if (scale < 1) {
|
scale = 1
|
}
|
var scale2 = 1
|
var offset = 50
|
|
//if (obj.eleType == "station" || obj.eleType == "edit_teaching") {
|
scale2 = 1 / scale
|
|
//}
|
offset = 50 / scale
|
this.objEditing = obj
|
let list = this.canvas.getObjects() || []
|
list = list.filter((a) => a.eleType == "cmd")
|
if (list.length > 0) {
|
let left = obj.left - offset
|
let top = obj.top + obj.height / 2
|
if (obj.originX == "center") {
|
left = obj.left - obj.width / 2 - offset
|
}
|
if (obj.originX == "center") {
|
top = obj.top
|
}
|
|
let curIndex = list.findIndex((a) => a.id == "cancel")
|
if (curIndex > -1) {
|
list[curIndex].set({
|
left,
|
top,
|
mainObj: obj,
|
scaleX: scale2,
|
scaleY: scale2
|
})
|
list[curIndex].setCoords()
|
this.canvas.bringObjectToFront(list[curIndex]);
|
}
|
left = obj.left + obj.width + offset
|
if (obj.originX == "center") {
|
left = obj.left + obj.width / 2 + offset
|
}
|
curIndex = list.findIndex((a) => a.id == "ok")
|
if (curIndex > -1) {
|
list[curIndex].set({
|
left,
|
top,
|
mainObj: obj,
|
scaleX: scale2,
|
scaleY: scale2
|
})
|
list[curIndex].setCoords()
|
this.canvas.bringObjectToFront(list[curIndex]);
|
}
|
return
|
}
|
let objList = []
|
let ellipse = new fabric.Ellipse({
|
left: -15,
|
top: -15,
|
rx: 15,
|
ry: 15,
|
stroke: "#F5222D",
|
strokeWidth: 1,
|
fill: "#F5222D",
|
|
});
|
objList.push(ellipse)
|
let line = new fabric.Line([-6, -6, 6, 6], {
|
stroke: "white",
|
strokeWidth: 3,
|
});
|
objList.push(line)
|
line = new fabric.Line([6, -6, -6, 6], {
|
stroke: "white",
|
strokeWidth: 3,
|
});
|
objList.push(line)
|
let left = obj.left - offset
|
let top = obj.top + obj.height / 2
|
if (obj.originX == "center") {
|
left = obj.left - obj.width / 2 - offset
|
}
|
if (obj.originY == "center") {
|
top = obj.top
|
}
|
let objGroup = new fabric.Group(objList, {
|
id: `cancel`,
|
eleType: "cmd",
|
left,
|
top,
|
width: 30,
|
height: 30,
|
originX: "center",
|
originY: "center",
|
lockRotation: true,
|
lockScalingX: true,
|
lockScalingY: true,
|
lockMovementX: true,
|
lockMovementY: true,
|
mainObj: obj,
|
scaleX: scale2,
|
scaleY: scale2
|
});
|
this.canvas.add(objGroup)
|
|
objList = []
|
ellipse = new fabric.Ellipse({
|
left: -15,
|
top: -15,
|
rx: 15,
|
ry: 15,
|
stroke: "#52C41A",
|
strokeWidth: 1,
|
fill: "#52C41A",
|
});
|
objList.push(ellipse)
|
line = new fabric.Line([-8, -2, -2, 5], {
|
stroke: "white",
|
strokeWidth: 3,
|
});
|
objList.push(line)
|
line = new fabric.Line([-3, 6, 9, -5], {
|
stroke: "white",
|
strokeWidth: 3,
|
});
|
objList.push(line)
|
left = obj.left + obj.width + offset
|
if (obj.originX == "center") {
|
left = obj.left + obj.width / 2 + offset
|
}
|
objGroup = new fabric.Group(objList, {
|
id: `ok`,
|
eleType: "cmd",
|
left,
|
top,
|
width: 30,
|
height: 30,
|
originX: "center",
|
originY: "center",
|
lockRotation: true,
|
lockScalingX: true,
|
lockScalingY: true,
|
lockMovementX: true,
|
lockMovementY: true,
|
scaleX: scale2,
|
scaleY: scale2,
|
mainObj: obj
|
});
|
this.canvas.add(objGroup)
|
this.$ownerInstance.callMethod('receiveRenderData', {
|
method: "cancel_positioning_agv",
|
});
|
},
|
|
canvasEventListener() {
|
const _this = this
|
|
const cantainerEl = document.getElementById("canvasMap")
|
_this.canvas.on('touch:gesture', function(e) {
|
console.log("touch:gesture", e);
|
// 处理手势操作
|
});
|
_this.canvas.on("selection:created", function(e) {
|
const activeObj = _this.canvas.getActiveObject();
|
activeObj.set({
|
borderColor: '#1890ff',
|
borderScaleFactor: 3,
|
padding: 5,
|
borderDashArray: [5, 5], //5px 实线和5px 间隔
|
});
|
_this.canvas.requestRenderAll();
|
|
_this.onSelectionChanage()
|
|
});
|
_this.canvas.on("selection:updated", function(e) {
|
console.log("selection:updated", e);
|
const activeObj = _this.canvas.getActiveObject();
|
|
activeObj.set({
|
borderColor: '#1890ff',
|
borderScaleFactor: 3,
|
padding: 3,
|
borderDashArray: [5, 5], //5px 实线和5px 间隔
|
});
|
_this.canvas.requestRenderAll();
|
_this.onSelectionChanage()
|
|
|
});
|
_this.canvas.on("selection:cleared", function(e) {
|
// console.log("selection:cleared", e);
|
|
//_this.selectionChangeCanvas();
|
});
|
_this.canvas.on("object:removed", function(e) {
|
// console.log("object:removed", e.target);
|
if (e.target) {
|
e.target.isRemoved = true;
|
}
|
});
|
_this.canvas.on("transform", function(e) {
|
_this.onScaleChange()
|
});
|
|
_this.canvas.on("object:modified", function(e) {
|
// console.log("object:modified", e.target);
|
});
|
_this.canvas.on("object:moving", function(e) {
|
console.log("object:moving", e.target);
|
_this.onOjectMoving(e.target)
|
|
});
|
_this.canvas.on("mouse:down", function(opt) {
|
console.log("mouse:down", opt);
|
// 如果点击的是空白处(没有目标对象)
|
if (!opt.target) {
|
// 取消当前选中
|
|
_this.canvas.discardActiveObject();
|
// 重新渲染画布
|
_this.canvas.renderAll();
|
} else {
|
if (!opt.target.selectable) {
|
_this.canvas.discardActiveObject();
|
// 重新渲染画布
|
_this.canvas.renderAll();
|
}
|
console.log("mouse:down", opt.target.eleType);
|
}
|
});
|
|
cantainerEl.addEventListener('touchstart', function(e) {
|
// console.log('touchstart:', e.touches.length);
|
e.preventDefault(); // 阻止默认行为
|
|
_this.canvas.fire('touch:start', {
|
e: e
|
});
|
_this.canvasTouchStart(e)
|
// _this.canvas._onMouseDown(e);
|
});
|
|
cantainerEl.addEventListener('touchmove', function(e) {
|
// _this.canvas._onMouseMove(e);
|
// console.log('touchmove:', e.touches.length);
|
e.preventDefault(); // 阻止默认行为
|
// 处理移动
|
_this.canvasTouchMove(e)
|
});
|
|
cantainerEl.addEventListener('touchend', function(e) {
|
// _this.canvas._onMouseUp(e);
|
|
e.preventDefault(); // 阻止默认行为
|
_this.touchPoint = {
|
x: 0,
|
y: 0
|
};
|
if (_this.pressObjTimer) {
|
|
clearTimeout(_this.pressObjTimer);
|
_this.pressObjTimer = null
|
|
}
|
const activeObj = _this.canvas.getActiveObject()
|
if (!activeObj) {
|
// 处理结束事件
|
if (e.touches.length === 0) {
|
_this.handleTouchEnd();
|
}
|
// if (_this.editObject) {
|
// _this.canvas.setActiveObject(_this.editObject)
|
// }
|
} else {
|
// if (activeObj.lockMovementX) {
|
// _this.canvas.discardActiveObject();
|
// }
|
}
|
|
});
|
cantainerEl.addEventListener('touchcancel', function(e) {
|
// console.log('touchcancel:');
|
if (_this.pressObjTimer) {
|
clearTimeout(_this.pressObjTimer);
|
_this.pressObjTimer = null
|
}
|
|
})
|
|
},
|
canvasTouchStart(e) {
|
const _this = this
|
// _this.pointerSelectObject(e)
|
if (!_this.canvas.getActiveObject()) {
|
// 根据触摸点数量判断交互类型
|
if (e.touches.length === 1) {
|
_this.handleSingleTouch(e.touches[0]);
|
} else if (e.touches.length >= 2) {
|
_this.handleMultiTouch(e.touches);
|
}
|
} else {
|
if (e.touches.length === 1) {
|
const touch = e.touches[0]
|
_this.lastPosX = touch.clientX;
|
_this.lastPosY = touch.clientY;
|
|
const list = _this.canvas.getActiveObjects()
|
if (list.length === 1) {
|
if (!_this.objEditing) {
|
|
this.pressObjTimer = setTimeout(function() {
|
|
const zoom = _this.canvas.getZoom();
|
const vpt = _this.canvas.viewportTransform;
|
if (list[0].eleType == "station") {
|
_this.setAllObjectSelectable(false)
|
|
list[0].set({
|
selectable: true
|
})
|
|
let deltaX = list[0].left * zoom
|
let deltaY = list[0].top * zoom
|
const scale = zoom < 1 ? zoom : 1
|
_this.$ownerInstance.callMethod('receiveRenderData', {
|
method: "edit_station",
|
station: list[0].data,
|
view: {
|
x: vpt[4] + deltaX,
|
y: vpt[5] + deltaY,
|
// x: e.touches[0].clientX,
|
// y: e.touches[0].clientY,
|
width: list[0].width * scale,
|
height: list[0].height * scale
|
}
|
});
|
} else if (list[0].eleType == "public_teaching") {
|
const pt = _this.canvas.getPointer(touch); // ← 关键
|
// 2. pointer 就是画布坐标
|
|
_this.$ownerInstance.callMethod('receiveRenderData', {
|
method: "select_teaching_path",
|
data: list[0].data,
|
type: "public",
|
point: pt
|
|
|
});
|
} else if (list[0].eleType == "station_teaching") {
|
_this.$ownerInstance.callMethod('receiveRenderData', {
|
method: "select_teaching_path",
|
data: list[0].data,
|
type: "station",
|
point: pt
|
});
|
}
|
}, 1000); //
|
|
}
|
let activeObj = list[0]
|
if (activeObj.eleType == "region_pt_add") {
|
const data = activeObj.mainObj?.data
|
|
data.path.push({
|
x: _this.getActualXFromImg(activeObj.left) || 0,
|
y: _this.getActualYFromImg(activeObj.top) || 0
|
})
|
_this.updateRegion(activeObj.mainObj, data)
|
} else if (activeObj.eleType == "cmd") {
|
let data = activeObj.mainObj.data
|
const objCmdMain = activeObj.mainObj
|
if (objCmdMain.eleType == "edit_teaching") {
|
|
const left = _this.getActualXFromImg(objCmdMain.left)
|
const top = _this.getActualYFromImg(objCmdMain.top)
|
const right = _this.getActualXFromImg(objCmdMain.left + objCmdMain.width)
|
const bottom = _this.getActualYFromImg(objCmdMain.top + objCmdMain.height)
|
data = []
|
if (Number.isNaN(left) || Number.isNaN(top) || Number.isNaN(right) || Number
|
.isNaN(bottom)) {
|
const now = new Date()
|
const date = `${now.getHours()}:${now.getMinutes()}:${now.getSeconds()}`
|
_this.$ownerInstance.callMethod('receiveRenderData', {
|
method: "show_log",
|
data: {
|
date,
|
method: `POST`,
|
url: "app/log/edit_teaching",
|
param: objCmdMain,
|
statusCode: 100,
|
data: _this.mapInfo
|
},
|
});
|
}
|
data.push([left, top])
|
data.push([left, bottom])
|
data.push([right, bottom])
|
data.push([right, top])
|
console.log(data)
|
}
|
_this.$ownerInstance.callMethod('receiveRenderData', {
|
method: "edit_finish",
|
cmd: activeObj.id,
|
type: objCmdMain.eleType,
|
data: data,
|
});
|
if (activeObj.id == "ok") {
|
if (objCmdMain.eleType == "region") {
|
_this.addRegionFinish(activeObj.mainObj)
|
} else if (activeObj.mainObj.eleType == "virtual_wall") {
|
_this.addVirtualWallFinish(activeObj.mainObj)
|
}
|
|
}
|
if (objCmdMain.eleType == "edit_teaching") {
|
let list = _this.canvas.getObjects() || []
|
for (let i2 in list) {
|
const obj = list[i2]
|
obj.set({
|
opacity: 1
|
})
|
}
|
if (objCmdMain?.mainObj) {
|
objCmdMain.mainObj.set({
|
selectable: true
|
})
|
}
|
const ptObjs = objCmdMain.ptObjs || []
|
_this.canvas.remove(objCmdMain)
|
for (let i2 in ptObjs) {
|
const obj = ptObjs[i2]
|
_this.canvas.remove(obj)
|
}
|
}
|
if (objCmdMain.eleType == "station") {
|
|
_this.setAllObjectSelectable(true)
|
objCmdMain.tipObj.set({
|
left: objCmdMain.left,
|
top: objCmdMain.top - objCmdMain.height / 2 -
|
objCmdMain.tipObj.height / 2,
|
visible: true
|
})
|
objCmdMain.tipObj.setCoords()
|
}
|
_this.closeOkCancelControl()
|
|
_this.canvas.requestRenderAll()
|
}
|
// else if (activeObj.eleType == "station") {
|
// _this.$ownerInstance.callMethod('receiveRenderData', {
|
// method: "select_station",
|
// data: activeObj.data,
|
// select: activeObj.mark ? false : true
|
// });
|
// }
|
}
|
} else if (e.touches.length >= 2) {
|
_this.handleMultiTouch(e.touches);
|
}
|
}
|
|
},
|
canvasTouchMove(e) {
|
const _this = this
|
const list = _this.canvas.getActiveObjects()
|
if (list.length == 0) {
|
if (e.touches.length === 1) {
|
_this.handleSingleTouchMove(e.touches[0]);
|
} else if (e.touches.length >= 2) {
|
_this.handleMultiTouchMove(e.touches);
|
}
|
} else {
|
if (e.touches.length === 1) {
|
if (list.length > 1 || list[0].lockMovementX) {
|
if (list[0].lockMovementX) {
|
_this.canvas.discardActiveObject();
|
}
|
_this.handleSingleTouchMove(e.touches[0]);
|
} else {
|
const touch = e.touches[0]
|
const deltaX = touch.clientX - this.lastPosX;
|
const deltaY = touch.clientY - this.lastPosY;
|
if (Math.abs(deltaX) > 20)
|
clearTimeout(this.pressObjTimer);
|
}
|
|
} else if (e.touches.length >= 2) {
|
_this.canvas.discardActiveObject();
|
_this.handleMultiTouchMove(e.touches);
|
}
|
}
|
|
},
|
// 计算点到线段的距离
|
pointToSegmentDistance(px, py, x1, y1, x2, y2) {
|
const A = px - x1;
|
const B = py - y1;
|
const C = x2 - x1;
|
const D = y2 - y1;
|
|
const dot = A * C + B * D;
|
const lenSq = C * C + D * D;
|
let param = -1;
|
if (lenSq !== 0) param = dot / lenSq;
|
|
let xx, yy;
|
if (param < 0) {
|
xx = x1;
|
yy = y1;
|
} else if (param > 1) {
|
xx = x2;
|
yy = y2;
|
} else {
|
xx = x1 + param * C;
|
yy = y1 + param * D;
|
}
|
|
const dx = px - xx;
|
const dy = py - yy;
|
return Math.sqrt(dx * dx + dy * dy);
|
},
|
|
// 获取 Path 的所有线段
|
getPathSegments(path) {
|
const segs = [];
|
const pts = path.path;
|
let lastX = 0,
|
lastY = 0;
|
|
for (let i = 0; i < pts.length; i++) {
|
const cmd = pts[i];
|
if (cmd[0] === 'M') {
|
lastX = cmd[1];
|
lastY = cmd[2];
|
} else if (cmd[0] === 'L') {
|
const x = cmd[1];
|
const y = cmd[2];
|
segs.push([lastX, lastY, x, y]);
|
lastX = x;
|
lastY = y;
|
}
|
}
|
return segs;
|
},
|
|
// 判断点是否在 Path 或 Line 的边框上
|
isPointOnStroke(obj, pointer, tolerance = 5) {
|
if (obj instanceof fabric.Line) {
|
const dist = this.pointToSegmentDistance(
|
pointer.x, pointer.y,
|
obj.x1, obj.y1, obj.x2, obj.y2
|
);
|
return dist <= tolerance;
|
}
|
|
if (obj instanceof fabric.Path) {
|
const segs = this.getPathSegments(obj);
|
for (const [x1, y1, x2, y2] of segs) {
|
const dist = this.pointToSegmentDistance(pointer.x, pointer.y, x1, y1, x2, y2);
|
if (dist <= tolerance) return true;
|
}
|
}
|
|
return false;
|
},
|
|
pointerSelectObject(e) {
|
const pointer = this.canvas.getPointer(e);
|
const objects = this.canvas.getObjects();
|
|
objects.splice(0, 1);
|
const objActive = this.canvas.getActiveObject()
|
let pointerList = []
|
let pointerList2 = []
|
return
|
if (objActive instanceof fabric.Group) {
|
|
const ptr = pointer
|
// 计算相对于 Group 的本地坐标
|
const localX = ptr.x - objActive.left;
|
const localY = ptr.y - objActive.top;
|
// 判断这个本地坐标是否“真的”点中了某个子对象
|
let hit = false;
|
objActive._objects.forEach((obj) => {
|
if (obj.selectable) {
|
if (this.isPointOnStroke(obj, pointer, 8)) {
|
hit = true;
|
}
|
|
}
|
});
|
console.log(hit)
|
// 如果已经选中,但这一次没点中任何子对象 → 取消选中
|
if (!hit) {
|
this.canvas.discardActiveObject();
|
this.canvas.requestRenderAll();
|
|
}
|
}
|
|
|
for (let i = objects.length - 1; i >= 0; i--) {
|
const obj = objects[i];
|
|
if (obj.selectable && obj.opacity > 0) {
|
if (obj instanceof fabric.Path || obj instanceof fabric.Line) {
|
if (this.isPointOnStroke(obj, pointer)) {
|
// console.log(i, obj.eleType)
|
pointerList.unshift(obj)
|
|
}
|
} else {
|
const isHit = obj.containsPoint(pointer);
|
if (isHit) {
|
// console.log(i, obj.eleType)
|
pointerList.unshift(obj)
|
|
}
|
}
|
}
|
|
}
|
for (let i = pointerList.length - 1; i >= 0; i--) {
|
const obj = pointerList[i];
|
if (obj instanceof fabric.Path || obj instanceof fabric.Line) {
|
if (this.isPointOnStroke(obj, pointer, 1)) {
|
if (objActive != obj) {
|
if (this.pressObjTimer) {
|
clearTimeout(this.pressObjTimer);
|
this.pressObjTimer = null
|
}
|
this.canvas.discardActiveObject()
|
this.canvas.setActiveObject(obj);
|
this.canvas.requestRenderAll();
|
}
|
return
|
}
|
pointerList2.unshift(obj)
|
} else {
|
if (objActive != obj) {
|
if (this.pressObjTimer) {
|
clearTimeout(this.pressObjTimer);
|
this.pressObjTimer = null
|
}
|
this.canvas.discardActiveObject()
|
this.canvas.setActiveObject(obj);
|
this.canvas.requestRenderAll();
|
}
|
return
|
}
|
}
|
|
if (pointerList2.length > 0) {
|
if (pointerList2.length == 1) {
|
const obj = pointerList2[0];
|
if (objActive != obj) {
|
if (this.pressObjTimer) {
|
clearTimeout(this.pressObjTimer);
|
this.pressObjTimer = null
|
}
|
this.canvas.discardActiveObject()
|
this.canvas.setActiveObject(obj);
|
this.canvas.requestRenderAll();
|
}
|
return
|
}
|
pointerList = pointerList2
|
pointerList2 = []
|
for (let i = pointerList.length - 1; i >= 0; i--) {
|
const obj = pointerList[i];
|
if (this.isPointOnStroke(obj, pointer, 2)) {
|
if (objActive != obj) {
|
if (this.pressObjTimer) {
|
clearTimeout(this.pressObjTimer);
|
this.pressObjTimer = null
|
}
|
this.canvas.discardActiveObject()
|
this.canvas.setActiveObject(obj);
|
this.canvas.requestRenderAll();
|
}
|
return
|
}
|
pointerList2.unshift(obj)
|
}
|
}
|
if (pointerList2.length > 0) {
|
if (pointerList2.length == 1) {
|
const obj = pointerList2[0];
|
if (objActive != obj) {
|
if (this.pressObjTimer) {
|
clearTimeout(this.pressObjTimer);
|
this.pressObjTimer = null
|
}
|
this.canvas.discardActiveObject()
|
this.canvas.setActiveObject(obj);
|
this.canvas.requestRenderAll();
|
}
|
return
|
}
|
pointerList = pointerList2
|
pointerList2 = []
|
for (let i = pointerList.length - 1; i >= 0; i--) {
|
const obj = pointerList[i];
|
if (this.isPointOnStroke(obj, pointer, 3)) {
|
if (objActive != obj) {
|
if (this.pressObjTimer) {
|
clearTimeout(this.pressObjTimer);
|
this.pressObjTimer = null
|
}
|
this.canvas.discardActiveObject()
|
this.canvas.setActiveObject(obj);
|
this.canvas.requestRenderAll();
|
}
|
return
|
}
|
pointerList2.unshift(obj)
|
}
|
}
|
if (pointerList2.length > 0) {
|
if (pointerList2.length == 1) {
|
const obj = pointerList2[0];
|
if (objActive != obj) {
|
if (this.pressObjTimer) {
|
clearTimeout(this.pressObjTimer);
|
this.pressObjTimer = null
|
}
|
this.canvas.discardActiveObject()
|
this.canvas.setActiveObject(obj);
|
this.canvas.requestRenderAll();
|
}
|
return
|
}
|
pointerList = pointerList2
|
pointerList2 = []
|
for (let i = pointerList.length - 1; i >= 0; i--) {
|
const obj = pointerList[i];
|
if (this.isPointOnStroke(obj, pointer, 4)) {
|
if (objActive != obj) {
|
if (this.pressObjTimer) {
|
clearTimeout(this.pressObjTimer);
|
this.pressObjTimer = null
|
}
|
this.canvas.discardActiveObject()
|
this.canvas.setActiveObject(obj);
|
this.canvas.requestRenderAll();
|
}
|
return
|
}
|
pointerList2.unshift(obj)
|
}
|
}
|
if (pointerList2.length > 0) {
|
|
const obj = pointerList2[pointerList2.length - 1];
|
if (objActive != obj) {
|
if (this.pressObjTimer) {
|
clearTimeout(this.pressObjTimer);
|
this.pressObjTimer = null
|
}
|
this.canvas.discardActiveObject()
|
this.canvas.setActiveObject(obj);
|
this.canvas.requestRenderAll();
|
}
|
return
|
}
|
},
|
objectMoving(target) {
|
const _this = this
|
if (!target)
|
return
|
if (target?.eleType == "station") {
|
const obj = target
|
obj.data.x = _this.getActualXFromImg(obj.left)
|
obj.data.y = _this.getActualYFromImg(obj.top)
|
const vpt = _this.canvas.viewportTransform;
|
const zoom = this.canvas.getZoom();
|
let deltaX = obj.left * zoom
|
let deltaY = obj.top * zoom
|
|
_this.$ownerInstance.callMethod('receiveRenderData', {
|
method: "update_station",
|
station: obj.data,
|
view: {
|
x: vpt[4] + deltaX,
|
y: vpt[5] + deltaY,
|
}
|
|
});
|
_this.createOkCancelControl(obj)
|
_this.canvas.requestRenderAll()
|
} else if (target?.eleType == "edit_teaching") {
|
|
_this.updateEditTeachingPath(target)
|
|
} else if (target.eleType == "virtual_wall" || target.eleType == "region") {
|
const data = target.data
|
|
const offX = target.left - target.oldLeft
|
const offY = target.top - target.oldTop
|
console.log("path", offX, offY, data.path)
|
|
data.path.forEach((pt) => {
|
pt.x += _this.getActualSizeFromImg(offX)
|
pt.y -= _this.getActualSizeFromImg(offY)
|
})
|
console.log("path2", data.path)
|
if (target.eleType == "virtual_wall")
|
_this.updateVirtualWall(target, data)
|
else
|
_this.updateRegion(target, data)
|
|
_this.canvas.requestRenderAll()
|
|
} else if (target?.eleType == "wall_pt") {
|
const data = target.mainObj?.data
|
if (!data)
|
return
|
let pt = data.path[0]
|
let id = `${data.id}_${ pt.x}_${pt.y}`
|
console.log("wall_pt", target.id, id, pt, data.path)
|
if (target.id == id) {
|
data.path[0] = {
|
x: _this.getActualXFromImg(target.left),
|
y: _this.getActualYFromImg(target.top)
|
}
|
} else {
|
pt = data.path[1]
|
|
id = `${data.id}_${ pt.x}_${pt.y}`
|
console.log("wall_pt", id, pt)
|
if (target.id == id) {
|
data.path[1] = {
|
|
x: _this.getActualXFromImg(target.left),
|
y: _this.getActualYFromImg(target.top)
|
}
|
}
|
}
|
|
_this.updateVirtualWall(target.mainObj, data)
|
|
_this.canvas.requestRenderAll()
|
} else if (target?.eleType == "region_pt") {
|
const data = target.mainObj?.data
|
if (!data)
|
return
|
const curIndex = data.path.findIndex((pt) => `${data.id}_${ pt.x}_${pt.y}` == target.id)
|
if (curIndex > -1) {
|
let polygon = []
|
data.path.forEach((pt, index) => {
|
if (curIndex == index) {
|
polygon.push([_this.getActualXFromImg(target.left), _this.getActualYFromImg(target
|
.top)])
|
} else {
|
polygon.push([pt.x, pt.y])
|
}
|
})
|
polygon.push(polygon[0])
|
if (hasSelfIntersection(polygon)) {
|
|
_this.showToast("进行区和可行区必须是闭合图形")
|
target.set({
|
left: _this.getXOnImg(data.path[curIndex].x),
|
top: _this.getYOnImg(data.path[curIndex].y)
|
})
|
target.setCoords()
|
} else {
|
data.path[curIndex] = {
|
x: _this.getActualXFromImg(target.left),
|
y: _this.getActualYFromImg(target.top)
|
}
|
_this.updateRegion(target.mainObj, data)
|
|
}
|
_this.canvas.requestRenderAll()
|
}
|
} else if (target?.eleType == "edit_teaching_pt") {
|
|
let left = target.mainObj.left
|
let top = target.mainObj.top
|
let width = target.mainObj.width
|
let height = target.mainObj.height
|
|
if (target.id == `edit_teaching_pt_0`) {
|
if (target.left > target.mainObj.left + target.mainObj.width - 10) {
|
target.set({
|
left: target.mainObj.left + target.mainObj.width - 10
|
})
|
}
|
width += left - target.left
|
left = target.left
|
if (target.top > target.mainObj.top + target.mainObj.height - 10) {
|
target.set({
|
top: target.mainObj.top + target.mainObj.height - 10
|
})
|
}
|
height += top - target.top
|
top = target.top
|
|
|
} else if (target.id == `edit_teaching_pt_1`) {
|
if (target.left > target.mainObj.left + target.mainObj.width - 10) {
|
target.set({
|
left: target.mainObj.left + target.mainObj.width - 10
|
})
|
}
|
width += left - target.left
|
left = target.left
|
if (target.top < target.mainObj.top + 10) {
|
target.set({
|
top: target.mainObj.top + 10
|
})
|
}
|
height = target.top - top
|
} else if (target.id == `edit_teaching_pt_2`) {
|
if (target.left < target.mainObj.left + 10) {
|
target.set({
|
left: target.mainObj.left + 10
|
})
|
}
|
width = target.left - left
|
if (target.top < target.mainObj.top + 10) {
|
target.set({
|
top: target.mainObj.top + 10
|
})
|
}
|
height = target.top - top
|
} else if (target.id == `edit_teaching_pt_3`) {
|
if (target.left < target.mainObj.left + 10) {
|
target.set({
|
left: target.mainObj.left + 10
|
})
|
}
|
width = target.left - left
|
if (target.top > target.mainObj.top + target.mainObj.height - 10) {
|
target.set({
|
top: target.mainObj.top + target.mainObj.height - 10
|
})
|
}
|
height += top - target.top
|
top = target.top
|
}
|
|
target.mainObj.set({
|
left,
|
top,
|
height,
|
width
|
})
|
target.mainObj.setCoords()
|
this.updateEditTeachingPath(target.mainObj)
|
|
}
|
|
},
|
|
onSelectionChanage() {
|
|
},
|
// 将 Base64 转为 Blob,再生成 URL
|
base64ToBlob(base64, mime) {
|
const byteString = atob(base64.split(',')[1]);
|
const ab = new ArrayBuffer(byteString.length);
|
const ia = new Uint8Array(ab);
|
for (let i = 0; i < byteString.length; i++) {
|
ia[i] = byteString.charCodeAt(i);
|
}
|
return new Blob([ab], {
|
type: mime
|
});
|
},
|
async loadBase64ImageWithProgress(data, maxSize = 2048) {
|
const _this = this
|
return new Promise((resolve, reject) => {
|
// 创建临时Image对象监控加载进度
|
var base64Image = data
|
if (base64Image.indexOf("data:image/png;base64,") < 0) {
|
base64Image = "data:image/png;base64," + data
|
}
|
const blob = this.base64ToBlob(base64Image, 'image/png');
|
const url = URL.createObjectURL(blob);
|
|
|
_this.$ownerInstance.callMethod('receiveRenderData', {
|
method: "set_backgroud_progress",
|
type: "start",
|
});
|
const img = new Image();
|
img.onloadstart = () => {
|
_this.$ownerInstance.callMethod('receiveRenderData', {
|
method: "set_backgroud_progress",
|
type: "progress",
|
percent: 5
|
});
|
};
|
img.onprogress = (e) => {
|
if (e.lengthComputable) {
|
const percent = Math.min(99, Math.round((e.loaded / e.total) * 100));
|
_this.$ownerInstance.callMethod('receiveRenderData', {
|
method: "set_backgroud_progress",
|
type: "progress",
|
percent
|
});
|
} else {
|
// 无法计算进度时的模拟进度
|
const percent = Math.min(this.progressPercent + 10, 95);
|
_this.$ownerInstance.callMethod('receiveRenderData', {
|
method: "set_backgroud_progress",
|
type: "progress",
|
percent
|
});
|
}
|
};
|
img.onload = () => {
|
// 用完释放内存
|
URL.revokeObjectURL(url);
|
const percent = Math.min(this.progressPercent + 10, 95);
|
_this.$ownerInstance.callMethod('receiveRenderData', {
|
method: "set_backgroud_progress",
|
type: "progress",
|
percent: 100
|
});
|
// 转换为Fabric图像
|
const scale = Math.min(
|
maxSize / Math.max(img.width, img.height),
|
1
|
);
|
const fabricImg = new fabric.Image(img, {
|
left: 0,
|
top: 0,
|
scaleX: scale,
|
scaleY: scale,
|
});
|
_this.$ownerInstance.callMethod('receiveRenderData', {
|
method: "set_backgroud_progress",
|
type: "end",
|
});
|
resolve(fabricImg);
|
};
|
|
img.onerror = (err) => {
|
_this.$ownerInstance.callMethod('receiveRenderData', {
|
method: "set_backgroud_progress",
|
type: "error",
|
msg: err
|
});
|
console.error("图片加载失败", err)
|
reject(new Error('图片加载失败'));
|
};
|
// 开始加载
|
//img.src = base64Image;
|
img.src = url;
|
});
|
},
|
|
// 监控内存使用情况
|
checkMemoryUsage() {
|
if (window.performance && window.performance.memory) {
|
const usedMB = window.performance.memory.usedJSHeapSize / (1024 * 1024);
|
console.log(`内存使用: ${usedMB.toFixed(2)}MB`);
|
return usedMB;
|
}
|
return null;
|
},
|
// 移除所有事件监听器
|
removeAllEventListeners() {
|
if (!this.canvas) return;
|
|
// 移除Fabric.js内置事件
|
this.canvas.off();
|
|
// 移除自定义事件监听器
|
const cantainerEl = document.getElementById("canvasMap");
|
if (cantainerEl) {
|
cantainerEl.replaceWith(cantainerEl.cloneNode(true));
|
}
|
},
|
clearObjects() {
|
|
if (!this.canvas) return;
|
this.canvas.discardActiveObject()
|
const objects = this.canvas.getObjects()
|
const batchSize = 50; // 每批删除50个对象
|
for (let i = 0; i < objects.length; i += batchSize) {
|
const batch = objects.slice(i, i + batchSize);
|
this.canvas.remove(...batch);
|
}
|
this.canvas.clear();
|
this.workSpace = null;
|
this.agvObj = null;
|
this.curTeachingObj = null;
|
this.objEditing = null;
|
this.editObject = null;
|
this.drawingObj = null;
|
this.objAgvLaser = null
|
this.objAgvLaserLine = null
|
if (this.pressObjTimer) {
|
clearTimeout(this.pressObjTimer);
|
this.pressObjTimer = null;
|
}
|
},
|
// 清理所有引用
|
cleanupReferences() {
|
this.canvas = null;
|
this.workSpace = null;
|
this.agvObj = null;
|
this.curTeachingObj = null;
|
this.objEditing = null;
|
this.editObject = null;
|
this.drawingObj = null;
|
this.mapInfo = {
|
proportion: 1,
|
img_proportion: 1,
|
max_x: 1,
|
max_y: 1,
|
min_x: 0,
|
min_y: 0,
|
img_x: 1,
|
img_y: 1
|
};
|
// 清除所有定时器
|
if (this.pressObjTimer) {
|
clearTimeout(this.pressObjTimer);
|
this.pressObjTimer = null;
|
}
|
},
|
|
setBackground(info) {
|
const _this = this
|
|
if (!this.canvas) {
|
_this.$ownerInstance.callMethod('receiveRenderData', {
|
method: "set_backgroud_progress",
|
type: "error",
|
});
|
return
|
}
|
|
|
// this.canvas.clear()
|
// this.canvas.selectionColor = 'rgba(100, 200, 255, 0.3)'; // 选中背景色
|
// this.canvas.selectionBorderColor = '#1890ff'; // 边框颜色
|
// this.canvas.selectionLineWidth = 3; // 边框宽度
|
|
const cantainerEl = document.getElementById("canvasMap")
|
this.eleWidth = cantainerEl.clientWidth
|
this.eleHeight = cantainerEl.clientHeight
|
this.canvas.setWidth(this.eleWidth);
|
this.canvas.setHeight(this.eleHeight);
|
this.mapInfo = {
|
proportion: parseInt(info.proportion) || 1,
|
img_proportion: parseInt(info.img_proportion) || 1,
|
max_x: parseInt(info.max_x) || 1,
|
max_y: parseInt(info.max_y) || 1,
|
min_x: parseInt(info.min_x) || 0,
|
min_y: parseInt(info.min_y) || 0,
|
img_x: parseInt(info.img_x) || 1,
|
img_y: parseInt(info.img_y) || 1,
|
}
|
|
return new Promise((resolve, reject) => {
|
|
if (info.filedata) {
|
_this.loadBase64ImageWithProgress(info.filedata).then((img) => {
|
//console.warn('setBackground', JSON.stringify(img));
|
if (img) {
|
if (_this.mapInfo.img_x == 1) {
|
_this.mapInfo.img_x = img.width
|
_this.mapInfo.max_x = _this.mapInfo.img_proportion * _this.mapInfo
|
.img_x + _this.mapInfo.min_x
|
}
|
if (_this.mapInfo.img_y == 1) {
|
_this.mapInfo.img_y = img.height
|
_this.mapInfo.max_y = _this.mapInfo.img_proportion * _this.mapInfo
|
.img_y + _this.mapInfo.min_y
|
}
|
img.set({
|
left: 0,
|
top: 0,
|
});
|
|
//
|
const rect = new fabric.Rect({
|
left: 0,
|
top: 0,
|
width: _this.mapInfo.img_x,
|
height: _this.mapInfo.img_y,
|
stroke: "#333",
|
strokeWidth: 1,
|
strokeDashArray: [5, 5],
|
strokeLineCap: 'butt',
|
fill: "rgba(255,255,255,0)",
|
})
|
|
|
console.log(rect.width, _this.mapInfo.img_x, img.width)
|
|
let wsGroup = new fabric.Group([img, rect], {
|
id: "workspace",
|
eleType: "workspace",
|
selectable: false,
|
hasControls: false,
|
left: 0,
|
top: 0,
|
width: _this.mapInfo.img_x,
|
height: _this.mapInfo.img_y,
|
|
});
|
|
_this.clearObjects()
|
|
_this.canvas.add(wsGroup)
|
// _this.canvas.renderAll()
|
|
_this.workSpace = wsGroup
|
const scale = _this.eleWidth / (4 * 84)
|
_this.setZoomAuto(scale); //
|
}
|
//_this.checkMemoryUsage()
|
resolve()
|
|
}).catch((err) => {
|
// _this.$ownerInstance.callMethod('receiveRenderData', {
|
// method: "set_backgroud_progress",
|
// type: "error",
|
// msg:err
|
// });
|
const rect = new fabric.Rect({
|
left: 0,
|
top: 0,
|
width: _this.mapInfo.img_x,
|
height: _this.mapInfo.img_y,
|
stroke: "#333",
|
strokeWidth: 1,
|
strokeDashArray: [5, 5],
|
strokeLineCap: 'butt',
|
fill: "rgba(255,255,255,0)",
|
})
|
let wsGroup = new fabric.Group([rect], {
|
id: "workspace",
|
eleType: "workspace",
|
selectable: false,
|
hasControls: false,
|
left: 0,
|
top: 0,
|
width: _this.mapInfo.img_x,
|
height: _this.mapInfo.img_y,
|
|
});
|
_this.clearObjects()
|
_this.canvas.add(wsGroup)
|
_this.workSpace = wsGroup
|
const scale = _this.eleWidth / (4 * 84)
|
_this.setZoomAuto(scale); //
|
resolve()
|
})
|
|
} else {
|
_this.$ownerInstance.callMethod('receiveRenderData', {
|
method: "set_backgroud_progress",
|
type: "end",
|
});
|
resolve()
|
}
|
|
})
|
},
|
|
getAutoScale() {
|
// 按照宽度
|
|
const eleWidth = this.eleWidth - 20
|
const eleHeight = this.eleHeight - 200
|
if (!this.workSpace)
|
return 0.8
|
const width = this.workSpace.width
|
const height = this.workSpace.height
|
if (eleWidth / eleHeight < width / height) {
|
let scale = eleWidth / width;
|
return scale - scale / 10
|
} // 按照宽度缩放
|
let scale = eleHeight / height;
|
return scale - scale / 10
|
},
|
|
auto() {
|
const scale = this.getAutoScale()
|
this.setZoomAuto((98 * scale) / 100);
|
},
|
one() {
|
this.setZoomAuto(1, ); //
|
this.canvas.requestRenderAll();
|
},
|
setZoomAuto(scale, zoomPoint) {
|
const _this = this
|
let center = this.canvas.getCenter();
|
if (zoomPoint) {
|
this.canvas.zoomToPoint(new fabric.Point(zoomPoint.x, zoomPoint.y), scale);
|
} else {
|
this.canvas.zoomToPoint(new fabric.Point(center.left, center.top), scale);
|
}
|
this.canvas.fire("transform", {
|
scale: scale
|
});
|
if (!this.workSpace) return;
|
|
},
|
setDrawingType(type, svg) {
|
if (svg) {
|
if (type == "svg") {
|
this.drawType = "svg"
|
this.drawSvg = svg
|
return
|
}
|
}
|
if (type == "line") {
|
this.drawType = "line"
|
} else {
|
this.drawType = ""
|
}
|
},
|
deleteSelected() {
|
const list = this.canvas.getActiveObjects()
|
list.map((item) => this.canvas.remove(item));
|
this.canvas.requestRenderAll();
|
this.canvas.discardActiveObject();
|
this.canvas.fire("object:modified");
|
|
},
|
/**
|
* 设置画布中心到指定对象中心点上
|
* @param {Object} obj 指定的对象
|
*/
|
setCenterFromObject(obj) {
|
const objCenter = obj.getCenterPoint();
|
const viewportTransform = this.canvas.viewportTransform;
|
if (this.canvas.width === undefined || this.canvas.height === undefined || !viewportTransform)
|
return;
|
viewportTransform[4] = this.canvas.width / 2 - objCenter.x * viewportTransform[0];
|
viewportTransform[5] = this.canvas.height / 2 - objCenter.y * viewportTransform[3];
|
this.canvas.setViewportTransform(viewportTransform);
|
this.canvas.renderAll();
|
},
|
handleSingleTouch(touch) {
|
this.touchStartPoint = {
|
x: touch.clientX,
|
y: touch.clientY
|
};
|
// console.log('单点触摸开始');
|
let activeObj = this.canvas.getActiveObject();
|
if (!activeObj) {
|
if (!this.drawType) {
|
this.isDrawing = false;
|
this.isDragging = true;
|
this.lastPosX = touch.clientX;
|
this.lastPosY = touch.clientY;
|
|
} else {
|
this.isDragging = false;
|
this.isDrawing = true;
|
|
}
|
}
|
|
// 单点触摸逻辑
|
},
|
handleMultiTouch(touches) {
|
const _this = this
|
const touch1 = touches[0];
|
const touch2 = touches[1];
|
this.initialDistance = Math.sqrt(
|
Math.pow(touch2.clientX - touch1.clientX, 2) +
|
Math.pow(touch2.clientY - touch1.clientY, 2)
|
);
|
this.initialZoom = this.canvas.getZoom();
|
// 多点触摸初始逻辑
|
},
|
handleSingleTouchMove(touch) {
|
//console.log('单点移动', touch.clientX, touch.clientY,this.lastPosX,this.lastPosY);
|
if (this.isDragging) {
|
const deltaX = touch.clientX - this.lastPosX;
|
const deltaY = touch.clientY - this.lastPosY;
|
|
if (Math.abs(deltaX) > 5 || Math.abs(deltaY) > 5) {
|
if (this.pressObjTimer) {
|
clearTimeout(this.pressObjTimer);
|
this.pressObjTimer = null
|
}
|
|
}
|
if (Math.abs(deltaX) > 20 || Math.abs(deltaY) > 20) {
|
this.$ownerInstance.callMethod('receiveRenderData', {
|
method: "cancel_positioning_agv",
|
});
|
}
|
// 移动视口
|
const vpt = this.canvas.viewportTransform;
|
this.canvas.relativePan(new fabric.Point(deltaX, deltaY));
|
if (this.objEditing) {
|
if (this.objEditing.eleType == "station") {
|
const zoom = this.canvas.getZoom();
|
let deltaX2 = this.objEditing.left * zoom
|
let deltaY2 = this.objEditing.top * zoom
|
this.$ownerInstance.callMethod('receiveRenderData', {
|
method: "update_station",
|
station: this.objEditing.data,
|
view: {
|
x: vpt[4] - deltaX + deltaX2,
|
y: vpt[5] - deltaY + deltaY2,
|
}
|
|
});
|
}
|
}
|
|
|
this.lastPosX = touch.clientX;
|
this.lastPosY = touch.clientY;
|
this.canvas.requestRenderAll()
|
} else if (this.isDrawing) {
|
const vpt = this.canvas.viewportTransform;
|
// console.log("viewportTransform", vpt[4], vpt[5])
|
let startX = this.touchStartPoint.x - vpt[4]
|
let startY = this.touchStartPoint.y - vpt[5]
|
let endX = touch.clientX - vpt[4]
|
let endY = touch.clientY - vpt[5]
|
|
const scale = this.canvas.getZoom();
|
startX /= scale
|
startY /= scale
|
endX /= scale
|
endY /= scale
|
// console.log("viewportTransform", startX, startY, endX, endY)
|
const left =
|
endX > startX ?
|
startX :
|
endX;
|
const top =
|
endY > startY ?
|
startY :
|
endY;
|
let width = Math.abs(endX - startX)
|
let height = Math.abs(endY - startY)
|
|
|
if (this.drawType == "rect") {
|
if (this.drawingObj) {
|
this.drawingObj.set({
|
left,
|
top,
|
width,
|
height
|
})
|
this.drawingObj.setCoords()
|
} else {
|
const rect = new fabric.Rect({
|
id: `${new Date().getTime()}`,
|
eleType: "rect",
|
left,
|
top,
|
width,
|
height,
|
stroke: "#333",
|
strokeWidth: 1,
|
fill: "#fff",
|
lockEdit: true
|
});
|
this.canvas.add(rect);
|
this.drawingObj = rect
|
}
|
this.canvas.requestRenderAll();
|
} else if (this.drawType == "ellipse") {
|
if (this.drawingObj) {
|
this.drawingObj.set({
|
left,
|
top,
|
rx: width / 2,
|
ry: height / 2,
|
})
|
this.drawingObj.setCoords()
|
} else {
|
const ellipse = new fabric.Ellipse({
|
id: `${new Date().getTime()}`,
|
eleType: "ellipse",
|
left,
|
top,
|
rx: width / 2,
|
ry: height / 2,
|
stroke: "#333",
|
strokeWidth: 1,
|
fill: "#fff",
|
lockEdit: true
|
});
|
this.canvas.add(ellipse);
|
this.drawingObj = ellipse
|
}
|
this.canvas.requestRenderAll();
|
} else if (this.drawType == "line") {
|
if (this.drawingObj) {
|
this.drawingObj.set({
|
left,
|
top,
|
x1: startX,
|
y1: startY,
|
x2: endX,
|
y2: endY,
|
})
|
this.drawingObj.setCoords()
|
} else {
|
const line = new fabric.Line([startX, startY, endX,
|
endY
|
], {
|
id: `${new Date().getTime()}`,
|
eleType: "line",
|
stroke: "#333",
|
strokeWidth: 5,
|
lockEdit: true
|
});
|
this.canvas.add(line);
|
this.drawingObj = line
|
}
|
this.canvas.requestRenderAll();
|
} else if (this.drawType == "arrow") {
|
if (this.drawingObj) {
|
this.setArrowPath(this.drawingObj, startX, startY, endX, endY);
|
this.drawingObj.setCoords()
|
} else {
|
let path = this.getArrowPath(
|
startX, startY, endX, endY,
|
30, 40
|
)
|
const line = new fabric.Path(path, {
|
id: `${new Date().getTime()}`,
|
eleType: "arrow",
|
stroke: "#333",
|
strokeWidth: 5,
|
lockEdit: true
|
});
|
this.canvas.add(line);
|
this.drawingObj = line
|
}
|
this.canvas.requestRenderAll();
|
} else if (this.drawType == "svg") {
|
if (this.drawingObj) {
|
this.drawingObj.set({
|
left,
|
top,
|
width,
|
height,
|
});
|
this.drawingObj.setCoords()
|
} else {
|
const _this = this
|
fabric.loadSVGFromURL(this.drawSvg).then(({
|
objects,
|
options
|
}) => {
|
const objGroup = fabric.util.groupSVGElements(objects, options);
|
objGroup.set({
|
id: `${new Date().getTime()}`,
|
eleType: "svg",
|
left,
|
top,
|
width,
|
height,
|
viewBoxWidth: options.viewBoxWidth || options.width,
|
viewBoxHeight: options.viewBoxHeight || options.height,
|
lockEdit: true
|
});
|
_this.canvas.add(objGroup)
|
|
});
|
|
|
|
this.drawingObj = line
|
}
|
// this.canvas.requestRenderAll();
|
}
|
|
|
}
|
|
// 单点移动逻辑
|
},
|
handleMultiTouchMove(touches) {
|
// 计算两点间距离(缩放)
|
if (!this.initialDistance) {
|
return
|
}
|
const _this = this
|
const touch1 = touches[0];
|
const touch2 = touches[1];
|
//console.log('多点移动', touch1.identifier, touch2.identifier);
|
|
const distance = Math.sqrt(
|
Math.pow(touch2.clientX - touch1.clientX, 2) +
|
Math.pow(touch2.clientY - touch1.clientY, 2)
|
);
|
|
// 计算旋转角度
|
const angle = Math.atan2(
|
touch2.clientY - touch1.clientY,
|
touch2.clientX - touch1.clientX
|
) * 180 / Math.PI;
|
|
// 计算中心点
|
const center = {
|
x: (touch1.clientX + touch2.clientX) / 2,
|
y: (touch1.clientY + touch2.clientY) / 2
|
};
|
|
// 计算缩放比例 (限制在0.1-10倍之间)
|
let scale = Math.min(Math.max(
|
this.initialZoom * (distance / this.initialDistance),
|
0.1), 10);
|
let scaleAuto = this.getAutoScale()
|
if (scale < scaleAuto) {
|
scale = scaleAuto
|
} else if (scale == scaleAuto) {
|
return
|
}
|
//console.log(scale, scaleAuto)
|
this.setZoomAuto(scale, center)
|
|
// console.log('多点移动 - 距离:', distance, '角度:', angle);
|
// 多点移动逻辑
|
},
|
handleTouchEnd() {
|
// console.log('所有触摸结束');
|
this.isDrawing = false
|
this.drawingObj = undefined
|
this.initialDistance = null;
|
// 触摸结束逻辑
|
},
|
|
getXOnImg(x) {
|
const mapX = x * this.mapInfo.proportion
|
const imgX = (mapX - this.mapInfo.min_x) / this.mapInfo.img_proportion
|
return imgX
|
},
|
getYOnImg(y) {
|
const mapY = y * this.mapInfo.proportion
|
const imgY = (this.mapInfo.max_y - mapY) / this.mapInfo.img_proportion
|
return imgY
|
},
|
getSizeOnImg(size) {
|
const imgSize = size * this.mapInfo.proportion / this.mapInfo.img_proportion
|
return imgSize
|
},
|
getActualXFromImg(x) {
|
const mapX = x * this.mapInfo.img_proportion + this.mapInfo.min_x
|
const actualX = mapX / this.mapInfo.proportion
|
return actualX
|
},
|
getActualYFromImg(y) {
|
const mapY = this.mapInfo.max_y - y * this.mapInfo.img_proportion
|
const actualY = mapY / this.mapInfo.proportion
|
return actualY
|
},
|
getActualSizeFromImg(size) {
|
const actualSize = size * this.mapInfo.img_proportion / this.mapInfo.proportion
|
return actualSize
|
},
|
onScaleChange() {
|
var scale = this.canvas.getZoom()
|
if (scale < 1) {
|
scale = 1
|
}
|
const scale2 = 1 / scale
|
|
let list = this.canvas.getObjects()
|
const filter = ["agv", "public_teaching", "current_teaching", "edit_teaching", "edit_teaching_pt", "cmd",
|
"station",
|
"station_mark", "station_tip"
|
]
|
list = list.filter((a) => filter.includes(a.eleType))
|
list.forEach((obj) => {
|
if (obj.eleType == "public_teaching") {
|
// const subObjs = obj.getObjects()
|
// subObjs.forEach((obj2) => {
|
// obj2.set({
|
// strokeWidth: obj2.strokeOriginWidth * scale2
|
// })
|
// })
|
} else if (obj.eleType == "edit_teaching") {
|
obj.set({
|
strokeWidth: 2 * scale2
|
})
|
} else {
|
obj.set({
|
scaleX: scale2,
|
scaleY: scale2
|
})
|
}
|
if (obj.eleType == "station" || obj.eleType == "edit_teaching") {
|
const tipObj = obj.tipObj
|
if (tipObj) {
|
tipObj.set({
|
left: obj.left,
|
top: obj.top - (obj.height / 2 + tipObj
|
.height / 2) * scale2
|
})
|
tipObj.setCoords()
|
}
|
} else if (obj.eleType == "cmd") {
|
const obj2 = obj.mainObj
|
if (obj2?.eleType == "station") {
|
if (obj.id == `cancel`) {
|
obj.set({
|
left: obj2.left - (obj2.width / 2 + 50) * scale2,
|
})
|
obj.setCoords()
|
} else if (obj.id == `ok`) {
|
obj.set({
|
left: obj2.left + (obj2.width / 2 + 50) * scale2,
|
})
|
obj.setCoords()
|
}
|
|
|
|
}
|
}
|
|
})
|
if (this.objEditing) {
|
this.createOkCancelControl(this.objEditing)
|
}
|
this.canvas.requestRenderAll()
|
|
},
|
|
addStation(info) {
|
const _this = this
|
|
return new Promise((resolve) => {
|
const zoom = _this.canvas.getZoom();
|
let svg = "static/images/station.svg"
|
|
const left = _this.getXOnImg(info.x) // * scale
|
const top = _this.getYOnImg(info.y) //* scale
|
const angle = -(info.angle * 180 / Math.PI)
|
fabric.loadSVGFromURL(svg).then(
|
({
|
objects,
|
options
|
}) => {
|
var scale = this.canvas.getZoom()
|
if (scale < 1) {
|
scale = 1
|
}
|
const scale2 = 1 / scale
|
const objGroup = fabric.util.groupSVGElements(objects, options);
|
objGroup.set({
|
id: `station_${new Date().getTime()}`,
|
eleType: "station",
|
left,
|
top,
|
angle,
|
data: info,
|
originX: "center",
|
originY: "center",
|
// width: options.width,
|
// height: options.height,
|
// viewBoxWidth: options.viewBoxWidth || options.width,
|
// viewBoxHeight: options.viewBoxHeight || options.height,
|
hasControls: this.editMode,
|
lockRotation: true,
|
lockScalingX: true,
|
lockScalingY: true,
|
lockMovementX: true,
|
lockMovementY: true,
|
canSelect: true,
|
lockEdit: true,
|
scaleX: scale2,
|
scaleY: scale2,
|
|
})
|
_this.canvas.add(objGroup)
|
|
const objectText = new fabric.Text(`${info.name}`, {
|
id: `station_tip${new Date().getTime()}`,
|
eleType: "station_tip",
|
left: objGroup.left,
|
top: objGroup.top - 20,
|
fontSize: 14,
|
fontFamily: "Microsoft YaHei", //
|
fill: "#000", //
|
stroke: "#000", //
|
textBaseline: "alphabetic", // Correct value
|
originX: "center",
|
originY: "center",
|
hasControls: false,
|
lockRotation: true,
|
lockScalingX: true,
|
lockScalingY: true,
|
lockMovementX: true,
|
lockMovementY: true,
|
canSelect: false,
|
selectable: false,
|
|
scaleX: scale2,
|
scaleY: scale2,
|
});
|
objGroup.set({
|
tipObj: objectText
|
})
|
objectText.set({
|
left: objGroup.left,
|
top: objGroup.top - (objGroup.height / 2 + objectText
|
.height / 2) * scale2,
|
})
|
_this.canvas.add(objectText)
|
_this.canvas.bringObjectToFront(objGroup);
|
resolve()
|
}
|
)
|
})
|
},
|
setMarkStation(obj, mark) {
|
console.log("setMarkStation", obj.id, obj.mark, mark)
|
if (mark) {
|
if (obj.mark) {
|
return
|
}
|
var scale = this.canvas.getZoom()
|
if (scale < 1) {
|
scale = 1
|
}
|
const scale2 = 1 / scale
|
let ellipse = new fabric.Ellipse({
|
id: `station_mark${new Date().getTime()}`,
|
eleType: "station_mark",
|
left: obj.left,
|
top: obj.top,
|
rx: 20,
|
ry: 20,
|
stroke: "#ff7f23",
|
strokeWidth: 2,
|
fill: "#ffffff00",
|
originX: "center",
|
originY: "center",
|
lockRotation: true,
|
lockScalingX: true,
|
lockScalingY: true,
|
scaleX: scale2,
|
scaleY: scale2,
|
});
|
obj.set({
|
mark: true,
|
markObj: ellipse,
|
})
|
obj.add(ellipse)
|
|
} else {
|
if (obj.mark) {
|
obj.remove(obj.markObj)
|
delete obj.markObj
|
obj.set({
|
mark: false,
|
})
|
}
|
return
|
}
|
|
},
|
updateCurrentTeaching(teachingData) {
|
var posArr = teachingData.pos_list || []
|
const pos_list = []
|
if (this.curTeachingObj) {
|
this.canvas.remove(this.curTeachingObj)
|
this.curTeachingObj = null
|
}
|
if (teachingData.lenght === 0)
|
return
|
posArr.forEach((item) => {
|
const curIndex = pos_list.findIndex((item2) => item2.x === item.x && item2.y === item.y)
|
if (curIndex < 0) {
|
pos_list.push(item)
|
}
|
})
|
let path2 = ""
|
|
const len = pos_list.length
|
let fromX = 0,
|
fromY = 0,
|
toX = 0,
|
toY = 0;
|
for (let index = 0; index < len; index++) {
|
const pt = pos_list[index]
|
let pt2 = {
|
x: this.getXOnImg(pt.x),
|
y: this.getYOnImg(pt.y)
|
}
|
if (index > 0) {
|
|
path2 += ` L${pt2.x} ${pt2.y}`
|
} else {
|
path2 = `M${pt2.x} ${pt2.y}`
|
}
|
fromX = pt2.x
|
fromY = pt2.y
|
}
|
let strokeWidth = 5
|
let stroke = "#69C0FF"
|
|
|
const objPath = new fabric.Path(
|
path2, {
|
id: "current_teaching",
|
eleType: "current_teaching",
|
stroke: stroke,
|
strokeWidth,
|
// strokeDashArray: [5, 3],
|
// strokeLineCap: 'butt',
|
fill: "#ffffff00",
|
hasControls: false,
|
lockRotation: true,
|
lockScalingX: true,
|
lockScalingY: true,
|
lockMovementX: true,
|
lockMovementY: true,
|
selectable: false,
|
opacity: 1,
|
})
|
this.canvas.add(objPath)
|
this.curTeachingObj = objPath
|
},
|
addTeachingPath(teachingData, id, type) {
|
|
var posArr = teachingData.pos_list || []
|
const pos_list = []
|
posArr.forEach((item) => {
|
const curIndex = pos_list.findIndex((item2) => item2.x === item.x && item2.y === item.y)
|
if (curIndex < 0) {
|
pos_list.push(item)
|
}
|
})
|
let teachingGroup = []
|
let path2 = ""
|
let pathArrow = ""
|
const theta = 30;
|
let headlen = 10;
|
var main_road = teachingData.main_road
|
|
const len = pos_list.length
|
let fromX = 0,
|
fromY = 0,
|
toX = 0,
|
toY = 0;
|
let right = 0
|
let bottom = 0
|
let left = this.mapInfo.img_x
|
let top = this.mapInfo.img_y
|
|
var scale = this.canvas.getZoom()
|
if (scale < 1) {
|
scale = 1
|
}
|
const scale2 = 1 / scale
|
for (let index = 0; index < len; index++) {
|
const pt = pos_list[index]
|
let pt2 = {
|
x: this.getXOnImg(pt.x),
|
y: this.getYOnImg(pt.y)
|
}
|
if (fromX == pt2.x && fromY == pt2.y) {
|
pos_list.splice(index, 1)
|
index--;
|
continue;
|
}
|
if (index > 0) {
|
if (type == "public_teaching") {
|
const offset = index % 50
|
if (offset > 20 && offset < 41) {
|
if (offset == 21) {
|
|
pathArrow = `M${pt2.x} ${pt2.y}`
|
} else
|
pathArrow += ` L${pt2.x} ${pt2.y}`
|
if (offset == 40) {
|
const pt = pos_list[index - 2]
|
fromX = this.getXOnImg(pt.x)
|
fromY = this.getYOnImg(pt.y)
|
// console.log(fromX,fromY, this.getXOnImg(pt.x),this.getYOnImg(pt.y))
|
toX = pt2.x
|
toY = pt2.y
|
let angle = (Math.atan2(fromY - toY, fromX - toX) * 180) / Math.PI,
|
angle1 = ((angle + theta) * Math.PI) / 180,
|
angle2 = ((angle - theta) * Math.PI) / 180,
|
topX = headlen * Math.cos(angle1),
|
topY = headlen * Math.sin(angle1),
|
botX = headlen * Math.cos(angle2),
|
botY = headlen * Math.sin(angle2);
|
let arrowX = fromX - topX,
|
arrowY = fromY - topY;
|
arrowX = toX + topX;
|
arrowY = toY + topY;
|
pathArrow += " M " + arrowX + " " + arrowY;
|
pathArrow += " L " + toX + " " + toY;
|
arrowX = toX + botX;
|
arrowY = toY + botY;
|
pathArrow += " L " + arrowX + " " + arrowY;
|
|
const objArrow = new fabric.Path(
|
pathArrow, {
|
stroke: "#fff",
|
strokeWidth:2,// * scale2,
|
strokeOriginWidth:2,
|
fill: "#ffffff00",
|
selectable: false,
|
|
})
|
teachingGroup.push(objArrow)
|
}
|
}
|
}
|
path2 += ` L${pt2.x} ${pt2.y}`
|
} else {
|
if (main_road === undefined)
|
main_road = pt.main_road
|
|
path2 = `M${pt2.x} ${pt2.y}`
|
}
|
// if (left > pt2.x) {
|
// left = pt2.x
|
// }
|
// if (top > pt2.y) {
|
// top = pt2.y
|
// }
|
// if (right < pt2.x) {
|
// right = pt2.x
|
// }
|
// if (bottom < pt2.y) {
|
// bottom = pt2.y
|
// }
|
fromX = pt2.x
|
fromY = pt2.y
|
|
}
|
// console.log("addTeachingPath",path2)
|
// path2 += " Z"
|
let strokeWidth = 8
|
let stroke = "#69C0FF"
|
if (type == "station_teaching") {
|
stroke = "#69C0FF80"
|
} else {
|
if (main_road == 1) {
|
//stroke = "#69C0FF"
|
if (teachingData.bidirection == 1) {
|
stroke = "#ffaa0080"
|
}
|
strokeWidth = 12
|
|
} else {
|
strokeWidth = 8
|
if (teachingData.bidirection == 1) {
|
stroke = "#FF00FF80"
|
}
|
}
|
}
|
teachingData.main_road = main_road
|
let list = this.canvas.getObjects() || []
|
list = list.filter((a) => a.eleType == "station_teaching" || a.eleType == "public_teaching")
|
let lenTeaching = list.length
|
if (main_road == 1) {
|
list = list.filter((a) => a.eleType == "public_teaching" && a.mainRoad == 1)
|
lenTeaching = list.length
|
}
|
|
|
let ptList = []
|
const obj = new fabric.Path(
|
path2, {
|
id: id,
|
stroke: stroke,
|
strokeWidth:strokeWidth,// * scale2,
|
strokeOriginWidth:strokeWidth,
|
// strokeDashArray: [5, 3],
|
// strokeLineCap: 'butt',
|
// originX: "center",
|
// originY: "center",
|
selectable: true,
|
fill: "#ffffff00",
|
})
|
// this.canvas.add(obj)
|
teachingGroup.unshift(obj)
|
const objPath = new fabric.Group(
|
teachingGroup, {
|
id: id,
|
eleType: type,
|
// stroke: stroke,
|
// strokeWidth,
|
fill: "#ffffff00",
|
hasControls: false,
|
lockRotation: true,
|
lockScalingX: true,
|
lockScalingY: true,
|
lockMovementX: true,
|
lockMovementY: true,
|
// perPixelTargetFind: true,
|
// left: left - strokeWidth / 2,
|
// top: top - strokeWidth / 2,
|
// width: right - left + strokeWidth,
|
// height: bottom - top + strokeWidth,
|
|
mainRoad: main_road,
|
data: teachingData
|
})
|
this.canvas.add(objPath)
|
this.canvas.sendObjectToBack(objPath);
|
this.canvas.moveObjectTo(objPath, lenTeaching + 1);
|
|
return objPath
|
},
|
|
isObjectFullyContained(outerObj, innerObj) {
|
const outer = outerObj.getBoundingRect(true, true);
|
const inner = innerObj.getBoundingRect(true, true);
|
|
return (
|
outer.left <= inner.left &&
|
outer.top <= inner.top &&
|
outer.left + outer.width >= inner.left + inner.width &&
|
outer.top + outer.height >= inner.top + inner.height
|
);
|
},
|
|
compareOverlap(bottomObj, topObj) {
|
const bottomRect = bottomObj.getBoundingRect(true, true);
|
const topRect = topObj.getBoundingRect(true, true);
|
const maxLeft = Math.min(bottomRect.left + bottomRect.width, topObj.width + topObj.left)
|
const maxTop = Math.min(bottomRect.top + bottomRect.height, topObj.height + topObj.top)
|
const minRight = Math.min(bottomRect.left + bottomRect.width, topObj.width + topObj.left)
|
const minBottom = Math.min(bottomRect.top + bottomRect.height, topObj.height + topObj.top)
|
if (minRight <= maxLeft || minBottom <= maxTop) return true; // 无重叠
|
const bottomArea = bottomRect.width * bottomRect.height;
|
const topArea = topRect.width * topRect.height;
|
const interArea = (minRight - maxLeft) * (minBottom - maxTop)
|
if (bottomArea - interArea > topArea - -interArea) {
|
return true; //底部未重叠面积大
|
}
|
return false;
|
|
},
|
showTeachingPath(show) {
|
if (!show)
|
this.canvas.discardActiveObject();
|
let list = this.canvas.getObjects() || []
|
list = list.filter((a) => a.eleType == "station_teaching" || a.eleType == "public_teaching")
|
for (let i2 in list) {
|
const obj = list[i2]
|
obj.set({
|
opacity: show ? 1 : 0,
|
// strokeDashArray: [],
|
// strokeLineCap: '',
|
hasControls: show,
|
selectable: show,
|
})
|
}
|
|
|
},
|
showEditTeachingPath(teachingMode) {
|
|
let list = this.canvas.getObjects() || []
|
let eleType = ""
|
let id = ""
|
if (teachingMode.mode == "Public") {
|
eleType = "public_teaching"
|
id = `public_teaching_${teachingMode.name}_${teachingMode.edge_name}`
|
} else if (teachingMode.mode == "Stations") {
|
eleType = "station_teaching"
|
id = `station_teaching_${teachingMode.src_dst}`
|
} else {
|
return
|
}
|
|
let objTeaching;
|
for (let i2 in list) {
|
const obj = list[i2]
|
if (obj.eleType == eleType && obj.id == id) {
|
obj.set({
|
opacity: 1,
|
selectable: false
|
})
|
objTeaching = obj
|
} else {
|
obj.set({
|
opacity: 0.5
|
})
|
}
|
}
|
|
if (objTeaching) {
|
var scale = this.canvas.getZoom()
|
if (scale < 1) {
|
scale = 1
|
}
|
const scale2 = 1 / scale
|
|
this.canvas.discardActiveObject();
|
|
const width = (objTeaching.width > 200 ? 200 : objTeaching.width < 100 ? 100 : objTeaching
|
.width) * scale2
|
const height = (objTeaching.height > 200 ? 200 : objTeaching.height < 100 ? 100 : objTeaching
|
.height) * scale2
|
|
const pt = teachingMode.point
|
let left = objTeaching.left - 10
|
let top = objTeaching.top - 10
|
if (pt) {
|
left = pt.x - width / 2
|
top = pt.y - height / 2
|
}
|
const rect = new fabric.Rect({
|
id: `edit_teaching`,
|
eleType: "edit_teaching",
|
left,
|
top,
|
width,
|
height,
|
stroke: "#ff4d4f",
|
strokeWidth: 2 * scale2,
|
strokeDashArray: [5, 3],
|
strokeLineCap: 'butt',
|
fill: "#ff4d4f20",
|
hasControls: true,
|
lockRotation: true,
|
lockScalingX: true,
|
lockScalingY: true,
|
mainObj: objTeaching
|
})
|
|
this.canvas.add(rect)
|
this.createOkCancelControl(rect)
|
const ptList = []
|
const path = [{
|
x: rect.left,
|
y: rect.top
|
}, {
|
x: rect.left,
|
y: rect.top + rect.height
|
}, {
|
x: rect.left + rect.width,
|
y: rect.top + rect.height
|
}, {
|
x: rect.left + rect.width,
|
y: rect.top
|
}]
|
path.forEach((pt, index) => {
|
let ellipse = new fabric.Ellipse({
|
id: `edit_teaching_pt_${index}`,
|
eleType: "edit_teaching_pt",
|
left: pt.x,
|
top: pt.y,
|
rx: 10,
|
ry: 10,
|
stroke: "#ff4d4f",
|
strokeWidth: 1,
|
fill: "#ff4d4f",
|
originX: "center",
|
originY: "center",
|
lockRotation: true,
|
lockScalingX: true,
|
lockScalingY: true,
|
scaleX: scale2,
|
scaleY: scale2,
|
mainObj: rect
|
});
|
this.canvas.add(ellipse)
|
ptList.push(ellipse)
|
})
|
|
rect.set({
|
ptObjs: ptList,
|
oldLeft: rect.left,
|
oldTop: rect.top,
|
})
|
|
} else {
|
for (let i2 in list) {
|
const obj = list[i2]
|
obj.set({
|
opacity: 1
|
})
|
}
|
}
|
|
},
|
updateEditTeachingPath(obj) {
|
const ptObjs = obj.ptObjs || []
|
ptObjs[0].set({
|
left: obj.left,
|
top: obj.top
|
})
|
ptObjs[0].setCoords()
|
ptObjs[1].set({
|
left: obj.left,
|
top: obj.top + obj.height
|
})
|
ptObjs[1].setCoords()
|
ptObjs[2].set({
|
left: obj.left + obj.width,
|
top: obj.top + obj.height
|
})
|
ptObjs[2].setCoords()
|
ptObjs[3].set({
|
left: obj.left + obj.width,
|
top: obj.top
|
})
|
ptObjs[3].setCoords()
|
this.createOkCancelControl(obj)
|
this.canvas.requestRenderAll();
|
},
|
updateAgv(info, obj) {
|
const _this = this
|
return new Promise((resolve) => {
|
// const scale = this.getAutoScale()
|
const left = _this.getXOnImg(info.x) // * scale
|
const top = _this.getYOnImg(info.y) //* scale
|
const angle = -(info.angle * 180 / Math.PI) + 90
|
if (obj) {
|
obj.set({
|
left,
|
top,
|
angle,
|
data: info
|
});
|
obj.setCoords()
|
this.canvas.requestRenderAll();
|
resolve(obj)
|
} else {
|
const zoom = _this.canvas.getZoom();
|
fabric.loadSVGFromURL("static/images/van.svg").then(
|
({
|
objects,
|
options
|
}) => {
|
|
var scale = this.canvas.getZoom()
|
if (scale < 1) {
|
scale = 1
|
}
|
const scale2 = 1 / scale
|
const obj = fabric.util.groupSVGElements(objects, options);
|
|
obj.set({
|
id: "agv",
|
eleType: "agv",
|
left,
|
top,
|
angle,
|
data: info,
|
originX: "center",
|
originY: "center",
|
// scale: 1 / zoom,
|
hasControls: false,
|
lockRotation: true,
|
lockScalingX: true,
|
lockScalingY: true,
|
lockMovementX: true,
|
lockMovementY: true,
|
selectable: false,
|
scaleX: scale2,
|
scaleY: scale2,
|
lockEdit: true
|
|
});
|
console.log("agv", JSON.stringify(info))
|
_this.canvas.add(obj)
|
// _this.canvas.bringObjectToFront(obj);
|
resolve(obj)
|
}
|
)
|
}
|
|
})
|
},
|
addVirtualWallShow(info) {
|
const path = info.path || []
|
if (path.length != 2)
|
return
|
|
const line = new fabric.Line([this.getXOnImg(path[0].x), this.getYOnImg(path[0].y), this
|
.getXOnImg(
|
path[1]
|
.x), this.getYOnImg(path[1].y)
|
], {
|
id: `${info.id}`,
|
eleType: "virtual_wall",
|
stroke: info.color || "#ff4d4f",
|
strokeWidth: 3,
|
strokeDashArray: [5, 3],
|
strokeLineCap: 'butt',
|
hasControls: false,
|
lockRotation: true,
|
lockScalingX: true,
|
lockScalingY: true,
|
lockMovementX: true,
|
lockMovementY: true,
|
selectable: true,
|
data: info
|
});
|
return line
|
},
|
addVirtualWall(info) {
|
const path = info.path || []
|
if (path.length != 2)
|
return
|
const line = new fabric.Line([this.getXOnImg(path[0].x), this.getYOnImg(path[0].y), this
|
.getXOnImg(
|
path[1]
|
.x), this.getYOnImg(path[1].y)
|
], {
|
id: `${info.id}`,
|
eleType: "virtual_wall",
|
stroke: info.color || "#ff4d4f",
|
strokeWidth: 3,
|
strokeDashArray: [5, 3],
|
strokeLineCap: 'butt',
|
hasControls: false,
|
lockRotation: true,
|
lockScalingX: true,
|
lockScalingY: true,
|
selectable: true,
|
|
data: info
|
});
|
this.canvas.add(line)
|
let ptImg = {
|
x: this.getXOnImg(path[0].x),
|
y: this.getYOnImg(path[0].y)
|
}
|
let pt = {
|
x: path[0].x,
|
y: path[0].y
|
}
|
let ellipse = new fabric.Ellipse({
|
id: `${info.id}_${ pt.x}_${pt.y}`,
|
eleType: "wall_pt",
|
left: ptImg.x,
|
top: ptImg.y,
|
rx: 10,
|
ry: 10,
|
stroke: info.color || "#ff4d4f",
|
strokeWidth: 1,
|
fill: info.color || "#ff4d4f",
|
originX: "center",
|
originY: "center",
|
lockEdit: true,
|
lockRotation: true,
|
lockScalingX: true,
|
lockScalingY: true,
|
mainObj: line
|
});
|
this.canvas.add(ellipse)
|
ptImg = {
|
x: this.getXOnImg(path[1].x),
|
y: this.getYOnImg(path[1].y)
|
}
|
pt = {
|
x: path[1].x,
|
y: path[1].y
|
}
|
let ellipse2 = new fabric.Ellipse({
|
id: `${info.id}_${ pt.x}_${pt.y}`,
|
eleType: "wall_pt",
|
left: ptImg.x,
|
top: ptImg.y,
|
rx: 10,
|
ry: 10,
|
stroke: info.color || "#ff4d4f",
|
strokeWidth: 1,
|
fill: info.color || "#ff4d4f",
|
originX: "center",
|
originY: "center",
|
lockEdit: true,
|
lockRotation: true,
|
lockScalingX: true,
|
lockScalingY: true,
|
mainObj: line
|
});
|
this.canvas.add(ellipse2)
|
line.set({
|
ptObj1: ellipse,
|
ptObj2: ellipse2
|
})
|
|
this.createOkCancelControl(line)
|
this.editObject = line
|
line.set({
|
oldLeft: line.left,
|
oldTop: line.top,
|
})
|
return line
|
},
|
updateVirtualWall(obj, info) {
|
const path = info.path || []
|
if (path.length != 2)
|
return
|
let ptImg1 = {
|
x: this.getXOnImg(path[0].x),
|
y: this.getYOnImg(path[0].y)
|
}
|
let ptImg2 = {
|
x: this.getXOnImg(path[1].x),
|
y: this.getYOnImg(path[1].y)
|
}
|
let pt1 = {
|
x: path[0].x,
|
y: path[0].y
|
}
|
let pt2 = {
|
x: path[1].x,
|
y: path[1].y
|
}
|
const left = ptImg2.x > ptImg1.x ? ptImg1.x : ptImg2.x;
|
const top = ptImg2.y > ptImg1.y ? ptImg1.y : ptImg2.y;
|
obj.set({
|
left: left,
|
top: top,
|
x1: ptImg1.x,
|
y1: ptImg1.y,
|
x2: ptImg2.x,
|
y2: ptImg2.y,
|
data: info,
|
});
|
obj.setCoords()
|
obj.ptObj1.set({
|
id: `${info.id}_${ pt1.x}_${pt1.y}`,
|
left: ptImg1.x,
|
top: ptImg1.y,
|
|
})
|
obj.ptObj2.set({
|
id: `${info.id}_${ pt2.x}_${pt2.y}`,
|
left: ptImg2.x,
|
top: ptImg2.y,
|
|
})
|
obj.ptObj1.setCoords()
|
obj.ptObj2.setCoords()
|
|
this.createOkCancelControl(obj)
|
this.editObject = obj
|
obj.set({
|
oldLeft: obj.left,
|
oldTop: obj.top,
|
})
|
},
|
addVirtualWallFinish(obj) {
|
this.canvas.remove(obj.ptObj1)
|
this.canvas.remove(obj.ptObj2)
|
delete obj.ptObj1
|
delete obj.ptObj2
|
this.canvas.sendObjectToBack(obj);
|
this.canvas.moveObjectTo(obj, 1);
|
obj.set({
|
lockMovementX: true,
|
lockMovementY: true,
|
selectable: false
|
})
|
|
},
|
removeVirtualWall(obj) {
|
this.closeOkCancelControl()
|
if (obj.ptObj1)
|
this.canvas.remove(obj.ptObj1)
|
if (obj.ptObj1)
|
this.canvas.remove(obj.ptObj2)
|
this.canvas.remove(obj)
|
},
|
addRegionShow(info) {
|
const _this = this
|
const path = info.path || []
|
let path2 = ""
|
path.forEach((pt, index) => {
|
|
let pt2 = {
|
x: this.getXOnImg(pt.x),
|
y: this.getYOnImg(pt.y)
|
}
|
|
|
if (index > 0) {
|
path2 += ` L${pt2} ${pt2.y}`
|
} else {
|
path2 = `M${pt2.x} ${pt2.y}`
|
}
|
})
|
path2 += " Z"
|
let ptList = []
|
let fillColor = info.color || "#ff4538"
|
|
const objPath = new fabric.Path(
|
path2, {
|
id: `${info.id}`,
|
eleType: "region",
|
stroke: info.color || "#ff4d4f",
|
strokeWidth: 3,
|
strokeDashArray: [5, 3],
|
strokeLineCap: 'butt',
|
fill: hexToRGBA(fillColor, 0.2),
|
hasControls: false,
|
lockRotation: true,
|
lockScalingX: true,
|
lockScalingY: true,
|
lockMovementX: true,
|
lockMovementY: true,
|
selectable: false,
|
data: info
|
})
|
this.canvas.add(objPath)
|
return objPath
|
},
|
|
addRegion(info) {
|
const _this = this
|
const path = info.path || []
|
let path2 = ""
|
path.forEach((pt, index) => {
|
let pt2 = {
|
x: this.getXOnImg(pt.x),
|
y: this.getYOnImg(pt.y)
|
}
|
if (index > 0) {
|
path2 += ` L${pt2.x} ${pt2.y}`
|
} else {
|
path2 = `M${pt2.x} ${pt2.y}`
|
}
|
})
|
path2 += " Z"
|
let ptList = []
|
const objPath = new fabric.Path(
|
path2, {
|
id: `${info.id}`,
|
eleType: "region",
|
stroke: info.color || "#ff4d4f",
|
strokeWidth: 3,
|
strokeDashArray: [5, 3],
|
strokeLineCap: 'butt',
|
fill: "rgba(255,255,255,0)",
|
hasControls: false,
|
lockRotation: true,
|
lockScalingX: true,
|
lockScalingY: true,
|
data: info
|
})
|
this.canvas.add(objPath)
|
path.forEach((pt, index) => {
|
let pt2 = {
|
x: this.getXOnImg(pt.x),
|
y: this.getYOnImg(pt.y)
|
}
|
let ellipse = new fabric.Ellipse({
|
id: `${info.id}_${ pt.x}_${pt.y}`,
|
eleType: "region_pt",
|
left: pt.x,
|
top: pt.y,
|
rx: 10,
|
ry: 10,
|
stroke: info.color || "#ff4d4f",
|
strokeWidth: 1,
|
fill: info.color || "#ff4d4f",
|
originX: "center",
|
originY: "center",
|
lockRotation: true,
|
lockScalingX: true,
|
lockScalingY: true,
|
mainObj: objPath
|
});
|
this.canvas.add(ellipse)
|
ptList.push(ellipse)
|
|
})
|
const ptLast = path[0]
|
const ptLast2 = path[path.length - 1]
|
|
const objAddList = []
|
let ellipse = new fabric.Ellipse({
|
left: -10,
|
top: -10,
|
rx: 10,
|
ry: 10,
|
stroke: info.color || "#ff4d4f",
|
strokeWidth: 1,
|
fill: info.color || "#ff4d4f",
|
});
|
objAddList.push(ellipse)
|
let line = new fabric.Line([-6, 0, 6, 0], {
|
stroke: "#FEFEFE",
|
strokeWidth: 2,
|
});
|
objAddList.push(line)
|
line = new fabric.Line([0, -6, 0, 6], {
|
stroke: "#FEFEFE",
|
strokeWidth: 2,
|
});
|
objAddList.push(line)
|
let objGroup = new fabric.Group(objAddList, {
|
id: `${info.id}_add`,
|
eleType: "region_pt_add",
|
left: ptLast2.x + (ptLast.x - ptLast2.x) / 2,
|
top: ptLast2.y + (ptLast.y - ptLast2.y) / 2,
|
width: 20,
|
height: 20,
|
originX: "center",
|
originY: "center",
|
lockRotation: true,
|
lockScalingX: true,
|
lockScalingY: true,
|
lockMovementX: true,
|
lockMovementY: true,
|
mainObj: objPath
|
});
|
this.canvas.add(objGroup)
|
|
objPath.set({
|
ptObjs: ptList,
|
ptAddObj: objGroup,
|
oldLeft: objPath.left,
|
oldTop: objPath.top,
|
})
|
this.createOkCancelControl(objPath)
|
this.editObject = objPath
|
return objPath
|
|
},
|
|
updateRegion(obj, info) {
|
const path = info.path || []
|
let path2 = ""
|
path.forEach((pt, index) => {
|
let pt2 = {
|
x: this.getXOnImg(pt.x),
|
y: this.getYOnImg(pt.y)
|
}
|
if (index > 0) {
|
path2 += ` L${pt2.x} ${pt2.y}`
|
} else {
|
path2 = `M${pt2.x} ${pt2.y}`
|
}
|
})
|
path2 += " Z"
|
|
const objList = obj.ptObjs || []
|
const objAdd = obj.ptAddObj
|
|
let listObj = this.canvas.getObjects() || []
|
const curIndex = listObj.findIndex((a) => a.eleType == "region" && a.id == obj.id)
|
let oldObj
|
if (curIndex > -1) {
|
oldObj = listObj[curIndex]
|
}
|
let objNew = new fabric.Path(
|
path2, {
|
id: `${info.id}`,
|
eleType: "region",
|
stroke: info.color || "#ff4d4f",
|
strokeWidth: 3,
|
strokeDashArray: [5, 3],
|
strokeLineCap: 'butt',
|
fill: "rgba(255,255,255,0)",
|
hasControls: false,
|
lockRotation: true,
|
lockScalingX: true,
|
lockScalingY: true,
|
|
data: info,
|
ptObjs: objList,
|
ptAddObj: objAdd
|
})
|
this.canvas.add(objNew)
|
// console.log("remove", curIndex, obj.id, obj)
|
if (oldObj) {
|
this.canvas.remove(oldObj)
|
}
|
if (objList.length > path.length) {
|
for (let i = path.length; i < objList.length; i++) {
|
this.canvas.remove(objList[i])
|
}
|
} else if (objList.length == path.length) {
|
for (let i = 0; i < objList.length; i++) {
|
const pt = path[i]
|
let pt2 = {
|
x: this.getXOnImg(pt.x),
|
y: this.getYOnImg(pt.y)
|
}
|
objList[i].set({
|
id: `${info.id}_${ pt.x}_${pt.y}`,
|
left: pt2.x,
|
top: pt2.y,
|
mainObj: objNew
|
})
|
|
this.canvas.bringObjectToFront(objList[i]);
|
objList[i].setCoords()
|
}
|
} else {
|
for (let i = objList.length; i < path.length; i++) {
|
const pt = path[i]
|
let pt2 = {
|
x: this.getXOnImg(pt.x),
|
y: this.getYOnImg(pt.y)
|
}
|
let ellipse = new fabric.Ellipse({
|
id: `${info.id}_${ pt.x}_${pt.y}`,
|
eleType: "region_pt",
|
left: pt2.x,
|
top: pt2.y,
|
rx: 10,
|
ry: 10,
|
stroke: info.color || "#ff4d4f",
|
strokeWidth: 1,
|
fill: info.color || "#ff4d4f",
|
originX: "center",
|
originY: "center",
|
lockRotation: true,
|
lockScalingX: true,
|
lockScalingY: true,
|
mainObj: objNew
|
});
|
this.canvas.add(ellipse)
|
objList.push(ellipse)
|
}
|
}
|
const ptLast = {
|
x: this.getXOnImg(path[0].x),
|
y: this.getYOnImg(path[0].y)
|
}
|
const ptLast2 = {
|
x: this.getXOnImg(path[path.length - 1].x),
|
y: this.getYOnImg(path[path.length - 1].y)
|
}
|
objNew.ptAddObj.set({
|
left: ptLast2.x + (ptLast.x - ptLast2.x) / 2,
|
top: ptLast2.y + (ptLast.y - ptLast2.y) / 2,
|
mainObj: objNew
|
})
|
objNew.set({
|
oldLeft: objNew.left,
|
oldTop: objNew.top,
|
})
|
this.canvas.bringObjectToFront(objNew.ptAddObj);
|
objNew.ptAddObj.setCoords()
|
let list = this.canvas.getObjects() || []
|
list = list.filter((a) => a.eleType == "cmd")
|
list.forEach((obj) => {
|
obj.mainObj = objNew
|
})
|
this.editObject = objNew
|
this.createOkCancelControl(objNew)
|
},
|
addRegionFinish(obj) {
|
const objList = obj.ptObjs || []
|
|
|
for (let i = 0; i < objList.length; i++) {
|
this.canvas.remove(objList[i])
|
}
|
let color = obj.data.color || "#ff4d4f"
|
obj.set({
|
fill: hexToRGBA(color, 0.2)
|
})
|
this.canvas.remove(obj.ptAddObj)
|
|
delete obj.ptObjs
|
delete obj.ptAddObj
|
this.canvas.sendObjectToBack(obj);
|
this.canvas.moveObjectTo(obj, 1);
|
obj.set({
|
lockMovementX: true,
|
lockMovementY: true,
|
selectable: false
|
})
|
},
|
removeRegion(obj) {
|
this.closeOkCancelControl()
|
const objList = obj.ptObjs || []
|
for (let i = 0; i < objList.length; i++) {
|
this.canvas.remove(objList[i])
|
}
|
if (obj.ptAddObj)
|
this.canvas.remove(obj.ptAddObj)
|
this.canvas.remove(obj)
|
},
|
updateAgvLaser(param) {
|
const angle = -param.angle //- Math.PI / 2 // * 180 / Math.PI
|
const pt = {
|
x: this.getXOnImg(param.x),
|
y: this.getYOnImg(param.y)
|
}
|
if (this.objAgvLaser) {
|
this.objAgvLaser.set({
|
stroke: "#00ff00",
|
rx: 2,
|
ry: 2
|
})
|
}
|
let ellipse = new fabric.Ellipse({
|
id: "agv_laser",
|
eleType: "agv_laser",
|
left: pt.x,
|
top: pt.y,
|
rx: 3,
|
ry: 3,
|
stroke: "#ff0000",
|
strokeWidth: 1,
|
fill: "#00ff00",
|
originX: "center",
|
originY: "center",
|
selectable: false,
|
hasControls: false,
|
|
});
|
this.canvas.add(ellipse)
|
const offX = 20 * Math.cos(angle)
|
const offY = 20 * Math.sin(angle)
|
this.objAgvLaser = ellipse
|
if (this.objAgvLaserLine) {
|
this.canvas.remove(this.objAgvLaserLine)
|
}
|
const line = new fabric.Line([pt.x, pt.y, pt.x + offX,
|
pt.y + offY
|
], {
|
id: "agv_laser_angle",
|
eleType: "agv_laser_angle",
|
stroke: "#00ff00",
|
strokeWidth: 1,
|
selectable: false,
|
hasControls: false,
|
});
|
|
this.objAgvLaserLine = line
|
this.canvas.add(line)
|
},
|
addTrajectoryPoint(list) {
|
const objs = []
|
|
let right = 0
|
let bottom = 0
|
let left = this.mapInfo.img_x
|
let top = this.mapInfo.img_y
|
for (let i in list) {
|
const pt = list[i]
|
const pt2 = {
|
x: this.getXOnImg(pt[0]),
|
y: this.getYOnImg(pt[1])
|
}
|
let ellipse = new fabric.Ellipse({
|
left: pt2.x,
|
top: pt2.y,
|
rx: 2,
|
ry: 2,
|
stroke: "#00ff00",
|
strokeWidth: 1,
|
fill: "#00ff00",
|
originX: "center",
|
originY: "center",
|
});
|
|
objs.push(ellipse)
|
if (left > pt2.x - 2) {
|
left = pt2.x - 2
|
}
|
if (top > pt2.y - 2) {
|
top = pt2.y - 2
|
}
|
if (right < pt2.x + 2) {
|
right = pt2.x + 2
|
}
|
if (bottom < pt2.y + 2) {
|
bottom = pt2.y + 2
|
}
|
}
|
const groupObj = new fabric.Group(objs, {
|
id: `trajectory_point_group`,
|
eleType: "trajectory_point_group",
|
left,
|
top,
|
width: right - left,
|
height: bottom - top,
|
selectable: false,
|
hasControls: true,
|
})
|
this.canvas.add(groupObj)
|
},
|
updateLaserPoint(param) {
|
|
let list2 = this.canvas.getObjects() || []
|
list2 = list2.filter((a) => a.eleType == "laser_point_cur_group")
|
for (let i in list2) {
|
const objGroup = list2[i]
|
objGroup.set({
|
eleType: "laser_point_group",
|
})
|
const list3 = objGroup.getObjects()
|
|
for (let i3 in list3) {
|
const obj = list3[i3]
|
obj.set({
|
fill: "#0000FF",
|
|
})
|
}
|
}
|
const list = param?.xy || []
|
|
const objs = []
|
|
let right = 0
|
let bottom = 0
|
let left = this.mapInfo.img_x
|
let top = this.mapInfo.img_y
|
for (let i in list) {
|
const pt = list[i]
|
const pt2 = {
|
x: this.getXOnImg(pt[0]),
|
y: this.getYOnImg(pt[1])
|
}
|
const point = new fabric.Rect({
|
// id: "laser_point",
|
// eleType: "laser_point",
|
left: pt2.x,
|
top: pt2.y,
|
width: 1,
|
height: 1,
|
fill: "#ff00ff",
|
originX: "left",
|
originY: "top",
|
// selectable: false,
|
// hasControls: true,
|
// lockRotation: true,
|
// lockScalingX: true,
|
// lockScalingY: true,
|
// lockMovementX: true,
|
// lockMovementY: true,
|
});
|
objs.push(point)
|
if (left > pt2.x) {
|
left = pt2.x
|
}
|
if (top > pt2.y) {
|
top = pt2.y
|
}
|
if (right < pt2.x + 1) {
|
right = pt2.x + 1
|
}
|
if (bottom < pt2.y + 1) {
|
bottom = pt2.y + 1
|
}
|
}
|
//console.log("updateLaserPoint", list.length, left, right,top,bottom)
|
const groupObj = new fabric.Group(objs, {
|
id: `laser_point_group`,
|
eleType: "laser_point_cur_group",
|
left,
|
top,
|
width: right - left,
|
height: bottom - top,
|
// originX: "left",
|
// originY: "top",
|
selectable: false,
|
hasControls: true,
|
|
})
|
this.canvas.add(groupObj)
|
|
},
|
|
ensurePointVisible(pt) {
|
var zoom = this.canvas.getZoom();
|
var vpt = this.canvas.viewportTransform; // 当前变换矩阵
|
|
var newPanX = -vpt[4];
|
var newPanY = -vpt[5];
|
const offWidth = pt.width || 20
|
const offHeight = pt.height || 20
|
// console.log("ensurePointVisible",pt.x,pt.y,newPanX,newPanY, this.eleWidth,this.eleHeight)
|
if (pt.x * zoom < -vpt[4] + offWidth || pt.x * zoom > -vpt[4] + this.eleWidth - offWidth) {
|
if (pt.x * zoom - this.eleWidth / 2 < offWidth) {
|
newPanX = -offWidth
|
} else {
|
newPanX = pt.x * zoom - this.eleWidth / 2
|
}
|
}
|
if (pt.y * zoom < -vpt[5] + offHeight || pt.y * zoom > -vpt[5] + this.eleHeight - (120 +
|
offHeight)) {
|
|
if (pt.y * zoom - this.eleHeight / 2 < offHeight) {
|
newPanY = -offHeight
|
} else {
|
newPanY = pt.y * zoom - (this.eleHeight - 120) / 2
|
}
|
}
|
// 只有在需要时才平移
|
if (newPanX !== -vpt[4] || newPanY !== -vpt[5]) {
|
this.canvas.absolutePan({
|
x: newPanX,
|
y: newPanY
|
});
|
}
|
},
|
ensurePointCenter(pt) {
|
var zoom = this.canvas.getZoom();
|
var newPanX = newPanX = pt.x * zoom - this.eleWidth / 2
|
var newPanY = pt.y * zoom - (this.eleHeight - 150) / 2
|
this.canvas.absolutePan({
|
x: newPanX,
|
y: newPanY
|
});
|
},
|
setAllObjectSelectable(selectable) {
|
let flag = false
|
|
this.canvas.forEachObject(function(obj) {
|
if (obj.canSelect) {
|
if (obj.selectable != selectable) {
|
flag = true
|
obj.set({
|
selectable: selectable,
|
lockEdit: true
|
})
|
}
|
}
|
});
|
if (flag)
|
this.canvas.requestRenderAll()
|
|
|
},
|
receiveMsg(newValue, oldValue) {
|
if (typeof newValue == "undefined")
|
return;
|
const _this = this
|
if (!this.canvas) {
|
return
|
}
|
if (_this.initFlag) {
|
_this.handleMsg(newValue, oldValue)
|
|
} else {
|
setTimeout(() => {
|
_this.receiveMsg(newValue, oldValue)
|
}, 100)
|
}
|
|
},
|
async handleMsg(newValue, oldValue) {
|
const _this = this
|
if (!this.canvas) {
|
return
|
}
|
try {
|
|
var data = JSON.parse(newValue);
|
// console.log("handleMsg", data.length)
|
const destroyCommand = data.find(item => item.method === "destroy");
|
if (destroyCommand) {
|
this.destroyCanvas();
|
return;
|
}
|
|
for (var i = 0; i < data.length; i++) {
|
const item = data[i]
|
|
|
if (item.method == "init") {
|
if (item.param?.editMode) {
|
_this.editMode = true
|
} else {
|
_this.editMode = false
|
}
|
}
|
if (item.method == "background") {
|
await _this.setBackground(item.param)
|
} else if (item.method == "update_agv_state") {
|
const info = item.param || {}
|
const obj = _this.agvObj
|
const obj2 = await _this.updateAgv(info, obj)
|
let obj3 = _this.agvObj
|
_this.agvObj = obj2
|
if (obj && obj2 != obj) {
|
|
_this.canvas.remove(obj)
|
if (obj3 == obj)
|
obj3 = undefined
|
}
|
if (obj3 && obj2 != obj3) {
|
_this.canvas.remove(obj3)
|
}
|
|
} else if (item.method == "update_current_teaching") {
|
const info = item.param || []
|
await _this.updateCurrentTeaching(info)
|
} else if (item.method == "move_pt_visible") {
|
const info2 = item.param || {}
|
|
const pt = {
|
x: this.getXOnImg(info2.x),
|
y: this.getYOnImg(info2.y),
|
width: 20,
|
height: 20
|
}
|
this.ensurePointVisible(pt)
|
} else if (item.method == "move_pt_center") {
|
const info2 = item.param || {}
|
const pt = {
|
x: this.getXOnImg(info2.x),
|
y: this.getYOnImg(info2.y)
|
}
|
this.ensurePointCenter(pt)
|
} else if (item.method == "add_station") {
|
const stationList = item.param || []
|
let list = _this.canvas.getObjects() || []
|
for (let i2 in stationList) {
|
const station = stationList[i2]
|
const curIndex = list.findIndex((a) => a.data?.stationID == station
|
.stationID)
|
if (curIndex < 0) {
|
await _this.addStation(station)
|
}
|
}
|
} else if (item.method == "update_station") {
|
const stationList = item.param || []
|
let list = _this.canvas.getObjects() || []
|
list = list.filter((a) => a.eleType == "station")
|
for (let i2 in stationList) {
|
const station = stationList[i2]
|
const curIndex = list.findIndex((a) => a.data.stationID == station
|
.stationID)
|
if (curIndex < 0) {
|
await _this.addStation(station)
|
} else {
|
// _this.canvas.remove(list[curIndex])
|
const curStationObj = list[curIndex]
|
const angle = -station.angle * 180 / Math.PI
|
//const scale = this.getAutoScale()
|
const left = this.getXOnImg(station.x) //* scale
|
const top = this.getYOnImg(station.y) //* scale
|
if (curStationObj.left != left || top != station.y ||
|
curStationObj.angle != angle) {
|
curStationObj.set({
|
left,
|
top,
|
angle,
|
data: station
|
})
|
|
curStationObj.tipObj.set({
|
text: `${station.name}`,
|
left: curStationObj.left,
|
top: curStationObj.top - curStationObj.height / 2 -
|
curStationObj
|
.tipObj.height / 2,
|
})
|
curStationObj.tipObj.setCoords()
|
curStationObj.setCoords()
|
if (_this.editObject == curStationObj) {
|
_this.editObject.tipObj.set({
|
visible: false
|
})
|
this.createOkCancelControl(curStationObj)
|
}
|
|
}
|
}
|
}
|
|
} else if (item.method == "remove_station") {
|
const stationList = item.param || []
|
this.canvas.discardActiveObject()
|
let list = _this.canvas.getObjects() || []
|
|
list = list.filter((a) => a.eleType == "station")
|
for (let i2 in stationList) {
|
const station = stationList[i2]
|
const curIndex = list.findIndex((a) => a.data.stationID == station
|
.stationID)
|
if (curIndex > -1) {
|
const tipObj = list[curIndex].tipObj
|
_this.closeOkCancelControl()
|
console.log("remove_station", list[curIndex])
|
_this.canvas.remove(list[curIndex])
|
if (tipObj) {
|
_this.canvas.remove(tipObj)
|
}
|
}
|
}
|
|
} else if (item.method == "edit_station_pos") {
|
const station = item.param || undefined
|
|
if (!station) {
|
if (_this.editObject) {
|
_this.editObject.set({
|
lockMovementX: true,
|
lockMovementY: true,
|
})
|
|
_this.editObject = null
|
}
|
|
_this.closeOkCancelControl()
|
continue
|
}
|
let list = _this.canvas.getObjects() || []
|
list = list.filter((a) => a.eleType == "station")
|
_this.setAllObjectSelectable(false)
|
const curIndex = list.findIndex((a) => a.data.stationID == station.stationID)
|
if (curIndex > -1) {
|
console.log("edit_station_pos", curIndex, list[curIndex])
|
if (_this.editObject != list[curIndex]) {
|
if (_this.editObject) {
|
_this.editObject.set({
|
lockMovementX: true,
|
lockMovementY: true,
|
})
|
}
|
_this.editObject = list[curIndex]
|
_this.editObject.set({
|
lockMovementX: false,
|
lockMovementY: false,
|
})
|
}
|
list[curIndex].set({
|
selectable: true,
|
lockEdit: false,
|
lockMovementX: false,
|
lockMovementY: false,
|
})
|
_this.editObject.tipObj.set({
|
left: _this.editObject.left,
|
top: _this.editObject.top - _this.editObject.height / 2 - _this
|
.editObject
|
.tipObj.height / 2,
|
visible: false
|
})
|
_this.createOkCancelControl(_this.editObject)
|
}
|
|
} else if (item.method == "mark_station") {
|
const stationIdList = item.param || []
|
let list2 = _this.canvas.getObjects() || []
|
let list = list2.filter((a) => a.eleType == "station")
|
const flag = stationIdList.length == 2
|
|
_this.showTeachingPath(_this.showTeachPathFlag ? true : false)
|
let objStation1
|
let objStation2
|
for (let i2 in list) {
|
const obj = list[i2]
|
const curIndex = stationIdList.findIndex((a) => a == obj.data.stationID)
|
if (curIndex > -1) {
|
await _this.setMarkStation(obj, true)
|
obj.set({
|
selectable: true,
|
opacity: 1
|
})
|
if (!objStation1)
|
objStation1 = obj
|
else
|
objStation2 = obj
|
|
} else {
|
await _this.setMarkStation(obj, false)
|
if (flag) {
|
obj.set({
|
selectable: false,
|
opacity: 0.5
|
})
|
} else {
|
obj.set({
|
selectable: true,
|
opacity: 1
|
})
|
}
|
}
|
}
|
list = list2.filter((a) => a.eleType == "station_teaching")
|
if (objStation1 && objStation2) {
|
const id =
|
`station_teaching_${objStation1.data.stationID}_${objStation2.data.stationID}`
|
const curIndex = list.findIndex((a) => a.id == id)
|
if (curIndex > -1) {
|
list[curIndex].set({
|
opacity: 1,
|
strokeDashArray: [3, 2],
|
strokeLineCap: 'butt',
|
})
|
}
|
}
|
|
} else if (item.method == "station_teaching") {
|
let list = _this.canvas.getObjects() || []
|
list.forEach((obj) => {
|
if (obj.eleType == "public_teaching" || obj.eleType ==
|
"station_teaching") {
|
obj.set({
|
hasControls: false,
|
selectable: false,
|
})
|
} else if (obj.eleType == "station" || obj.eleType ==
|
"station_tip") {
|
obj.set({
|
selectable: true,
|
opacity: 1
|
})
|
} else {
|
obj.set({
|
opacity: 0.5
|
})
|
}
|
})
|
} else if (item.method == "public_teaching") {
|
_this.setAllObjectSelectable(false)
|
let list = _this.canvas.getObjects() || []
|
list.forEach((obj) => {
|
if (obj.eleType == "public_teaching" || obj.eleType ==
|
"station_teaching") {
|
obj.set({
|
opacity: 1
|
})
|
} else if (obj.eleType == "agv")
|
obj.set({
|
opacity: 1
|
})
|
else
|
obj.set({
|
opacity: 0.5
|
})
|
})
|
} else if (item.method == "teaching_finish") {
|
if (this.curTeachingObj) {
|
this.canvas.remove(this.curTeachingObj)
|
this.curTeachingObj = null
|
}
|
let list = _this.canvas.getObjects() || []
|
for (let i2 in list) {
|
const obj = list[i2]
|
obj.set({
|
selectable: obj?.canSelect ? true : false,
|
opacity: 1
|
})
|
}
|
_this.showTeachingPath(_this.showTeachPathFlag ? true : false)
|
|
} else if (item.method == "clear_teaching_path") {
|
this.canvas.discardActiveObject()
|
let list = _this.canvas.getObjects() || []
|
list = list.filter((a) => a.eleType == "public_teaching" || a.eleType ==
|
"station_teaching")
|
for (let i2 in list) {
|
this.canvas.remove(list[i2])
|
}
|
} else if (item.method == "public_teaching_path") {
|
|
const teachingPathList = item.param?.list || []
|
const show = item.param?.show || false
|
for (let i2 in teachingPathList) {
|
const teachingPath = teachingPathList[i2]
|
const id = `public_teaching_${teachingPath.name}_${teachingPath.edge_name}`
|
const obj = await this.addTeachingPath(teachingPath, id, "public_teaching",
|
show)
|
obj.set({
|
selectable: show ? true : false,
|
opacity: show ? 1 : 0,
|
})
|
|
}
|
} else if (item.method == "station_teaching_path") {
|
const show = item.param?.show || false
|
const teachingPathList = item.param?.list || []
|
for (let i2 in teachingPathList) {
|
const teachingPath = teachingPathList[i2]
|
const id = `station_teaching_${teachingPath.src_dst}`
|
const obj = await this.addTeachingPath(teachingPath, id, "station_teaching",
|
show)
|
obj.set({
|
selectable: show ? true : false,
|
opacity: show ? 1 : 0,
|
})
|
}
|
} else if (item.method == "show_teaching_path") {
|
_this.showTeachPathFlag = item.param.show
|
_this.showTeachingPath(item.param.show)
|
|
} else if (item.method == "remove_teaching_path") {
|
this.canvas.discardActiveObject()
|
let list = _this.canvas.getObjects() || []
|
if (item.param.mode == "Public") {
|
list = list.filter((a) => a.eleType == "public_teaching")
|
const id = `public_teaching_${item.param.name}_${item.param.edge_name}`
|
const curIndex = list.findIndex((a) => a.id == id)
|
|
if (curIndex > -1) {
|
const obj = list[curIndex]
|
console.log(item.method, curIndex, id, obj)
|
_this.canvas.remove(obj)
|
}
|
} else if (item.param.mode == "Stations") {
|
list = list.filter((a) => a.eleType == "station_teaching")
|
const id = `station_teaching_${item.param.src_dst}`
|
const curIndex = list.findIndex((a) => a.id == id)
|
if (curIndex > -1) {
|
|
const obj = list[curIndex]
|
this.canvas.remove(obj)
|
}
|
}
|
} else if (item.method == "edit_teaching") {
|
const teachingMode = item.param
|
_this.showEditTeachingPath(teachingMode)
|
|
} else if (item.method == "update_teaching") {
|
let list = _this.canvas.getObjects() || []
|
list = list.filter((a) => a.eleType == "public_teaching")
|
const id = `public_teaching_${item.param.name}_${item.param.edge_name}`
|
const curIndex = list.findIndex((a) => a.id == id)
|
if (curIndex > -1) {
|
const obj = list[curIndex]
|
let strokeWidth = 1.5
|
let stroke = "#69C0FF"
|
|
if (item.param.main_road == 1) {
|
//stroke = "#69C0FF"
|
if (item.param.bidirection == 1) {
|
stroke = "#ffaa00"
|
}
|
strokeWidth = 3
|
|
} else {
|
if (item.param.bidirection == 1) {
|
stroke = "#FF00FF"
|
}
|
}
|
|
obj.set({
|
strokeWidth,
|
stroke
|
})
|
|
}
|
} else if (item.method == "set_selectable") {
|
if (item.param)
|
_this.setAllObjectSelectable(true)
|
else
|
_this.setAllObjectSelectable(false)
|
if (_this.editObject) {
|
_this.editObject.set({
|
lockMovementX: true,
|
lockMovementY: true,
|
})
|
_this.editObject = null
|
}
|
} else if (item.method == "add_wall") {
|
const wallList = item.param || []
|
for (let i2 in wallList) {
|
const wall = wallList[i2]
|
const obj = await _this.addVirtualWall(wall)
|
}
|
} else if (item.method == "wall_list") {
|
const wallList = item.param || []
|
let list = _this.canvas.getObjects() || []
|
list = list.filter((a) => a.eleType == "virtual_wall")
|
for (let i2 in wallList) {
|
const wall = wallList[i2]
|
const curIndex = list.findIndex((a) => a.id == wall.id)
|
if (curIndex < 0) {
|
await _this.addVirtualWallShow(wall)
|
}
|
}
|
} else if (item.method == "add_region") {
|
const regionList = item.param || []
|
for (let i2 in regionList) {
|
const region = regionList[i2]
|
const obj = await _this.addRegion(region)
|
}
|
} else if (item.method == "region_list") {
|
const regionList = item.param || []
|
let list = _this.canvas.getObjects() || []
|
list = list.filter((a) => a.eleType == "region")
|
for (let i2 in regionList) {
|
const region = regionList[i2]
|
const curIndex = list.findIndex((a) => a.id == region.id)
|
if (curIndex < 0) {
|
await _this.addRegionShow(region)
|
}
|
}
|
} else if (item.method == "remove_wall") {
|
const wallList = item.param || []
|
this.canvas.discardActiveObject()
|
let list = _this.canvas.getObjects() || []
|
list = list.filter((a) => a.eleType == "virtual_wall")
|
for (let i2 in wallList) {
|
const wall = wallList[i2]
|
const curIndex = list.findIndex((a) => a.id == wall.id)
|
if (curIndex > -1) {
|
_this.removeVirtualWall(list[curIndex])
|
}
|
}
|
} else if (item.method == "remove_region") {
|
const regionList = item.param || []
|
this.canvas.discardActiveObject()
|
let list = _this.canvas.getObjects() || []
|
list = list.filter((a) => a.eleType == "region")
|
for (let i2 in regionList) {
|
const region = regionList[i2]
|
const curIndex = list.findIndex((a) => a.id == region.id)
|
if (curIndex > -1) {
|
_this.removeRegion(list[curIndex])
|
}
|
}
|
} else if (item.method == "agv_laser") {
|
this.updateAgvLaser(item.param || {})
|
} else if (item.method == "point_cloud") {
|
this.updateLaserPoint(item.param || {})
|
} else if (item.method == "point_trajectory") {
|
this.addTrajectoryPoint(item.param || [])
|
}
|
|
}
|
if (_this.canvas)
|
_this.canvas.renderAll()
|
} catch (ex) {
|
console.log(ex)
|
this.showError(ex)
|
}
|
},
|
showError(ex) {
|
const type = typeof ex
|
if (type == "string") {
|
let tip = ex
|
console.log(ex)
|
plus.nativeUI.alert(tip, undefined);
|
return
|
}
|
let exStr = JSON.stringify(ex)
|
if (exStr == "{}")
|
exStr = ex
|
|
let tip = typeof ex.msg == "string" ? ex.msg : exStr
|
console.log(tip)
|
plus.nativeUI.alert(tip, undefined);
|
},
|
showToast(ex) {
|
const type = typeof ex
|
if (type == "string") {
|
let tip = ex
|
console.log(ex)
|
plus.nativeUI.toast(tip); // undefined, "错误"
|
return
|
}
|
let exStr = JSON.stringify(ex)
|
if (exStr == "{}")
|
exStr = ex
|
|
let tip = typeof ex.msg == "string" ? ex.msg : exStr
|
console.log(tip)
|
plus.nativeUI.toast(tip, {
|
duration: 'long',
|
verticalAlign: "center"
|
}); // undefined, "错误"
|
},
|
}
|
}
|