UNPKG

@logicflow/extension

Version:
529 lines (528 loc) 21.1 kB
var __values = (this && this.__values) || function(o) { var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0; if (m) return m.call(o); if (o && typeof o.length === "number") return { next: function () { if (o && i >= o.length) o = void 0; return { value: o && o[i++], done: !o }; } }; throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined."); }; import { createTeleportContainer } from '@logicflow/vue-node-registry'; var MiniMap = /** @class */ (function () { function MiniMap(_a) { var lf = _a.lf, LogicFlow = _a.LogicFlow, options = _a.options; var _this = this; /** * 小地图中画布容器的宽度 */ this.width = 200; /** * 小地图中画布容器的高度 */ this.height = 150; /** * 小地图中画布的缩放比例 */ this.scale = 1; /** * 小地图中画布的水平位移 */ this.translateX = 0; /** * 小地图中画布的垂直位移 */ this.translateY = 0; /** * 在小地图的画布中是否渲染边 */ this.showEdge = false; /** * 预览视窗左上角在主画布的y坐标 */ this.viewPortTop = 0; /** * 预览视窗左上角在主画布的x坐标 */ this.viewPortLeft = 0; // 预览视窗的宽高 this.viewPortWidth = 150; this.viewPortHeight = 75; /** * 是否显示小地图 */ this.isShow = false; /** * 是否显示小地图的标题栏 */ this.isShowHeader = false; /** * 是否显示关闭按钮 */ this.isShowCloseIcon = false; /** * 小地图标题栏的文本内容 */ this.headerTitle = '导航'; /** * 小地图的logicFlow实例需要禁用的插件 */ this.disabledPlugins = ['miniMap', 'control', 'selectionSelect']; this.onGraphResize = function () { _this.updateViewPortBounds(); if (_this.isShow) { _this.updateViewPort(); } }; this.render = function (_, container) { _this.container = container; _this.lf.on('history:change', function () { if (_this.isShow) { _this.setView(); } }); _this.lf.on('graph:transform', function () { if (_this.isShow) { _this.setView(false); } }); }; /** * 显示小地图 * @param left 相对画布的左边距 * @param top 相对画布的上边距 */ this.show = function (left, top) { if (!_this.isShow) { _this.createMiniMap(left, top); _this.setView(); } _this.isShow = true; }; /** * 隐藏小地图 */ this.hide = function () { if (_this.isShow) { _this.removeMiniMap(); _this.lf.emit('miniMap:close', {}); } _this.isShow = false; }; /** * 更新小地图在画布中的位置 * @param {MiniMapPosition} position */ this.updatePosition = function (position) { if (typeof position === 'object') { if (position.left !== undefined || position.right !== undefined) { _this.leftPosition = position.left; _this.rightPosition = position.right; } if (position.top !== undefined || position.bottom !== undefined) { _this.topPosition = position.top; _this.bottomPosition = position.bottom; } } else { switch (position) { case 'left-top': _this.leftPosition = 0; _this.rightPosition = undefined; _this.topPosition = 0; _this.bottomPosition = undefined; break; case 'right-top': _this.leftPosition = undefined; _this.rightPosition = 0; _this.topPosition = 0; _this.bottomPosition = undefined; break; case 'left-bottom': _this.leftPosition = 0; _this.rightPosition = undefined; _this.topPosition = undefined; _this.bottomPosition = 0; break; case 'right-bottom': _this.leftPosition = undefined; _this.rightPosition = 0; _this.topPosition = undefined; _this.bottomPosition = 0; break; } } _this.updateMiniMapPosition(); }; /** * 重置主画布的缩放和平移 */ this.reset = function () { _this.lf.resetTranslate(); _this.lf.resetZoom(); }; /** * 设置小地图的画布中是否显示边 * @param {boolean} showEdge */ this.setShowEdge = function (showEdge) { if (_this.showEdge !== showEdge) { _this.showEdge = showEdge; _this.setView(); } }; this.startDrag = function (e) { document.addEventListener('mousemove', _this.drag); document.addEventListener('mouseup', _this.drop); var x = e.x, y = e.y; _this.startPosition = { x: x, y: y }; }; /** * 拖拽预览视窗过程中,更新主画布视口 */ this.drag = function (e) { var x = e.x, y = e.y; var translateX = (x - _this.startPosition.x) / _this.scale; var translateY = (y - _this.startPosition.y) / _this.scale; var centerX = _this.viewPortLeft + translateX + _this.viewPortWidth / _this.scale / 2; var centerY = _this.viewPortTop + translateY + _this.viewPortHeight / _this.scale / 2; // 每移动一次预览视窗都需要更新拖拽的起始点 _this.startPosition = { x: x, y: y }; _this.lf.focusOn({ coordinate: { x: centerX, y: centerY, }, }); }; /** * 拖拽预览视窗结束,移除拖拽事件 */ this.drop = function () { document.removeEventListener('mousemove', _this.drag); document.removeEventListener('mouseup', _this.drop); }; /** * 点击小地图中非预览视窗的区域时,移动主画布视口聚焦于点击位置 */ this.mapClick = function (e) { var offsetX = e.offsetX, offsetY = e.offsetY; var centerX = _this.translateX + offsetX / _this.scale; var centerY = _this.translateY + offsetY / _this.scale; _this.lf.focusOn({ coordinate: { x: centerX, y: centerY, }, }); }; this.lf = lf; this.LFCtor = LogicFlow; if (options) { this.setOption(options); } this.viewPortWidth = lf.graphModel.width; this.viewPortHeight = lf.graphModel.height; var boundsInit = { left: 0, right: this.viewPortWidth, top: 0, bottom: this.viewPortHeight, }; this.bounds = boundsInit; this.elementAreaBounds = boundsInit; this.viewPortBounds = boundsInit; this.initMiniMap(); lf.on('graph:resize', this.onGraphResize); } /** * 初始化小地图的配置 * @param options */ MiniMap.prototype.setOption = function (options) { var _a = options.width, width = _a === void 0 ? 150 : _a, _b = options.height, height = _b === void 0 ? 220 : _b, _c = options.showEdge, showEdge = _c === void 0 ? false : _c, _d = options.isShowHeader, isShowHeader = _d === void 0 ? false : _d, _e = options.isShowCloseIcon, isShowCloseIcon = _e === void 0 ? false : _e, leftPosition = options.leftPosition, topPosition = options.topPosition, _f = options.rightPosition, rightPosition = _f === void 0 ? 0 : _f, _g = options.bottomPosition, bottomPosition = _g === void 0 ? 0 : _g, _h = options.headerTitle, headerTitle = _h === void 0 ? '导航' : _h; this.width = width; this.height = height; this.showEdge = showEdge; this.isShowHeader = isShowHeader; this.isShowCloseIcon = isShowCloseIcon; this.leftPosition = leftPosition; this.rightPosition = leftPosition !== undefined ? undefined : rightPosition; this.topPosition = topPosition; this.bottomPosition = topPosition !== undefined ? undefined : bottomPosition; this.headerTitle = headerTitle; }; /** * 初始化小地图的 LogicFlow 实例 */ MiniMap.prototype.initMiniMap = function () { var miniMapWrap = document.createElement('div'); miniMapWrap.className = 'lf-mini-map-graph'; miniMapWrap.style.width = "".concat(this.width, "px"); miniMapWrap.style.height = "".concat(this.height, "px"); this.lfMap = new this.LFCtor({ container: miniMapWrap, grid: false, isSilentMode: true, stopZoomGraph: true, stopScrollGraph: true, // 禁用画布移动会导致 transformModel.translate 无效,所以这里不禁用 stopMoveGraph: false, history: false, snapline: false, disabledPlugins: this.disabledPlugins, }); // minimap中禁用adapter。 // this.lfMap.adapterIn = (a) => a // this.lfMap.adapterOut = (a) => a // 创建teleport容器(vue3中生效) createTeleportContainer(miniMapWrap, this.lfMap.graphModel.flowId); this.miniMapWrap = miniMapWrap; this.createViewPort(); miniMapWrap.addEventListener('click', this.mapClick); }; MiniMap.prototype.createMiniMap = function (left, top) { var _a; var miniMapContainer = document.createElement('div'); this.miniMapContainer = miniMapContainer; miniMapContainer.appendChild(this.miniMapWrap); miniMapContainer.style.position = 'absolute'; if (left !== undefined || top !== undefined) { this.leftPosition = left || 0; this.topPosition = top || 0; this.rightPosition = undefined; this.bottomPosition = undefined; } this.updateMiniMapPosition(); miniMapContainer.className = 'lf-mini-map'; if (!this.isShowCloseIcon) { miniMapContainer.classList.add('lf-mini-map-no-close-icon'); } if (!this.isShowHeader) { miniMapContainer.classList.add('lf-mini-map-no-header'); } (_a = this.container) === null || _a === void 0 ? void 0 : _a.appendChild(miniMapContainer); this.miniMapWrap.appendChild(this.viewport); var header = document.createElement('div'); header.className = 'lf-mini-map-header'; header.innerText = this.headerTitle; miniMapContainer.appendChild(header); var close = document.createElement('span'); close.className = 'lf-mini-map-close'; close.addEventListener('click', this.hide); miniMapContainer.appendChild(close); }; MiniMap.prototype.updateMiniMapPosition = function () { if (this.miniMapContainer) { var style = this.miniMapContainer.style; if (this.rightPosition !== undefined) { style.right = "".concat(this.rightPosition, "px"); style.left = ''; } else { style.left = "".concat(this.leftPosition, "px"); style.right = ''; } if (this.bottomPosition !== undefined) { style.bottom = "".concat(this.bottomPosition, "px"); style.top = ''; } else { style.top = "".concat(this.topPosition, "px"); style.bottom = ''; } } }; MiniMap.prototype.removeMiniMap = function () { var _a; if (this.miniMapContainer) { (_a = this.container) === null || _a === void 0 ? void 0 : _a.removeChild(this.miniMapContainer); } }; /** * 更新小地图的区域范围 * @param data */ MiniMap.prototype.updateBounds = function (data) { if (data) { this.updateElementAreaBounds(data); } this.updateViewPortBounds(); this.bounds = { left: Math.min(this.elementAreaBounds.left, this.viewPortBounds.left), right: Math.max(this.elementAreaBounds.right, this.viewPortBounds.right), top: Math.min(this.elementAreaBounds.top, this.viewPortBounds.top), bottom: Math.max(this.elementAreaBounds.bottom, this.viewPortBounds.bottom), }; }; /** * 计算所有图形一起,占领的区域范围。 * @param data */ MiniMap.prototype.updateElementAreaBounds = function (data) { var elementAreaBounds = { left: 0, right: 0, top: 0, bottom: 0, }; var nodes = data.nodes; if (nodes && nodes.length > 0) { // TODO: 后续能获取节点高宽信息后,需要更新这里的计算方式 nodes.forEach(function (node) { var _a, _b; var x = node.x, y = node.y; var width = (_a = node.width) !== null && _a !== void 0 ? _a : 200; var height = (_b = node.height) !== null && _b !== void 0 ? _b : 200; var nodeLeft = x - width / 2; var nodeRight = x + width / 2; var nodeTop = y - height / 2; var nodeBottom = y + height / 2; elementAreaBounds.left = Math.min(nodeLeft, elementAreaBounds.left); elementAreaBounds.right = Math.max(nodeRight, elementAreaBounds.right); elementAreaBounds.top = Math.min(nodeTop, elementAreaBounds.top); elementAreaBounds.bottom = Math.max(nodeBottom, elementAreaBounds.bottom); }); } this.elementAreaBounds = elementAreaBounds; }; /** * 获取视口范围 */ MiniMap.prototype.updateViewPortBounds = function () { var _a = this.lf.getTransform(), TRANSLATE_X = _a.TRANSLATE_X, TRANSLATE_Y = _a.TRANSLATE_Y, SCALE_X = _a.SCALE_X, SCALE_Y = _a.SCALE_Y; var _b = this.lf.graphModel, width = _b.width, height = _b.height; this.viewPortBounds = { left: -TRANSLATE_X / SCALE_X, right: (-TRANSLATE_X + width) / SCALE_X, top: -TRANSLATE_Y / SCALE_Y, bottom: (-TRANSLATE_Y + height) / SCALE_Y, }; }; /** * 删除部分内容以简化渲染,包括边与节点文本 */ MiniMap.prototype.resetData = function (data) { var nodes = data.nodes, edges = data.edges; nodes.forEach(function (node) { // 删除节点文本 node.text = undefined; }); if (this.showEdge) { edges.forEach(function (edge) { // 删除边上的文本 edge.text = undefined; }); } return { nodes: nodes, // 是否渲染边 edges: this.showEdge ? edges : [], }; }; /** * MiniMap视图重绘 * @param reRender 是否重新渲染画布元素 */ // TODO: 确定 render 函数是否为增量渲染,如果是则不需要 reRender 参数做限制 MiniMap.prototype.setView = function (reRender) { var e_1, _a; if (reRender === void 0) { reRender = true; } if (reRender) { // 1. 获取到图中所有的节点中的位置 var graphData = this.lf.getGraphRawData(); var data = this.resetData(graphData); // 由于随时都会有新节点注册进来,需要同步将注册的 var viewMap = this.lf.viewMap; var modelMap = this.lf.graphModel.modelMap; var minimapViewMap = this.lfMap.viewMap; try { for (var _b = __values(viewMap.keys()), _c = _b.next(); !_c.done; _c = _b.next()) { var key = _c.value; if (!minimapViewMap.has(key)) { this.lfMap.register({ type: key, view: viewMap.get(key), model: modelMap.get(key), }); } } } catch (e_1_1) { e_1 = { error: e_1_1 }; } finally { try { if (_c && !_c.done && (_a = _b.return)) _a.call(_b); } finally { if (e_1) throw e_1.error; } } // 2. 将数据渲染到小地图的画布上 this.lfMap.render(data); // 3. 更新所有节点与当前视口构成的区域范围 this.updateBounds(data); } else { this.updateBounds(); } // 4. 计算小地图画布相对小地图容器的缩放比例,并移动小地图的视图保证元素全部可见且整体居中。 var _d = this.bounds, left = _d.left, top = _d.top, right = _d.right, bottom = _d.bottom; var realWidth = right - left; var realHeight = bottom - top; var realWidthScale = this.width / realWidth; var realHeightScale = this.height / realHeight; var scale = Math.min(realWidthScale, realHeightScale); this.scale = scale; var translateX = left - (this.width / scale - realWidth) / 2; var translateY = top - (this.height / scale - realHeight) / 2; this.lfMap.graphModel.transformModel.translate(-translateX + this.translateX, -translateY + this.translateY); this.translateX = translateX; this.translateY = translateY; // 5. 将小地图的画布缩放对应的比例。 if (this.miniMapWrap.firstChild) { var innerStyle = this.miniMapWrap.firstChild.style; innerStyle.pointerEvents = 'none'; innerStyle.transform = "matrix(".concat(scale, ", 0, 0, ").concat(scale, ", 0, 0)"); innerStyle.transformOrigin = 'left top'; innerStyle.height = "".concat(this.height / scale, "px"); innerStyle.width = "".concat(this.width / scale, "px"); this.updateViewPort(); } }; /** * 更新预览视窗的位置 */ MiniMap.prototype.updateViewPort = function () { var viewStyle = this.viewport.style; var _a = this.lf.getTransform(), TRANSLATE_X = _a.TRANSLATE_X, TRANSLATE_Y = _a.TRANSLATE_Y, SCALE_X = _a.SCALE_X, SCALE_Y = _a.SCALE_Y; var _b = this.lf.graphModel, width = _b.width, height = _b.height; this.viewPortLeft = -TRANSLATE_X / SCALE_X; this.viewPortTop = -TRANSLATE_Y / SCALE_Y; this.viewPortWidth = (width / SCALE_X) * this.scale; this.viewPortHeight = (height / SCALE_Y) * this.scale; viewStyle.width = "".concat(this.viewPortWidth, "px"); viewStyle.height = "".concat(this.viewPortHeight, "px"); viewStyle.left = "".concat((this.viewPortLeft - this.translateX) * this.scale, "px"); viewStyle.top = "".concat((this.viewPortTop - this.translateY) * this.scale, "px"); }; /** * 创建预览视窗元素 */ MiniMap.prototype.createViewPort = function () { var div = document.createElement('div'); div.className = 'lf-minimap-viewport'; // 拖拽预览视窗,主画布视口跟随移动 div.addEventListener('mousedown', this.startDrag); // 禁止预览视窗的点击事件冒泡 div.addEventListener('click', function (e) { e.stopPropagation(); }); this.viewport = div; }; MiniMap.prototype.destroy = function () { this.lf.off('graph:resize', this.onGraphResize); }; MiniMap.pluginName = 'miniMap'; return MiniMap; }()); export { MiniMap }; export default MiniMap;