@logicflow/extension
Version:
LogicFlow Extensions
529 lines (528 loc) • 21.1 kB
JavaScript
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;