@antv/f6-plugin
Version:
F6 plugin
648 lines (513 loc) • 19.6 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
var _tslib = require("tslib");
var _util = require("@antv/util");
var _matrixUtil = require("@antv/matrix-util");
var _base = _interopRequireDefault(require("../base"));
var _f6Ui = require("@antv/f6-ui");
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
var max = Math.max;
var transform = _matrixUtil.ext.transform;
var DEFAULT_MODE = 'default';
var KEYSHAPE_MODE = 'keyShape';
var DELEGATE_MODE = 'delegate';
var MiniMap =
/** @class */
function (_super) {
(0, _tslib.__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: '',
viewportClassName: '',
// Minimap 中默认展示和主图一样的内容,KeyShape 只展示节点和边的 key shape 部分,delegate表示展示自定义的rect,用户可自定义样式
type: 'default',
padding: 50,
size: [200, 120],
delegateStyle: {
fill: '#40a9ff',
stroke: '#096dd9'
},
refresh: true
};
};
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; // viewport 就是minimap的缩略图 拖拽方框 小程序模式下 不能使用createDom 使用createUi替代
var handleUI = this.get('container').query('.viewport'); // 计算拖拽水平方向距离
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; // 拖拽start事件
handleUI.on('panstart', function (e) {
cfgs.refresh = false; // 如果视口已经最大了,不需要拖拽
left = parseInt(handleUI.getStyle('left'), 10);
top = parseInt(handleUI.getStyle('top'), 10);
width = parseInt(handleUI.getStyle('width'), 10);
height = parseInt(handleUI.getStyle('height'), 10);
if (width > size[0] || height > size[1]) {
return;
}
zoom = graph.getZoom();
ratio = _this.get('ratio');
dragging = true;
x = e.x;
y = e.y;
});
handleUI.on('panmove', function (e) {
if (!dragging || (0, _util.isNil)(e.x) || (0, _util.isNil)(e.y)) {
return;
}
var dx = x - e.x;
var dy = y - e.y; // 若视口移动到最左边或最右边了,仅移动到边界
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; // graph 移动需要偏移量 dx/dy * 缩放比例才会得到正确的移动距离
graph.translate(dx * zoom / ratio, dy * zoom / ratio);
x = e.x;
y = e.y;
}, false);
handleUI.on('panend', function () {
dragging = false;
cfgs.refresh = true;
}, false);
this.set('viewport', handleUI); // 这里viewport的 key 先保留,下面继续使用
};
/**
* 更新 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 graphWidth = graph.get('width');
var graphHeight = graph.get('height');
var topLeft = graph.getPointByCanvas(0, 0);
var bottomRight = graph.getPointByCanvas(graphWidth, graphHeight);
var viewport = this.get('viewport'); // 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);
if (viewport) {
var borderWidth = viewport.getStyle('borderWidth');
if (Math.floor(width) >= borderWidth * 2) {
viewport.setStyle('left', left);
viewport.setStyle('width', width);
}
if (Math.floor(height) >= borderWidth * 2) {
viewport.setStyle('top', top);
viewport.setStyle('height', height);
}
}
};
/**
* 将主图上的图形完全复制到小图
*/
MiniMap.prototype.updateGraphShapes = function () {
var graph = this._cfgs.graph;
var graphGroup = graph.get('group');
if (graphGroup.destroyed) return;
var clonedGroup = graphGroup.clone();
var groupCanvas = this.get('groupCanvas');
clonedGroup.resetMatrix();
groupCanvas.clear();
groupCanvas.add(clonedGroup);
}; // 仅在 minimap 上绘制 keyShape
// FIXME 如果用户自定义绘制了其他内容,minimap上就无法画出
MiniMap.prototype.updateKeyShapes = function () {
var _this = this;
var graph = this._cfgs.graph;
var group = this.get('groupCanvas'); // canvas.get('children')[0] || canvas.addGroup();
(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);
}
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 group = this.get('groupCanvas'); // 差量更新 minimap 上的节点和边
(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);
} else {
mappedItem = group.addShape('rect', {
attrs: (0, _tslib.__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 () {
var _this = this;
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);
setTimeout(function () {
_this.initContainer();
});
};
/**
* 初始化 Minimap 的容器
*/
MiniMap.prototype.initContainer = function () {
var _a;
var self = this;
var graph = self.get('graph');
var size = self.get('size');
var className = self.get('className');
var viewportClassName = self.get('viewportClassName');
var getCss = self.get('getCss');
var containerHtml = "\n <div class='f6-minimap " + className + "'>\n <div class=\"f6-minimap-container\"></div>\n <div class=\"viewport " + viewportClassName + "\"></div>\n </div>";
var containerCss = "\n root {\n width: " + graph.getWidth() + ";\n height: " + graph.getHeight() + ";\n opacity: 0;\n pointer-events: none;\n }\n .f6-minimap{ \n position: absolute;\n left: 0;\n bottom: 0;\n width: " + size[0] + "; height: " + size[1] + ";\n } \n .f6-minimap-container{\n position: relative;\n background: rgba(0,0,0,0);\n width: " + size[0] + "; height: " + size[1] + ";\n }\n .viewport{\n position: absolute;\n border: 3 solid blue;\n width: 100;\n height: 100;\n background: rgba(0,0,0,0);\n z-index: 1000;\n }\n " + ((_a = getCss === null || getCss === void 0 ? void 0 : getCss()) !== null && _a !== void 0 ? _a : '') + "\n ";
var uiGroup = graph.get('uiGroup');
var miniMapContainerUI = (0, _f6Ui.createUI)(containerHtml, containerCss, uiGroup);
var background = miniMapContainerUI.query('.f6-minimap-container');
var group = background.gNode;
var canvasGroup = group.addGroup();
this.set('groupCanvas', canvasGroup);
this.set('container', miniMapContainerUI);
self.updateCanvas();
this.initViewport();
};
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 type = this.get('type'); // minimap 的类型
var padding = this.get('padding'); // 用户额定义的 minimap 的 padding
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 = this.get('groupCanvas'); // canvas.get('children')[0];
if (!group) return;
group.resetMatrix(); // 重新计算所有子节点的matrix,从group开始重新计算totalmatrix,重置传递下来的matrix,保证计算canvasBBox的时候,相对左上角
group.applyMatrix([1, 0, 0, 0, 1, 0, 0, 0, 1]);
var bbox = group.getCanvasBBox();
var graphBBox = graph.get('group').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('container')) === null || _a === void 0 ? void 0 : _a.remove();
};
return MiniMap;
}(_base.default);
var _default = MiniMap;
exports.default = _default;