UNPKG

@antv/f6

Version:

A Mobile Graph Visualization Framework in JavaScript

277 lines (276 loc) 10.3 kB
import { __awaiter, __extends, __generator } from "tslib"; import { AbstractLayout } from '@antv/f6-core'; import { Layout } from '../../layout'; import { mix, clone } from '@antv/util'; var LayoutPipesAdjustNames = ['force', 'grid', 'circular']; var LayoutController = /** @class */function (_super) { __extends(LayoutController, _super); function LayoutController(graph) { var _this = _super.call(this, graph) || this; _this.graph = graph; _this.layoutCfg = graph.get('layout') || {}; _this.layoutType = _this.getLayoutType(); return _this; } // 更新布局参数 LayoutController.prototype.updateLayoutCfg = function (cfg) { var _this = this; var _a = this, graph = _a.graph, layoutMethods = _a.layoutMethods; var layoutCfg = mix({}, this.layoutCfg, cfg); this.layoutCfg = layoutCfg; if (!(layoutMethods === null || layoutMethods === void 0 ? void 0 : layoutMethods.length)) { this.layout(); return; } this.data = this.setDataFromGraph(); graph.emit('beforelayout'); var start = Promise.resolve(); if (layoutMethods.length === 1) { start = start.then(function () { return _this.updateLayoutMethod(layoutMethods[0], layoutCfg); }); } else { layoutMethods === null || layoutMethods === void 0 ? void 0 : layoutMethods.forEach(function (layoutMethod, index) { var currentCfg = layoutCfg.pipes[index]; start = start.then(function () { return _this.updateLayoutMethod(layoutMethod, currentCfg); }); }); } this.data = this.setDataFromGraph(); start.then(function () { if (layoutCfg.onAllLayoutEnd) layoutCfg.onAllLayoutEnd(); }).catch(function (error) { console.warn('layout failed', error); }); }; /** * @param {function} success callback * @return {boolean} 是否使用web worker布局 */ LayoutController.prototype.layout = function (success) { var _this = this; var graph = this.graph; this.data = this.setDataFromGraph(); var _a = this.data, nodes = _a.nodes, hiddenNodes = _a.hiddenNodes; if (!nodes) { return false; } var width = graph.get('width'); var height = graph.get('height'); var layoutCfg = {}; Object.assign(layoutCfg, { width: width, height: height, center: [width / 2, height / 2] }, this.layoutCfg); this.layoutCfg = layoutCfg; this.destoryLayoutMethods(); graph.emit('beforelayout'); this.initPositions(layoutCfg.center, nodes); // init hidden ndoes this.initPositions(layoutCfg.center, hiddenNodes); // 在 onAllLayoutEnd 中执行用户自定义 onLayoutEnd,触发 afterlayout、更新节点位置、fitView/fitCenter、触发 afterrender var onLayoutEnd = layoutCfg.onLayoutEnd, layoutEndFormatted = layoutCfg.layoutEndFormatted, adjust = layoutCfg.adjust; if (!layoutEndFormatted) { layoutCfg.layoutEndFormatted = true; layoutCfg.onAllLayoutEnd = function () { return __awaiter(_this, void 0, void 0, function () { return __generator(this, function (_a) { switch (_a.label) { case 0: // 执行用户自定义 onLayoutEnd if (onLayoutEnd) { onLayoutEnd(); } // 更新节点位置 this.refreshLayout(); if (!(adjust && layoutCfg.pipes)) return [3 /*break*/, 2]; return [4 /*yield*/, this.adjustPipesBox(this.data, adjust)]; case 1: _a.sent(); this.refreshLayout(); _a.label = 2; case 2: // 触发 afterlayout graph.emit('afterlayout'); return [2 /*return*/]; } }); }); }; } var start = Promise.resolve(); if (layoutCfg.type) { start = start.then(function () { return _this.execLayoutMethod(layoutCfg, 0); }); } else if (layoutCfg.pipes) { layoutCfg.pipes.forEach(function (cfg, index) { start = start.then(function () { return _this.execLayoutMethod(cfg, index); }); }); } // 最后统一在外部调用onAllLayoutEnd start.then(function () { if (layoutCfg.onAllLayoutEnd) layoutCfg.onAllLayoutEnd(); // 在执行 execute 后立即执行 success,且在 timeBar 中有 throttle,可以防止 timeBar 监听 afterrender 进行 changeData 后 layout,从而死循环 // 对于 force 一类布局完成后的 fitView 需要用户自己在 onLayoutEnd 中配置 if (success) success(); }).catch(function (error) { console.warn('graph layout failed,', error); }); return false; }; LayoutController.prototype.execLayoutMethod = function (layoutCfg, order) { var _this = this; return new Promise(function (reslove, reject) { var graph = _this.graph; var layoutType = layoutCfg.type; // 每个布局方法都需要注册 layoutCfg.onLayoutEnd = function () { graph.emit('aftersublayout', { type: layoutType }); reslove(); }; var isForce = layoutType === 'force' || layoutType === 'g6force' || layoutType === 'gForce'; if (isForce) { var onTick_1 = layoutCfg.onTick; var tick = function tick() { if (onTick_1) { onTick_1(); } graph.refreshPositions(); }; layoutCfg.tick = tick; } else if (layoutCfg.type === 'comboForce') { layoutCfg.comboTrees = graph.get('comboTrees'); } var enableTick = false; var layoutMethod; try { layoutMethod = new Layout(layoutCfg); } catch (e) { console.warn("The layout method: '".concat(layoutType, "' does not exist! Please specify it first.")); reject(); } // 是否需要迭代的方式完成布局。这里是来自布局对象的实例属性,是由布局的定义者在布局类定义的。 enableTick = layoutMethod.enableTick; if (enableTick) { var onTick_2 = layoutCfg.onTick; var tick = function tick() { if (onTick_2) { onTick_2(); } graph.refreshPositions(); }; layoutMethod.tick = tick; } var layoutData = _this.filterLayoutData(_this.data, layoutCfg); addLayoutOrder(layoutData, order); layoutMethod.init(layoutData); // 若存在节点没有位置信息,且没有设置 layout,在 initPositions 中 random 给出了所有节点的位置,不需要再次执行 random 布局 // 所有节点都有位置信息,且指定了 layout,则执行布局(代表不是第一次进行布局) graph.emit('beforesublayout', { type: layoutType }); layoutMethod.execute(); if (layoutMethod.isCustomLayout && layoutCfg.onLayoutEnd) layoutCfg.onLayoutEnd(); _this.layoutMethods.push(layoutMethod); }); }; LayoutController.prototype.updateLayoutMethod = function (layoutMethod, layoutCfg) { var _this = this; return new Promise(function (reslove, reject) { var graph = _this.graph; var layoutType = layoutCfg === null || layoutCfg === void 0 ? void 0 : layoutCfg.type; // 每个布局方法都需要注册 layoutCfg.onLayoutEnd = function () { graph.emit('aftersublayout', { type: layoutType }); reslove(); }; var layoutData = _this.filterLayoutData(_this.data, layoutCfg); layoutMethod.init(layoutData); layoutMethod.updateCfg(layoutCfg); graph.emit('beforesublayout', { type: layoutType }); layoutMethod.execute(); if (layoutMethod.isCustomLayout && layoutCfg.onLayoutEnd) layoutCfg.onLayoutEnd(); }); }; LayoutController.prototype.adjustPipesBox = function (data, adjust) { var _this = this; return new Promise(function (resolve) { var nodes = data.nodes; if (!(nodes === null || nodes === void 0 ? void 0 : nodes.length)) { resolve(); } if (!LayoutPipesAdjustNames.includes(adjust)) { console.warn("The adjust type ".concat(adjust, " is not supported yet, please assign it with 'force', 'grid', or 'circular'.")); resolve(); } var layoutCfg = { center: _this.layoutCfg.center, nodeSize: function nodeSize(d) { return Math.max(d.height, d.width); }, preventOverlap: true, onLayoutEnd: function onLayoutEnd() {} }; // 计算出大单元 var _a = _this.getLayoutBBox(nodes), groupNodes = _a.groupNodes, layoutNodes = _a.layoutNodes; var preNodes = clone(layoutNodes); // 根据大单元坐标的变化,调整这里面每个小单元nodes layoutCfg.onLayoutEnd = function () { layoutNodes === null || layoutNodes === void 0 ? void 0 : layoutNodes.forEach(function (ele, index) { var _a, _b, _c; var dx = ele.x - ((_a = preNodes[index]) === null || _a === void 0 ? void 0 : _a.x); var dy = ele.y - ((_b = preNodes[index]) === null || _b === void 0 ? void 0 : _b.y); (_c = groupNodes[index]) === null || _c === void 0 ? void 0 : _c.forEach(function (n) { n.x += dx; n.y += dy; }); }); resolve(); }; var layoutMethod = new Layout(layoutCfg); layoutMethod.layout({ nodes: layoutNodes }); }); }; LayoutController.prototype.destroy = function () { this.destoryLayoutMethods(); this.destroyed = true; this.graph.set('layout', undefined); this.layoutCfg = undefined; this.layoutType = undefined; this.layoutMethods = undefined; this.graph = null; }; return LayoutController; }(AbstractLayout); export default LayoutController; function addLayoutOrder(data, order) { var _a; if (!((_a = data === null || data === void 0 ? void 0 : data.nodes) === null || _a === void 0 ? void 0 : _a.length)) { return; } var nodes = data.nodes; nodes.forEach(function (node) { node.layoutOrder = order; }); }