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 okIcon from '../../../static/images/confirm.svg'; // import cancelIcon from '../../../static/images/remove.svg'; export default { data() { return { vehicleIp: "", 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 } }, mounted() { console.log("ctx mounted") this.init() }, methods: { async init(ip) { try { this.agvObj = null 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, targetFindTolerance: 10, // 增大触摸容差 isTouchSupported: true, enableRetinaScaling: true, renderOnAddRemove: false, imageSmoothingEnabled: true }) 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: "#FFFFFF" }) this.canvas.add(this.workSpace) this.patchFabricForUniApp(this.canvas) this.initFlag = true await this.$nextTick(); this.canvasEventListener() } catch (ex) { this.showError(ex) } }, 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) }, 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.selectionChangeCanvas(); }); _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.resizetCanvas(); }); _this.canvas.on("object:moving", function(e) { console.log("object:moving", e.target); _this.onOjectMoving(e.target) }); cantainerEl.addEventListener('touchstart', function(e) { // console.log('touchstart:', e.touches.length); e.preventDefault(); // 阻止默认行为 _this.canvas.fire('touch:start', { e: e }); // _this.canvas._onMouseDown(e); _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), y: _this.getActualYFromImg(activeObj.top) }) _this.updateRegion(activeObj.mainObj, data) } else if (activeObj.eleType == "cmd") { let data = activeObj.mainObj.data if (activeObj.mainObj.eleType == "edit_teaching") { let left = _this.getActualXFromImg(activeObj.left) let top = _this.getActualYFromImg(activeObj.top) let right = _this.getActualXFromImg(activeObj.left + activeObj.width) let bottom = _this.getActualYFromImg(activeObj.top + activeObj.height) data = [ [left, top], [left, bottom], [right, bottom], [right, top] ] console.log(data) } _this.$ownerInstance.callMethod('receiveRenderData', { method: "edit_finish", cmd: activeObj.id, type: activeObj.mainObj.eleType, data: data, }); if (activeObj.id == "ok") { if (activeObj.mainObj.eleType == "region") { _this.addRegionFinish(activeObj.mainObj) } else if (activeObj.mainObj.eleType == "virtual_wall") { _this.addVirtualWallFinish(activeObj.mainObj) } } if (activeObj.mainObj.eleType == "edit_teaching") { let list = _this.canvas.getObjects() || [] for (let i2 in list) { const obj = list[i2] obj.set({ opacity: 1 }) } if (activeObj.mainObj?.mainObj) { activeObj.mainObj.mainObj.set({ selectable: true }) } const ptObjs = activeObj.mainObj.ptObjs || [] _this.canvas.remove(activeObj.mainObj) for (let i2 in ptObjs) { const obj = ptObjs[i2] _this.canvas.remove(obj) } } if (activeObj.mainObj.eleType == "station") { _this.setAllObjectSelectable(true) activeObj.mainObj.tipObj.set({ left: activeObj.mainObj.left, top: activeObj.mainObj.top - activeObj.mainObj.height / 2 - activeObj.mainObj .tipObj.height / 2, visible: true }) activeObj.mainObj.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); } } }); cantainerEl.addEventListener('touchmove', function(e) { // _this.canvas._onMouseMove(e); // console.log('touchmove:', e.touches.length); e.preventDefault(); // 阻止默认行为 // 处理移动 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); } } }); cantainerEl.addEventListener('touchend', function(e) { // _this.canvas._onMouseUp(e); // console.log('touchend:'); e.preventDefault(); // 阻止默认行为 _this.touchPoint = { x: 0, y: 0 }; 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(); // } } if (this.pressObjTimer) { clearTimeout(this.pressObjTimer); this.pressObjTimer = null } }); cantainerEl.addEventListener('touchcancel', function(e) { // console.log('touchcancel:'); if (this.pressObjTimer) { clearTimeout(this.pressObjTimer); this.pressObjTimer = null } // const activeObj = _this.canvas.getActiveObject() // if (activeObj) { // if (activeObj.lockMovementX) { // _this.canvas.discardActiveObject(); // } // } }) }, // 计算点到线段的距离 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); this.canvas.discardActiveObject() console.log("pointer", pointer.x, pointer.y, objects.length) let pointerList = [] let pointerList2 = [] 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)) { pointerList.push(obj) } } else { const isHit = obj.containsPoint(pointer); if (isHit) { pointerList2.push(obj) } } } } if (pointerList2.length > 0) { const obj = pointerList2[0]; console.log('命中对象:', obj.eleType); this.canvas.discardActiveObject() this.canvas.setActiveObject(obj); this.canvas.requestRenderAll(); return } if (pointerList.length > 0) { const obj = pointerList[0]; console.log('命中对象2:', obj.eleType); this.canvas.discardActiveObject() this.canvas.setActiveObject(obj); this.canvas.requestRenderAll(); return } // objects.forEach(obj => { // if (obj instanceof fabric.Path || obj instanceof fabric.Line) { // // 设置一个“点击容忍度”,比如 5 像素 // const tolerance = 5; // // 临时扩大路径的点击区域 // const originalStrokeWidth = obj.strokeWidth; // obj.strokeWidth = originalStrokeWidth + tolerance * 2; // const isHit = obj.intersectsWithPointer(pointer); // // 恢复原始宽度 // obj.strokeWidth = originalStrokeWidth; // if (isHit) { // pointerList.push(obj) // } // } else if (obj instanceof fabric.Rect || obj instanceof fabric.Ellipse) { // const isHit = obj.containsPoint(pointer); // if (isHit) { // pointerList.push(obj) // } // } // // else if (obj instanceof fabric.Group ) // // { // // const objects2 = obj.getObjects(); // // const isHit = obj.containsPoint(pointer); // // if (isHit) { // // pointerList.push(obj) // // } // // } // }); // if (pointerList.length === 0) { // return // } // const obj = pointerList.pop() // this.canvas.discardActiveObject() // this.canvas.setActiveObject(obj) }, 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.renderAll() } 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.renderAll() } 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.renderAll() } 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.renderAll() } } 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() { const _this = this // const list = _this.canvas.getActiveObjects() // if (list.length === 1) { // if (list[0].eleType == "station") { // _this.$ownerInstance.callMethod('receiveRenderData', { // method: "selected_change", // type: list[0].eleType, // param: list[0].data // }); // } else if (list[0].eleType == "agv") { // _this.$ownerInstance.callMethod('receiveRenderData', { // method: "selected_change", // type: list[0].eleType, // param: list[0].data // }); // } else if (list[0].eleType == "agv_line") { // _this.$ownerInstance.callMethod('receiveRenderData', { // method: "selected_change", // type: list[0].eleType, // param: list[0].data // }); // } else { // _this.$ownerInstance.callMethod('receiveRenderData', { // method: "selected_change", // type: "" // }); // } // } else { // _this.$ownerInstance.callMethod('receiveRenderData', { // method: "selected_change", // type: "" // }); // } }, safeLoadImage(url, maxSize = 2048) { console.log(url) return new Promise((resolve) => { const img = new Image(); img.onload = () => { // 检查尺寸是否超出限制 const scale = Math.min( maxSize / Math.max(img.width, img.height), 1 ); resolve(new fabric.Image(img, { scaleX: scale, scaleY: scale })); }; img.onerror = () => { console.error('图片加载失败'); resolve(null); }; img.src = url; }); }, safeLoadImageData(data, maxSize = 2048) { //console.log("safeLoadImageData") const _this = this return new Promise((resolve) => { let base64Image = data if (base64Image.indexOf("data:image/png;base64,") < 0) { base64Image = "data:image/png;base64," + data } // var img = new fabric.Image(); // img.setSrc(base64Image, function() { // console.log("img", JSON.stringify(img)) // const scale = Math.min( // maxSize / Math.max(img.width, img.height), // 1 // ); // img.set({ // scaleX: scale, // scaleY: scale // }) // resolve(img) // }); fabric.Image.fromURL(base64Image, { crossOrigin: 'anonymous' // 重要:设置跨域 }).then((img) => { //console.log("img",JSON.stringify(img)) const scale = Math.min( maxSize / Math.max(img.width, img.height), 1 ); img.set({ scaleX: scale, scaleY: scale, }) resolve(img) }).catch((err) => { console.error("图片加载失败", err) _this.showError("图片加载失败") resolve(null); }) }); }, // 将 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", }); 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; }, 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; // 边框宽度 this.agvObj = null const cantainerEl = document.getElementById("canvasMap") this.eleWidth = cantainerEl.clientWidth this.eleHeight = cantainerEl.clientHeight console.log("client", this.eleWidth, this.eleHeight) this.canvas.setWidth(this.eleWidth); this.canvas.setHeight(this.eleHeight); this.mapInfo = { proportion: info.proportion || 1, img_proportion: info.img_proportion || 1, max_x: info.max_x || 1, max_y: info.max_y || 1, min_x: info.min_x || 0, min_y: info.min_y || 0, img_x: info.img_x || 1, img_y: 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 } 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)", }) img.set({ // id: "workspace", // eleType: "workspace", left: 0, top: 0, // selectable: false, // hasControls: false, }); 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.canvas.add(wsGroup) if (_this.workSpace) { _this.canvas.remove(_this.workSpace) } _this.workSpace = wsGroup } //_this.checkMemoryUsage() resolve() }).catch((err) => { _this.$ownerInstance.callMethod('receiveRenderData', { method: "set_backgroud_progress", type: "error", }); 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 1 const width = this.workSpace.width const height = this.workSpace.height if (eleWidth / eleHeight < width / height) { return eleWidth / width; } // 按照宽度缩放 return eleHeight / height; }, 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; // this.setCenterFromObject(this.workSpace); // 超出画布不展示 // _this.workSpace.clone().then((cloned) => { // _this.canvas.clipPath = cloned; // _this.canvas.requestRenderAll(); // }); }, 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 } } // 移动视口 // console.log('relativePan', deltaX, deltaY); 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.renderAll() } 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", "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 == "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() } } 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.renderAll() }, 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 / 3.14 console.log("addStation", svg, info) 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 } 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 theta = 20; let headlen = 10; var main_road = teachingData.main_road || 0 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) { if ((index % 50 == 0 || index == len - 1 || index % 50 == 1)) { if (index % 50 == 0 || index == len - 1) { toX = pt2.x toY = pt2.y path2 += ` L${pt2.x} ${pt2.y}` // 计算各角度和对应的P2,P3坐标 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; path2 += " L " + arrowX + " " + arrowY; arrowX = toX + botX; arrowY = toY + botY; path2 += " M " + arrowX + " " + arrowY; path2 += " L " + toX + " " + toY; } } else { path2 += ` L${pt2.x} ${pt2.y}` } } else { path2 = `M${pt2.x} ${pt2.y}` } fromX = pt2.x fromY = pt2.y } let strokeWidth = 1 let stroke = "#95DE64" if (main_road == 1) { 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) } }) console.log(posArr.length, pos_list.length) let path2 = "" const theta = 20; let headlen = 10; var main_road = 1 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) { if ((type == "public_teaching") && (index % 50 == 0 || index == len - 1 || index % 50 == 1)) { if (index % 50 == 1 || index == len - 1) { if (teachingData.bidirection == 1 && index < len - 1) { //- 1 toY = fromY toX = fromX fromX = pt2.x fromY = 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; let path3 = " L " + arrowX + " " + arrowY; arrowX = toX + botX; arrowY = toY + botY; path3 += " M " + arrowX + " " + arrowY; path3 += " L " + toX + " " + toY; path2 += path3 fromY = toY fromX = toX } } if (index % 50 == 0 || index == len - 1) { toX = pt2.x toY = pt2.y // if (fromX == toX && fromY == toY) { // if (index - 2 >= 0) { // const pt3 = pos_list[index - 2] // fromX = this.getXOnImg(pt3.x), // fromY = this.getYOnImg(pt3.y) // } else { // continue; // } // } path2 += ` L${pt2.x} ${pt2.y}` // 计算各角度和对应的P2,P3坐标 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; path2 += " L " + arrowX + " " + arrowY; arrowX = toX + botX; arrowY = toY + botY; path2 += " M " + arrowX + " " + arrowY; path2 += " L " + toX + " " + toY; } // console.log(`箭头 L${pt2.x} ${pt2.y}`) } else { // console.log(`点 L${pt2.x} ${pt2.y} ${index} == ${len-1}`) path2 += ` L${pt2.x} ${pt2.y}` } } else { main_road = pt.main_road if (main_road == 1) { headlen = 15 } path2 = `M${pt2.x} ${pt2.y}` } fromX = pt2.x fromY = pt2.y } //console.log(path2) // path2 += " Z" let strokeWidth = 1 let stroke = "#95DE64" if (type == "station_teaching") { stroke = "#69C0FF" } else { if (main_road == 1) { stroke = "#69C0FF" } } 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 objPath = new fabric.Path( path2, { id: id, eleType: type, 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: 0, mainRoad: main_road, data: teachingData }) this.canvas.add(objPath) // this.canvas.sendObjectToBack(objPath); // lenTeaching = 0 // for (let i = list.length - 1; i >= 0; i--) { // const obj = list[i] // if (this.compareOverlap(obj, objPath)) { // lenTeaching = i + 1 // break // } // } // 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) { 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}` } 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 } console.log(left, top, width, height, scale2) 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, }) /*const zoom = this.canvas.getZoom(); const eleHeight = this.eleHeight - 150 const info = { x: rect.left + rect.width / 2, y: rect.top + rect.height / 2 } let deltaX = info.x * zoom - this.eleWidth / 2 // * scale; let deltaY = info.y * zoom - eleHeight / 2 //* scale; const vpt = this.canvas.viewportTransform; const oldX = vpt[4] const oldY = vpt[5] if (deltaX + this.eleWidth > this.workSpace.width) deltaX = this.workSpace.width - this.eleWidth if (deltaY + eleHeight > this.workSpace.height) deltaY = this.workSpace.height - eleHeight if (oldX + this.eleWidth >= info.x * zoom && info.x * zoom >= oldX) { deltaX = -oldX //console.log("move_pt_center X", oldX) } if (oldY + eleHeight >= info.y * zoom && info.y * zoom >= oldY) { // console.log("move_pt_center Y", oldY) deltaY = -oldY } this.canvas.absolutePan(new fabric.Point(deltaX, deltaY));*/ } 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) { 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 / 3.14 if (this.agvObj) { this.agvObj.set({ left, top, angle, data: info }); this.agvObj.setCoords() _this.canvas.requestRenderAll(); resolve() } 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); _this.agvObj = obj resolve() } ) } }) }, 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 (pt.x < 10) { // pt.x = 10 // } // if (pt.y < 10) { // pt.y = 10 // } // if (pt.x > this.workSpace.width - 10) { // pt.x = 10 // } // if (pt.y > this.workSpace.height - 10) { // pt.y = 10 // } 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) => { // if (pt.x < 10) { // pt.x = 10 // } // if (pt.y < 10) { // pt.y = 10 // } // if (pt.x > this.workSpace.width - 10) { // pt.x = this.workSpace.width - 10 // } // if (pt.y > this.workSpace.height - 10) { // pt.y = this.workSpace.height - 10 // } 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) }, ensurePointVisible(pt) { var zoom = this.canvas.getZoom(); var vpt = this.canvas.viewportTransform; // 当前变换矩阵 var newPanX = vpt[4]; var newPanY = vpt[5]; if (pt.x * zoom < vpt[4] + 80 || pt.x * zoom > vpt[4] + this.eleWidth - 80) { if (pt.x * zoom - this.eleWidth / 2 < 80) { newPanX = -80 } else if (pt.x * zoom > this.mapInfo.img_x * zoom - 80) { newPanX = this.mapInfo.img_x * zoom - this.eleWidth + 80 } else { newPanX = pt.x * zoom - this.eleWidth / 2 } } if (pt.y * zoom < vpt[5] + 80 || pt.y * zoom > vpt[5] + this.eleHeight - 200) { if (pt.y * zoom - this.eleHeight / 2 < 80) { newPanY = -80 } else if (pt.y * zoom > this.mapInfo.img_y * zoom - 200) { newPanY = this.mapInfo.img_y * zoom - this.eleHeight + 200 } else { newPanY = pt.y * zoom - this.eleHeight / 2 } } // 只有在需要时才平移 if (newPanX !== vpt[4] || newPanY !== vpt[5]) { this.canvas.absolutePan({ x: newPanX, y: newPanY }); } }, setAllObjectSelectable(selectable) { let flag = false this.canvas.forEachObject(function(obj) { if (obj.canSelect) { if (!obj.flag) { flag = true } obj.set({ selectable: selectable, lockEdit: true }) } }); if (flag) { this.canvas.requestRenderAll() } }, receiveMsg(newValue, oldValue) { if (typeof newValue == "undefined") return; const _this = this //console.log("receiveMsg",_this.initFlag) setTimeout(() => { if (_this.initFlag) { _this.handleMsg(newValue, oldValue) } else { _this.receiveMsg(newValue, oldValue) } }, 500) }, async handleMsg(newValue, oldValue) { const _this = this try { //console.log("handleMsg", newValue) var data = JSON.parse(newValue); 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 || {} await _this.updateAgv(info) } else if (item.method == "update_current_teaching") { const info = item.param || [] await _this.updateCurrentTeaching(info) } else if (item.method == "move_pt_center") { const info2 = item.param || {} const pt = { x: this.getXOnImg(info2.x), y: this.getYOnImg(info2.y) } this.ensurePointVisible(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 / 3.14 //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 || [] 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") { 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 == "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: false, opacity: 1 }) if (obj.eleType == "station") { await _this.setMarkStation(obj, false) } } _this.showTeachingPath(_this.showTeachPathFlag ? true : false) } else if (item.method == "public_teaching_path") { let list = _this.canvas.getObjects() || [] list = list.filter((a) => a.eleType == "public_teaching") for (let i2 in list) { this.canvas.remove(list[i2]) } const teachingPathList = item.param || [] for (let i2 in teachingPathList) { const teachingPath = teachingPathList[i2] const id = `public_teaching_${teachingPath.name}` await this.addTeachingPath(teachingPath, id, "public_teaching") } } else if (item.method == "station_teaching_path") { let list = _this.canvas.getObjects() || [] list = list.filter((a) => a.eleType == "station_teaching") for (let i2 in list) { this.canvas.remove(list[i2]) } const teachingPathList = item.param || [] for (let i2 in teachingPathList) { const teachingPath = teachingPathList[i2] const id = `station_teaching_${teachingPath.src_dst}` await this.addTeachingPath(teachingPath, id, "station_teaching") } } else if (item.method == "show_teaching_path") { _this.showTeachPathFlag = item.param.show _this.showTeachingPath(item.param.show) } else if (item.method == "remove_teaching_path") { 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}` 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 == "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 || [] 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 || [] 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]) } } } } _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, this.translate('error')); 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, this.translate('error')); }, 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, "错误" }, }, }