@antv/f6
Version:
A Mobile Graph Visualization Framework in JavaScript
284 lines (282 loc) • 10.4 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
var _tslib = require("tslib");
var _f6Core = require("@antv/f6-core");
var _layout = require("../../layout");
var _util = require("@antv/util");
var LayoutPipesAdjustNames = ['force', 'grid', 'circular'];
var LayoutController = /** @class */function (_super) {
(0, _tslib.__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 = (0, _util.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 (0, _tslib.__awaiter)(_this, void 0, void 0, function () {
return (0, _tslib.__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.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 = (0, _util.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.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;
}(_f6Core.AbstractLayout);
var _default = LayoutController;
exports.default = _default;
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;
});
}