diff options
Diffstat (limited to 'src/js/util/mxMorphing.js')
-rw-r--r-- | src/js/util/mxMorphing.js | 239 |
1 files changed, 239 insertions, 0 deletions
diff --git a/src/js/util/mxMorphing.js b/src/js/util/mxMorphing.js new file mode 100644 index 0000000..442143d --- /dev/null +++ b/src/js/util/mxMorphing.js @@ -0,0 +1,239 @@ +/** + * $Id: mxMorphing.js,v 1.4 2010-06-03 13:37:07 gaudenz Exp $ + * Copyright (c) 2006-2010, JGraph Ltd + */ +/** + * + * Class: mxMorphing + * + * Implements animation for morphing cells. Here is an example of + * using this class for animating the result of a layout algorithm: + * + * (code) + * graph.getModel().beginUpdate(); + * try + * { + * var circleLayout = new mxCircleLayout(graph); + * circleLayout.execute(graph.getDefaultParent()); + * } + * finally + * { + * var morph = new mxMorphing(graph); + * morph.addListener(mxEvent.DONE, function() + * { + * graph.getModel().endUpdate(); + * }); + * + * morph.startAnimation(); + * } + * (end) + * + * Constructor: mxMorphing + * + * Constructs an animation. + * + * Parameters: + * + * graph - Reference to the enclosing <mxGraph>. + * steps - Optional number of steps in the morphing animation. Default is 6. + * ease - Optional easing constant for the animation. Default is 1.5. + * delay - Optional delay between the animation steps. Passed to <mxAnimation>. + */ +function mxMorphing(graph, steps, ease, delay) +{ + mxAnimation.call(this, delay); + this.graph = graph; + this.steps = (steps != null) ? steps : 6; + this.ease = (ease != null) ? ease : 1.5; +}; + +/** + * Extends mxEventSource. + */ +mxMorphing.prototype = new mxAnimation(); +mxMorphing.prototype.constructor = mxMorphing; + +/** + * Variable: graph + * + * Specifies the delay between the animation steps. Defaul is 30ms. + */ +mxMorphing.prototype.graph = null; + +/** + * Variable: steps + * + * Specifies the maximum number of steps for the morphing. + */ +mxMorphing.prototype.steps = null; + +/** + * Variable: step + * + * Contains the current step. + */ +mxMorphing.prototype.step = 0; + +/** + * Variable: ease + * + * Ease-off for movement towards the given vector. Larger values are + * slower and smoother. Default is 4. + */ +mxMorphing.prototype.ease = null; + +/** + * Variable: cells + * + * Optional array of cells to be animated. If this is not specified + * then all cells are checked and animated if they have been moved + * in the current transaction. + */ +mxMorphing.prototype.cells = null; + +/** + * Function: updateAnimation + * + * Animation step. + */ +mxMorphing.prototype.updateAnimation = function() +{ + var move = new mxCellStatePreview(this.graph); + + if (this.cells != null) + { + // Animates the given cells individually without recursion + for (var i = 0; i < this.cells.length; i++) + { + this.animateCell(cells[i], move, false); + } + } + else + { + // Animates all changed cells by using recursion to find + // the changed cells but not for the animation itself + this.animateCell(this.graph.getModel().getRoot(), move, true); + } + + this.show(move); + + if (move.isEmpty() || + this.step++ >= this.steps) + { + this.stopAnimation(); + } +}; + +/** + * Function: show + * + * Shows the changes in the given <mxCellStatePreview>. + */ +mxMorphing.prototype.show = function(move) +{ + move.show(); +}; + +/** + * Function: animateCell + * + * Animates the given cell state using <mxCellStatePreview.moveState>. + */ +mxMorphing.prototype.animateCell = function(cell, move, recurse) +{ + var state = this.graph.getView().getState(cell); + var delta = null; + + if (state != null) + { + // Moves the animated state from where it will be after the model + // change by subtracting the given delta vector from that location + delta = this.getDelta(state); + + if (this.graph.getModel().isVertex(cell) && + (delta.x != 0 || delta.y != 0)) + { + var translate = this.graph.view.getTranslate(); + var scale = this.graph.view.getScale(); + + delta.x += translate.x * scale; + delta.y += translate.y * scale; + + move.moveState(state, -delta.x / this.ease, -delta.y / this.ease); + } + } + + if (recurse && !this.stopRecursion(state, delta)) + { + var childCount = this.graph.getModel().getChildCount(cell); + + for (var i = 0; i < childCount; i++) + { + this.animateCell(this.graph.getModel().getChildAt(cell, i), move, recurse); + } + } +}; + +/** + * Function: stopRecursion + * + * Returns true if the animation should not recursively find more + * deltas for children if the given parent state has been animated. + */ +mxMorphing.prototype.stopRecursion = function(state, delta) +{ + return delta != null && (delta.x != 0 || delta.y != 0); +}; + +/** + * Function: getDelta + * + * Returns the vector between the current rendered state and the future + * location of the state after the display will be updated. + */ +mxMorphing.prototype.getDelta = function(state) +{ + var origin = this.getOriginForCell(state.cell); + var translate = this.graph.getView().getTranslate(); + var scale = this.graph.getView().getScale(); + var current = new mxPoint( + state.x / scale - translate.x, + state.y / scale - translate.y); + + return new mxPoint( + (origin.x - current.x) * scale, + (origin.y - current.y) * scale); +}; + +/** + * Function: getOriginForCell + * + * Returns the top, left corner of the given cell. TODO: Improve performance + * by using caching inside this method as the result per cell never changes + * during the lifecycle of this object. + */ +mxMorphing.prototype.getOriginForCell = function(cell) +{ + var result = null; + + if (cell != null) + { + result = this.getOriginForCell(this.graph.getModel().getParent(cell)); + var geo = this.graph.getCellGeometry(cell); + + // TODO: Handle offset, relative geometries etc + if (geo != null) + { + result.x += geo.x; + result.y += geo.y; + } + } + + if (result == null) + { + var t = this.graph.view.getTranslate(); + result = new mxPoint(-t.x, -t.y); + } + + return result; +}; |