| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523 |
- define(function(require, exports, module) {
- var kity = require('./kity');
- var utils = require('./utils');
- var Minder = require('./minder');
- var MinderNode = require('./node');
- var MinderEvent = require('./event');
- var Command = require('./command');
- var _layouts = {};
- var _defaultLayout;
- function register(name, layout) {
- _layouts[name] = layout;
- _defaultLayout = _defaultLayout || name;
- }
- /**
- * @class Layout 布局基类,具体布局需要从该类派生
- */
- var Layout = kity.createClass('Layout', {
- /**
- * @abstract
- *
- * 子类需要实现的布局算法,该算法输入一个节点,排布该节点的子节点(相对父节点的变换)
- *
- * @param {MinderNode} node 需要布局的节点
- *
- * @example
- *
- * doLayout: function(node) {
- * var children = node.getChildren();
- * // layout calculation
- * children[i].setLayoutTransform(new kity.Matrix().translate(x, y));
- * }
- */
- doLayout: function(parent, children) {
- throw new Error('Not Implement: Layout.doLayout()');
- },
- /**
- * 对齐指定的节点
- *
- * @param {Array<MinderNode>} nodes 要对齐的节点
- * @param {string} border 对齐边界,允许取值 left, right, top, bottom
- *
- */
- align: function(nodes, border, offset) {
- var me = this;
- offset = offset || 0;
- nodes.forEach(function(node) {
- var tbox = me.getTreeBox([node]);
- var matrix = node.getLayoutTransform();
- switch (border) {
- case 'left':
- return matrix.translate(offset - tbox.left, 0);
- case 'right':
- return matrix.translate(offset - tbox.right, 0);
- case 'top':
- return matrix.translate(0, offset - tbox.top);
- case 'bottom':
- return matrix.translate(0, offset - tbox.bottom);
- }
- });
- },
- stack: function(nodes, axis, distance) {
- var me = this;
- var position = 0;
- distance = distance || function(node, next, axis) {
- return node.getStyle({
- x: 'margin-right',
- y: 'margin-bottom'
- }[axis]) + next.getStyle({
- x: 'margin-left',
- y: 'margin-top'
- }[axis]);
- };
- nodes.forEach(function(node, index, nodes) {
- var tbox = me.getTreeBox([node]);
- var size = {
- x: tbox.width,
- y: tbox.height
- }[axis];
- var offset = {
- x: tbox.left,
- y: tbox.top
- }[axis];
- var matrix = node.getLayoutTransform();
- if (axis == 'x') {
- matrix.translate(position - offset, 0);
- } else {
- matrix.translate(0, position - offset);
- }
- position += size;
- if (nodes[index + 1])
- position += distance(node, nodes[index + 1], axis);
- });
- return position;
- },
- move: function(nodes, dx, dy) {
- nodes.forEach(function(node) {
- node.getLayoutTransform().translate(dx, dy);
- });
- },
- /**
- * 工具方法:获取给点的节点所占的布局区域
- *
- * @param {MinderNode[]} nodes 需要计算的节点
- *
- * @return {Box} 计算结果
- */
- getBranchBox: function(nodes) {
- var box = new kity.Box();
- var i, node, matrix, contentBox;
- for (i = 0; i < nodes.length; i++) {
- node = nodes[i];
- matrix = node.getLayoutTransform();
- contentBox = node.getContentBox();
- box = box.merge(matrix.transformBox(contentBox));
- }
- return box;
- },
- /**
- * 工具方法:计算给定的节点的子树所占的布局区域
- *
- * @param {MinderNode} nodes 需要计算的节点
- *
- * @return {Box} 计算的结果
- */
- getTreeBox: function(nodes) {
- var i, node, matrix, treeBox;
- var box = new kity.Box();
- if (!(nodes instanceof Array)) nodes = [nodes];
- for (i = 0; i < nodes.length; i++) {
- node = nodes[i];
- matrix = node.getLayoutTransform();
- treeBox = node.getContentBox();
- if (node.isExpanded() && node.children.length) {
- treeBox = treeBox.merge(this.getTreeBox(node.children));
- }
- box = box.merge(matrix.transformBox(treeBox));
- }
- return box;
- },
- getOrderHint: function(node) {
- return [];
- }
- });
- Layout.register = register;
- Minder.registerInitHook(function(options) {
- this.refresh();
- });
- /**
- * 布局支持池子管理
- */
- utils.extend(Minder, {
- getLayoutList: function() {
- return _layouts;
- },
- getLayoutInstance: function(name) {
- var LayoutClass = _layouts[name];
- if (!LayoutClass) throw new Error('Missing Layout: ' + name);
- var layout = new LayoutClass();
- return layout;
- }
- });
- /**
- * MinderNode 上的布局支持
- */
- kity.extendClass(MinderNode, {
- /**
- * 获得当前节点的布局名称
- *
- * @return {String}
- */
- getLayout: function() {
- var layout = this.getData('layout');
- layout = layout || (this.isRoot() ? _defaultLayout : this.parent.getLayout());
- return layout;
- },
- setLayout: function(name) {
- if (name) {
- if (name == 'inherit') {
- this.setData('layout');
- } else {
- this.setData('layout', name);
- }
- }
- return this;
- },
- layout: function(name) {
- this.setLayout(name).getMinder().layout();
- return this;
- },
- getLayoutInstance: function() {
- return Minder.getLayoutInstance(this.getLayout());
- },
- getOrderHint: function(refer) {
- return this.parent.getLayoutInstance().getOrderHint(this);
- },
- /**
- * 获取当前节点相对于父节点的布局变换
- */
- getLayoutTransform: function() {
- return this._layoutTransform || new kity.Matrix();
- },
- /**
- * 第一轮布局计算后,获得的全局布局位置
- *
- * @return {[type]} [description]
- */
- getGlobalLayoutTransformPreview: function() {
- var pMatrix = this.parent ? this.parent.getLayoutTransform() : new kity.Matrix();
- var matrix = this.getLayoutTransform();
- var offset = this.getLayoutOffset();
- if (offset) {
- matrix = matrix.clone().translate(offset.x, offset.y);
- }
- return pMatrix.merge(matrix);
- },
- getLayoutPointPreview: function() {
- return this.getGlobalLayoutTransformPreview().transformPoint(new kity.Point());
- },
- /**
- * 获取节点相对于全局的布局变换
- */
- getGlobalLayoutTransform: function() {
- if (this._globalLayoutTransform) {
- return this._globalLayoutTransform;
- } else if (this.parent) {
- return this.parent.getGlobalLayoutTransform();
- } else {
- return new kity.Matrix();
- }
- },
- /**
- * 设置当前节点相对于父节点的布局变换
- */
- setLayoutTransform: function(matrix) {
- this._layoutTransform = matrix;
- return this;
- },
- /**
- * 设置当前节点相对于全局的布局变换(冗余优化)
- */
- setGlobalLayoutTransform: function(matrix) {
- this.getRenderContainer().setMatrix(this._globalLayoutTransform = matrix);
- return this;
- },
- setVertexIn: function(p) {
- this._vertexIn = p;
- },
- setVertexOut: function(p) {
- this._vertexOut = p;
- },
- getVertexIn: function() {
- return this._vertexIn || new kity.Point();
- },
- getVertexOut: function() {
- return this._vertexOut || new kity.Point();
- },
- getLayoutVertexIn: function() {
- return this.getGlobalLayoutTransform().transformPoint(this.getVertexIn());
- },
- getLayoutVertexOut: function() {
- return this.getGlobalLayoutTransform().transformPoint(this.getVertexOut());
- },
- setLayoutVectorIn: function(v) {
- this._layoutVectorIn = v;
- return this;
- },
- setLayoutVectorOut: function(v) {
- this._layoutVectorOut = v;
- return this;
- },
- getLayoutVectorIn: function() {
- return this._layoutVectorIn || new kity.Vector();
- },
- getLayoutVectorOut: function() {
- return this._layoutVectorOut || new kity.Vector();
- },
- getLayoutBox: function() {
- var matrix = this.getGlobalLayoutTransform();
- return matrix.transformBox(this.getContentBox());
- },
- getLayoutPoint: function() {
- var matrix = this.getGlobalLayoutTransform();
- return matrix.transformPoint(new kity.Point());
- },
- getLayoutOffset: function() {
- if (!this.parent) return new kity.Point();
- // 影响当前节点位置的是父节点的布局
- var data = this.getData('layout_' + this.parent.getLayout() + '_offset');
- if (data) return new kity.Point(data.x, data.y);
- return new kity.Point();
- },
- setLayoutOffset: function(p) {
- if (!this.parent) return this;
- this.setData('layout_' + this.parent.getLayout() + '_offset', p ? {
- x: p.x,
- y: p.y
- } : undefined);
- return this;
- },
- hasLayoutOffset: function() {
- return !!this.getData('layout_' + this.parent.getLayout() + '_offset');
- },
- resetLayoutOffset: function() {
- return this.setLayoutOffset(null);
- },
- getLayoutRoot: function() {
- if (this.isLayoutRoot()) {
- return this;
- }
- return this.parent.getLayoutRoot();
- },
- isLayoutRoot: function() {
- return this.getData('layout') || this.isRoot();
- }
- });
- /**
- * Minder 上的布局支持
- */
- kity.extendClass(Minder, {
- layout: function() {
- var duration = this.getOption('layoutAnimationDuration');
- this.getRoot().traverse(function(node) {
- // clear last results
- node.setLayoutTransform(null);
- });
- function layoutNode(node, round) {
- // layout all children first
- // 剪枝:收起的节点无需计算
- if (node.isExpanded() || true) {
- node.children.forEach(function(child) {
- layoutNode(child, round);
- });
- }
- var layout = node.getLayoutInstance();
- // var childrenInFlow = node.getChildren().filter(function(child) {
- // return !child.hasLayoutOffset();
- // });
- layout.doLayout(node, node.getChildren(), round);
- }
- // 第一轮布局
- layoutNode(this.getRoot(), 1);
- // 第二轮布局
- layoutNode(this.getRoot(), 2);
- var minder = this;
- this.applyLayoutResult(this.getRoot(), duration, function() {
- /**
- * 当节点>200, 不使用动画时, 此处逻辑变为同步逻辑, 外部minder.on事件无法
- * 被提前录入, 因此增加setTimeout
- * @author Naixor
- */
- setTimeout(function () {
- minder.fire('layoutallfinish');
- }, 0);
- });
- return this.fire('layout');
- },
- refresh: function() {
- this.getRoot().renderTree();
- this.layout().fire('contentchange')._interactChange();
- return this;
- },
- applyLayoutResult: function(root, duration, callback) {
- root = root || this.getRoot();
- var me = this;
- var complex = root.getComplex();
- function consume() {
- if (!--complex) {
- if (callback) {
- callback();
- }
- }
- }
- // 节点复杂度大于 100,关闭动画
- if (complex > 200) duration = 0;
- function applyMatrix(node, matrix) {
- node.setGlobalLayoutTransform(matrix);
- me.fire('layoutapply', {
- node: node,
- matrix: matrix
- });
- }
- function apply(node, pMatrix) {
- var matrix = node.getLayoutTransform().merge(pMatrix.clone());
- var lastMatrix = node.getGlobalLayoutTransform() || new kity.Matrix();
- var offset = node.getLayoutOffset();
- matrix.translate(offset.x, offset.y);
- matrix.m.e = Math.round(matrix.m.e);
- matrix.m.f = Math.round(matrix.m.f);
- // 如果当前有动画,停止动画
- if (node._layoutTimeline) {
- node._layoutTimeline.stop();
- node._layoutTimeline = null;
- }
- // 如果要求以动画形式来更新,创建动画
- if (duration) {
- node._layoutTimeline = new kity.Animator(lastMatrix, matrix, applyMatrix)
- .start(node, duration, 'ease')
- .on('finish', function() {
- //可能性能低的时候会丢帧,手动添加一帧
- setTimeout(function() {
- applyMatrix(node, matrix);
- me.fire('layoutfinish', {
- node: node,
- matrix: matrix
- });
- consume();
- }, 150);
- });
- }
- // 否则直接更新
- else {
- applyMatrix(node, matrix);
- me.fire('layoutfinish', {
- node: node,
- matrix: matrix
- });
- consume();
- }
- for (var i = 0; i < node.children.length; i++) {
- apply(node.children[i], matrix);
- }
- }
- apply(root, root.parent ? root.parent.getGlobalLayoutTransform() : new kity.Matrix());
- return this;
- }
- });
- module.exports = Layout;
- });
|