"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;