"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.default = void 0; var _gCanvas = require("@antv/g-canvas"); var _gSvg = require("@antv/g-svg"); var _util = require("@antv/util"); var _domUtil = require("@antv/dom-util"); var _matrixUtil = require("@antv/matrix-util"); var _base = _interopRequireDefault(require("../base")); function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; } var __extends = void 0 && (void 0).__extends || function () { var _extendStatics = function extendStatics(d, b) { _extendStatics = Object.setPrototypeOf || { __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; } || function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; }; return _extendStatics(d, b); }; return function (d, b) { if (typeof b !== "function" && b !== null) throw new TypeError("Class extends value " + String(b) + " is not a constructor or null"); _extendStatics(d, b); function __() { this.constructor = d; } d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); }; }(); var __assign = void 0 && (void 0).__assign || function () { __assign = Object.assign || function (t) { for (var s, i = 1, n = arguments.length; i < n; i++) { s = arguments[i]; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p]; } return t; }; return __assign.apply(this, arguments); }; var max = Math.max; var transform = _matrixUtil.ext.transform; var DEFAULT_MODE = 'default'; var KEYSHAPE_MODE = 'keyShape'; var DELEGATE_MODE = 'delegate'; var SVG = 'svg'; var MiniMap = /** @class */function (_super) { __extends(MiniMap, _super); function MiniMap(config) { var _this = _super.call(this, config) || this; /** * 主图更新的监听函数,使用 debounce 减少渲染频率 * e.g. 拖拽节点只会在松手后的 100ms 后执行 updateCanvas * e.g. render 时大量 addItem 也只会执行一次 updateCanvas */ _this.handleUpdateCanvas = (0, _util.debounce)(function (event) { var self = _this; if (self.destroyed) return; self.updateCanvas(); }, 100, false); return _this; } MiniMap.prototype.getDefaultCfgs = function () { return { container: null, className: 'g6-minimap', viewportClassName: 'g6-minimap-viewport', // Minimap 中默认展示和主图一样的内容,KeyShape 只展示节点和边的 key shape 部分,delegate表示展示自定义的rect,用户可自定义样式 type: 'default', padding: 50, size: [200, 120], delegateStyle: { fill: '#40a9ff', stroke: '#096dd9' }, refresh: true, hideEdge: false }; }; MiniMap.prototype.getEvents = function () { return { beforepaint: 'updateViewport', beforeanimate: 'disableRefresh', afteranimate: 'enableRefresh', viewportchange: 'disableOneRefresh' }; }; // 若是正在进行动画,不刷新缩略图 MiniMap.prototype.disableRefresh = function () { this.set('refresh', false); }; MiniMap.prototype.enableRefresh = function () { this.set('refresh', true); this.updateCanvas(); }; MiniMap.prototype.disableOneRefresh = function () { this.set('viewportChange', true); }; MiniMap.prototype.initViewport = function () { var _this = this; var cfgs = this._cfgs; var size = cfgs.size, graph = cfgs.graph; if (this.destroyed) return; var canvas = this.get('canvas'); var containerDOM = canvas.get('container'); var isFireFox = navigator.userAgent.toLowerCase().indexOf('firefox') > -1; var isSafari = navigator.userAgent.toLowerCase().indexOf('safari') > -1; var viewport = (0, _domUtil.createDom)("\n ")); // 计算拖拽水平方向距离 var x = 0; // 计算拖拽垂直方向距离 var y = 0; // 是否在拖拽minimap的视口 var dragging = false; // 缓存viewport当前对于画布的x var left = 0; // 缓存viewport当前对于画布的y var top = 0; // 缓存viewport当前宽度 var width = 0; // 缓存viewport当前高度 var height = 0; var ratio = 0; var zoom = 0; var dragstartevent = isSafari || isFireFox ? 'mousedown' : 'dragstart'; viewport.addEventListener(dragstartevent, function (e) { var _a, _b; if (e.dataTransfer) { var img = new Image(); img.src = "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' %3E%3Cpath /%3E%3C/svg%3E"; (_b = (_a = e.dataTransfer).setDragImage) === null || _b === void 0 ? void 0 : _b.call(_a, img, 0, 0); try { e.dataTransfer.setData('text/html', 'view-port-minimap'); } catch (_c) { // support IE e.dataTransfer.setData('text', 'view-port-minimap'); } } cfgs.refresh = false; if (e.target !== viewport) { return; } // 如果视口已经最大了,不需要拖拽 var style = viewport.style; left = parseInt(style.left, 10); top = parseInt(style.top, 10); width = parseInt(style.width, 10); height = parseInt(style.height, 10); if (width > size[0] || height > size[1]) { return; } zoom = graph.getZoom(); ratio = _this.get('ratio'); dragging = true; x = e.clientX; y = e.clientY; }, false); var dragListener = function dragListener(e) { if (!dragging || (0, _util.isNil)(e.clientX) || (0, _util.isNil)(e.clientY)) { return; } var dx = x - e.clientX; var dy = y - e.clientY; // 若视口移动到最左边或最右边了,仅移动到边界 if (left - dx < 0 || left - dx + width >= size[0]) { dx = 0; } // 若视口移动到最上或最下边了,仅移动到边界 if (top - dy < 0 || top - dy + height >= size[1]) { dy = 0; } left -= dx; top -= dy; // 先移动视口,避免移动到边上以后出现视口闪烁 (0, _domUtil.modifyCSS)(viewport, { left: "".concat(left, "px"), top: "".concat(top, "px") }); // graph 移动需要偏移量 dx/dy * 缩放比例才会得到正确的移动距离 graph.translate(dx * zoom / ratio, dy * zoom / ratio); x = e.clientX; y = e.clientY; }; if (!isSafari && !isFireFox) { viewport.addEventListener('drag', dragListener, false); } var dragendListener = function dragendListener() { dragging = false; cfgs.refresh = true; }; var dragendevent = isSafari || isFireFox ? 'mouseup' : 'dragend'; viewport.addEventListener(dragendevent, dragendListener, false); containerDOM.addEventListener('mouseleave', dragendListener); containerDOM.addEventListener('mouseup', dragendListener); if (isSafari || isFireFox) { containerDOM.addEventListener('mousemove', dragListener, false); } this.set('viewport', viewport); containerDOM.appendChild(viewport); }; /** * 更新 viewport 视图 */ MiniMap.prototype.updateViewport = function () { if (this.destroyed) return; var ratio = this.get('ratio'); var totaldx = this.get('totaldx'); var totaldy = this.get('totaldy'); var graph = this.get('graph'); var size = this.get('size'); var graphCanvasEl = graph.get('canvas').get('el'); var graphWidth = graph.get('width') || graphCanvasEl.scrollWidth || 500; var graphHeight = graph.get('height') || graphCanvasEl.scrollHeight || 500; var topLeft = graph.getPointByCanvas(0, 0); var bottomRight = graph.getPointByCanvas(graphWidth, graphHeight); var viewport = this.get('viewport'); if (!viewport) { this.initViewport(); } // viewport宽高,左上角点的计算 var width = (bottomRight.x - topLeft.x) * ratio; var height = (bottomRight.y - topLeft.y) * ratio; var left = topLeft.x * ratio + totaldx; var top = topLeft.y * ratio + totaldy; var right = left + width; var bottom = top + height; if (left < 0) { width += left; left = 0; } if (right > size[0]) { width = width - (right - size[0]); } if (top < 0) { height += top; top = 0; } if (bottom > size[1]) { height = height - (bottom - size[1]); } // 缓存目前缩放比,在移动 minimap 视窗时就不用再计算大图的移动量 this.set('ratio', ratio); var correctLeft = "".concat(left, "px"); var correctTop = "".concat(top, "px"); (0, _domUtil.modifyCSS)(viewport, { left: correctLeft, top: correctTop, width: "".concat(width, "px"), height: "".concat(height, "px") }); }; /** * 将主图上的图形完全复制到小图 */ MiniMap.prototype.updateGraphShapes = function () { var graph = this._cfgs.graph; var canvas = this.get('canvas'); var graphGroup = graph.get('group'); if (graphGroup.destroyed) return; canvas.clear(); var clonedGroup; if (this.get('hideEdge')) { clonedGroup = canvas.addGroup(); graphGroup.get('children').forEach(function (group) { if (group.get('id').includes('-edge')) return; clonedGroup.add(group.clone()); }); } else { clonedGroup = graphGroup.clone(); clonedGroup.resetMatrix(); canvas.add(clonedGroup); } // 当 renderer 是 svg,由于渲染引擎的 bug,这里需要将 visible 为 false 的元素手动隐藏 var renderer = graph.get('renderer'); if (renderer === SVG) { // 递归更新子元素 this.updateVisible(clonedGroup); } }; // svg 在 canvas.add(clonedGroup) 之后会出现 visible 为 false 的元素被展示出来,需要递归更新 MiniMap.prototype.updateVisible = function (ele) { var _this = this; if (!ele.isGroup() && !ele.get('visible')) { ele.hide(); } else { var children = ele.get('children'); if (!children || !children.length) return; children.forEach(function (child) { if (!child.get('visible')) child.hide(); _this.updateVisible(child); }); } }; // 仅在 minimap 上绘制 keyShape // FIXME 如果用户自定义绘制了其他内容,minimap上就无法画出 MiniMap.prototype.updateKeyShapes = function () { var _this = this; var graph = this._cfgs.graph; var canvas = this.get('canvas'); var group = canvas.get('children')[0] || canvas.addGroup(); if (!this.get('hideEdge')) { (0, _util.each)(graph.getEdges(), function (edge) { _this.updateOneEdgeKeyShape(edge, group); }); } (0, _util.each)(graph.getNodes(), function (node) { _this.updateOneNodeKeyShape(node, group); }); var combos = graph.getCombos(); if (combos && combos.length) { var comboGroup_1 = group.find(function (e) { return e.get('name') === 'comboGroup'; }) || group.addGroup({ name: 'comboGroup' }); setTimeout(function () { if (_this.destroyed) return; (0, _util.each)(combos, function (combo) { _this.updateOneComboKeyShape(combo, comboGroup_1); }); comboGroup_1 === null || comboGroup_1 === void 0 ? void 0 : comboGroup_1.sort(); comboGroup_1 === null || comboGroup_1 === void 0 ? void 0 : comboGroup_1.toBack(); _this.updateCanvas(); }, 250); } this.clearDestroyedShapes(); }; /** * 增加/更新单个元素的 keyShape * @param item ICombo 实例 */ MiniMap.prototype.updateOneComboKeyShape = function (item, comboGroup) { if (this.destroyed) return; var itemMap = this.get('itemMap') || {}; // 差量更新 minimap 上的一个节点,对应主图的 item var mappedItem = itemMap[item.get('id')]; var bbox = item.getBBox(); // 计算了节点父组矩阵的 bbox var cKeyShape = item.get('keyShape').clone(); var keyShapeStyle = cKeyShape.attr(); var attrs = { x: bbox.centerX, y: bbox.centerY }; if (!mappedItem) { mappedItem = cKeyShape; comboGroup.add(mappedItem); } else { attrs = Object.assign(keyShapeStyle, attrs); } var shapeType = mappedItem.get('type'); if (shapeType === 'rect' || shapeType === 'image') { attrs.x = bbox.minX; attrs.y = bbox.minY; } mappedItem.attr(attrs); if (!item.isVisible()) mappedItem.hide();else mappedItem.show(); mappedItem.exist = true; var zIndex = item.getModel().depth; if (!isNaN(zIndex)) mappedItem.set('zIndex', zIndex); itemMap[item.get('id')] = mappedItem; this.set('itemMap', itemMap); }; /** * 增加/更新单个元素的 keyShape * @param item INode 实例 */ MiniMap.prototype.updateOneNodeKeyShape = function (item, group) { var itemMap = this.get('itemMap') || {}; // 差量更新 minimap 上的一个节点,对应主图的 item var mappedItem = itemMap[item.get('id')]; var bbox = item.getBBox(); // 计算了节点父组矩阵的 bbox var cKeyShape = item.get('keyShape').clone(); var keyShapeStyle = cKeyShape.attr(); var attrs = { x: bbox.centerX, y: bbox.centerY }; if (!mappedItem) { mappedItem = cKeyShape; group.add(mappedItem); } else { attrs = Object.assign(keyShapeStyle, attrs); mappedItem.toFront(); } var shapeType = mappedItem.get('type'); if (shapeType === 'rect' || shapeType === 'image') { attrs.x = bbox.minX; attrs.y = bbox.minY; } mappedItem.attr(attrs); if (!item.isVisible()) mappedItem.hide();else mappedItem.show(); mappedItem.exist = true; var zIndex = item.getModel().depth; if (!isNaN(zIndex)) mappedItem.set('zIndex', zIndex); itemMap[item.get('id')] = mappedItem; this.set('itemMap', itemMap); }; /** * Minimap 中展示自定义的rect,支持用户自定义样式和节点大小 */ MiniMap.prototype.updateDelegateShapes = function () { var _this = this; var graph = this._cfgs.graph; var canvas = this.get('canvas'); var group = canvas.get('children')[0] || canvas.addGroup(); // 差量更新 minimap 上的节点和边 if (!this.get('hideEdge')) { (0, _util.each)(graph.getEdges(), function (edge) { _this.updateOneEdgeKeyShape(edge, group); }); } (0, _util.each)(graph.getNodes(), function (node) { _this.updateOneNodeDelegateShape(node, group); }); var combos = graph.getCombos(); if (combos && combos.length) { var comboGroup_2 = group.find(function (e) { return e.get('name') === 'comboGroup'; }) || group.addGroup({ name: 'comboGroup' }); setTimeout(function () { if (_this.destroyed) return; (0, _util.each)(combos, function (combo) { _this.updateOneComboKeyShape(combo, comboGroup_2); }); comboGroup_2 === null || comboGroup_2 === void 0 ? void 0 : comboGroup_2.sort(); comboGroup_2 === null || comboGroup_2 === void 0 ? void 0 : comboGroup_2.toBack(); _this.updateCanvas(); }, 250); } this.clearDestroyedShapes(); }; MiniMap.prototype.clearDestroyedShapes = function () { var itemMap = this.get('itemMap') || {}; var keys = Object.keys(itemMap); if (!keys || keys.length === 0) return; for (var i = keys.length - 1; i >= 0; i--) { var shape = itemMap[keys[i]]; var exist = shape.exist; shape.exist = false; if (!exist) { shape.remove(); delete itemMap[keys[i]]; } } }; /** * 设置只显示 edge 的 keyShape * @param item IEdge 实例 */ MiniMap.prototype.updateOneEdgeKeyShape = function (item, group) { var itemMap = this.get('itemMap') || {}; // 差量更新 minimap 上的一个节点,对应主图的 item var mappedItem = itemMap[item.get('id')]; if (mappedItem) { var path = item.get('keyShape').attr('path'); mappedItem.attr('path', path); } else { mappedItem = item.get('keyShape').clone(); group.add(mappedItem); } if (!item.isVisible()) mappedItem.hide();else mappedItem.show(); mappedItem.exist = true; itemMap[item.get('id')] = mappedItem; this.set('itemMap', itemMap); }; /** * Minimap 中展示自定义的 rect,支持用户自定义样式和节点大小 * 增加/更新单个元素 * @param item INode 实例 */ MiniMap.prototype.updateOneNodeDelegateShape = function (item, group) { var delegateStyle = this.get('delegateStyle'); var itemMap = this.get('itemMap') || {}; // 差量更新 minimap 上的一个节点,对应主图的 item var mappedItem = itemMap[item.get('id')]; var bbox = item.getBBox(); // 计算了节点父组矩阵的 bbox if (mappedItem) { var attrs = { x: bbox.minX, y: bbox.minY, width: bbox.width, height: bbox.height }; mappedItem.attr(attrs); mappedItem.toFront(); } else { mappedItem = group.addShape('rect', { attrs: __assign({ x: bbox.minX, y: bbox.minY, width: bbox.width, height: bbox.height }, delegateStyle), name: 'minimap-node-shape' }); } if (!item.isVisible()) mappedItem.hide();else mappedItem.show(); mappedItem.exist = true; itemMap[item.get('id')] = mappedItem; this.set('itemMap', itemMap); }; MiniMap.prototype.init = function () { this.initContainer(); this.get('graph').on('afterupdateitem', this.handleUpdateCanvas); this.get('graph').on('afteritemstatechange', this.handleUpdateCanvas); this.get('graph').on('afteradditem', this.handleUpdateCanvas); this.get('graph').on('afterremoveitem', this.handleUpdateCanvas); this.get('graph').on('afterrender', this.handleUpdateCanvas); this.get('graph').on('afterlayout', this.handleUpdateCanvas); }; /** * 初始化 Minimap 的容器 */ MiniMap.prototype.initContainer = function () { var self = this; var graph = self.get('graph'); var size = self.get('size'); var className = self.get('className'); var parentNode = self.get('container'); var container = (0, _domUtil.createDom)("
")); if ((0, _util.isString)(parentNode)) { parentNode = document.getElementById(parentNode); } if (parentNode) { parentNode.appendChild(container); } else { graph.get('container').appendChild(container); } self.set('container', container); var containerDOM = (0, _domUtil.createDom)('
'); container.appendChild(containerDOM); containerDOM.addEventListener('dragenter', function (e) { e.preventDefault(); }); containerDOM.addEventListener('dragover', function (e) { e.preventDefault(); }); var canvas; var renderer = graph.get('renderer'); if (renderer === SVG) { canvas = new _gSvg.Canvas({ container: containerDOM, width: size[0], height: size[1] }); } else { canvas = new _gCanvas.Canvas({ container: containerDOM, width: size[0], height: size[1] }); } self.set('canvas', canvas); self.updateCanvas(); }; MiniMap.prototype.updateCanvas = function () { if (this.destroyed) return; // 如果是在动画,则不刷新视图 var isRefresh = this.get('refresh'); if (!isRefresh) { return; } var graph = this.get('graph'); if (graph.get('destroyed')) { return; } // 如果是视口变换,也不刷新视图,但是需要重置视口大小和位置 if (this.get('viewportChange')) { this.set('viewportChange', false); this.updateViewport(); } var size = this.get('size'); // 用户定义的 minimap size var canvas = this.get('canvas'); // minimap 的 canvas var type = this.get('type'); // minimap 的类型 var padding = this.get('padding'); // 用户额定义的 minimap 的 padding if (canvas.destroyed) { return; } switch (type) { case DEFAULT_MODE: this.updateGraphShapes(); break; case KEYSHAPE_MODE: this.updateKeyShapes(); break; case DELEGATE_MODE: // 得到的节点直接带有 x 和 y,每个节点不存在父 group,即每个节点位置不由父 group 控制 this.updateDelegateShapes(); break; default: break; } var group = canvas.get('children')[0]; if (!group) return; group.resetMatrix(); // 该 bbox 是准确的,不计算 matrix 的包围盒 var bbox = group.getCanvasBBox(); var graphBBox = graph.get('canvas').getCanvasBBox(); // 主图的 bbox var graphZoom = graph.getZoom() || 1; var width = graphBBox.width / graphZoom; var height = graphBBox.height / graphZoom; if (Number.isFinite(bbox.width)) { // 刷新后bbox可能会变,需要重置画布矩阵以缩放到合适的大小 width = max(bbox.width, width); height = max(bbox.height, height); } width += 2 * padding; height += 2 * padding; var ratio = Math.min(size[0] / width, size[1] / height); var matrix = [1, 0, 0, 0, 1, 0, 0, 0, 1]; var minX = 0; var minY = 0; // 平移到左上角 if (Number.isFinite(bbox.minX)) { minX = -bbox.minX; } if (Number.isFinite(bbox.minY)) { minY = -bbox.minY; } // 缩放到适合视口后, 平移到画布中心 var dx = (size[0] - (width - 2 * padding) * ratio) / 2; var dy = (size[1] - (height - 2 * padding) * ratio) / 2; matrix = transform(matrix, [['t', minX, minY], ['s', ratio, ratio], ['t', dx, dy] // 移动到画布中心 ]); group.setMatrix(matrix); // 更新minimap视口 this.set('ratio', ratio); this.set('totaldx', dx + minX * ratio); this.set('totaldy', dy + minY * ratio); this.set('dx', dx); this.set('dy', dy); this.updateViewport(); }; /** * 获取minimap的画布 * @return {GCanvas} G的canvas实例 */ MiniMap.prototype.getCanvas = function () { return this.get('canvas'); }; /** * 获取minimap的窗口 * @return {HTMLElement} 窗口的dom实例 */ MiniMap.prototype.getViewport = function () { return this.get('viewport'); }; /** * 获取minimap的容器dom * @return {HTMLElement} dom */ MiniMap.prototype.getContainer = function () { return this.get('container'); }; MiniMap.prototype.destroy = function () { var _a; (_a = this.get('canvas')) === null || _a === void 0 ? void 0 : _a.destroy(); var container = this.get('container'); if (container === null || container === void 0 ? void 0 : container.parentNode) container.parentNode.removeChild(container); }; return MiniMap; }(_base.default); var _default = exports.default = MiniMap;