Browse Source

- Added animation and camera controls by the method .moveTo()

- Added new event that fires when the animation is finished.
- Added new example showing the new features of animation.
v3_develop
Alex de Mulder 10 years ago
parent
commit
eec53d0470
8 changed files with 4912 additions and 4363 deletions
  1. +3
    -0
      HISTORY.md
  2. +4320
    -4328
      dist/vis.js
  3. +1
    -1
      dist/vis.min.css
  4. +53
    -6
      docs/network.html
  5. +314
    -0
      examples/network/33_animation.html
  6. +2
    -0
      examples/network/index.html
  7. +218
    -27
      lib/network/Network.js
  8. +1
    -1
      lib/network/mixins/HierarchicalLayoutMixin.js

+ 3
- 0
HISTORY.md View File

@ -15,6 +15,9 @@ http://visjs.org
- Implemented support for broken image fallback. Thanks @sfairgrieve.
- Added multiline labels to edges as they are implemented in nodes. Updated
multiline example to show this.
- Added animation and camera controls by the method .moveTo()
- Added new event that fires when the animation is finished.
- Added new example showing the new features of animation.
### Timeline

+ 4320
- 4328
dist/vis.js
File diff suppressed because it is too large
View File


+ 1
- 1
dist/vis.min.css
File diff suppressed because it is too large
View File


+ 53
- 6
docs/network.html View File

@ -2146,10 +2146,20 @@ var options: {
</td>
</tr>
<tr>
<td>focusOnNode(nodeId, [zoomLevel])</td>
<td>focusOnNode(nodeId, [options])</td>
<td>none</td>
<td>This function will move the view to center on the specified node. An optional zoomLevel can be passed where 1.0 is 100&#37;, between 0.0 and 1.0 is zooming out and > 1.0 is zooming in. Generally, close to 1.0 is sufficient.
If this argument is not passed the view will only move, not zoom.
<td>This function will move the view to center on the specified node. An optional options object can submitted where you can define the animation properties. <br />
The options that can be defined are:<br />
<b><code>scale:Number</code></b><br /> - to zoom to that scale,<br />
<b><code>offset:{x:Number, y:Number}</code></b><br /> - to offset the position from the center of the canvas (in DOM units),<br />
<b><code>animation: Object || Boolean</code></b><br /> - to define the specifics of the animation. True is animated with default settings, false is not animated.<br />
<br />
The animation object can consist of:<br />
<b><code>duration: Number</code></b><br /> - the duration of the animation in milliseconds,<br />
<b><code>easingFunction: String</code></b><br /> - the easing function of the animation, available are:<br />
<code>linear, easeInQuad, easeOutQuad, easeInOutQuad, easeInCubic, easeOutCubic, easeInOutCubic,
easeInQuart, easeOutQuart, easeInOutQuart,
easeInQuint, easeOutQuint, easeInOutQuint </code> <br /><br />
</td>
</tr>
<tr>
@ -2171,7 +2181,27 @@ var options: {
<td>This function converts canvas coordinates to coordinates on the DOM. Input and output are in the form of {x:xpos,y:ypos}. The DOM values are relative to the network container.
</td>
</tr>
<tr>
<tr>
<td>moveTo(options)</td>
<td>object</td>
<td>This function allows you to programmatically move the view. The options that can be defined are:<br />
<b><code>position:{x:Number, y:Number}</code></b><br /> - to move to that position (in canvas units), <br />
<b><code>scale:Number</code></b><br /> - to zoom to that scale,<br />
<b><code>offset:{x:Number, y:Number}</code></b><br /> - to offset the position from the center of the canvas (in DOM units),<br />
<b><code>animation: Object || Boolean</code></b><br /> - to define the specifics of the animation.<br />
<br />
The animation object can consist of:<br />
<b><code>duration: Number</code></b><br /> - the duration of the animation in milliseconds,<br />
<b><code>easingFunction: String</code></b><br /> - the easing function of the animation, available are:<br />
<code>linear, easeInQuad, easeOutQuad, easeInOutQuad, easeInCubic, easeOutCubic, easeInOutCubic,
easeInQuart, easeOutQuart, easeInOutQuart,
easeInQuint, easeOutQuint, easeInOutQuint </code> <br /><br />
<i>You will have to define at least a scale or a position. Otherwise, there is nothing to move to.</i>
</td>
</tr>
<tr>
<td>on(event, callback)</td>
<td>none</td>
<td>Create an event listener. The callback function is invoked every time the event is triggered. Avialable events: <code>select</code>. The callback function is invoked as <code>callback(properties)</code>, where <code>properties</code> is an object containing event specific properties. See section <a href="#Events">Events</a> for more information.</td>
@ -2248,9 +2278,18 @@ var options: {
</tr>
<tr>
<td>zoomExtent()</td>
<td>zoomExtent([options])</td>
<td>none</td>
<td>Scales the network so all the nodes are in center view.</td>
<td>Scales the network so all the nodes are in center view. Optionally you can supply options for animation. These
options can just be a boolean. When true, the zoom is animated, when false there is no animation.
Alternatively, you can supply an object.
<br /><br /> The object can consist of:<br />
<b><code>duration: Number</code></b><br /> - the duration of the animation in milliseconds,<br />
<b><code>easingFunction: String</code></b><br /> - the easing function of the animation, available are:<br />
<code>linear, easeInQuad, easeOutQuad, easeInOutQuad, easeInCubic, easeOutCubic, easeInOutCubic,
easeInQuart, easeOutQuart, easeInOutQuart,
easeInQuint, easeOutQuint, easeInOutQuint </code>
</td>
</tr>
</table>
@ -2301,6 +2340,14 @@ network.off('select', onSelect);
<th>Properties</th>
</tr>
<tr>
<td>animationFinished</td>
<td>Fired after an animation is finished.
</td>
<td>
none
</td>
</tr>
<tr>
<td>select</td>
<td>Fired after the user selects or deselects a node by clicking it.

+ 314
- 0
examples/network/33_animation.html View File

@ -0,0 +1,314 @@
<!doctype html>
<html>
<head>
<title>Network | Animation</title>
<style type="text/css">
body {
font: 10pt sans;
}
#mynetwork {
width: 600px;
height: 600px;
border: 1px solid lightgray;
}
div.left {
position:relative;
float:left;
width:300px;
border: 1px #c7c7c7 solid;
height:590px;
padding:5px;
}
div.right {
padding-left:10px;
float:left;
width:600px;
}
div.bottom {
position:absolute;
bottom:5px;
}
</style>
<script type="text/javascript" src="../../dist/vis.js"></script>
<link href="../../dist/vis.css" rel="stylesheet" type="text/css" />
<script type="text/javascript">
var nodes = null;
var edges = null;
var network = null;
var doButton, focusButton, showButton;
var statusUpdateSpan;
var finishMessage = "";
function draw() {
statusUpdateSpan = document.getElementById("statusUpdate");
doButton = document.getElementById("btnDo");
focusButton = document.getElementById("btnFocus");
showButton = document.getElementById("btnShow");
nodes = [];
edges = [];
var connectionCount = [];
// randomly create some nodes and edges
var nodeCount = 25;
for (var i = 0; i < nodeCount; i++) {
nodes.push({
id: i,
label: String(i)
});
connectionCount[i] = 0;
// create edges in a scale-free-network way
if (i == 1) {
var from = i;
var to = 0;
edges.push({
from: from,
to: to
});
connectionCount[from]++;
connectionCount[to]++;
}
else if (i > 1) {
var conn = edges.length * 2;
var rand = Math.floor(Math.random() * conn);
var cum = 0;
var j = 0;
while (j < connectionCount.length && cum < rand) {
cum += connectionCount[j];
j++;
}
var from = i;
var to = j;
edges.push({
from: from,
to: to
});
connectionCount[from]++;
connectionCount[to]++;
}
}
// create a network
var container = document.getElementById('mynetwork');
var data = {
nodes: nodes,
edges: edges
};
var options = {};
network = new vis.Network(container, data, options);
// add event listeners
network.on('select', function(params) {
document.getElementById('selection').innerHTML = 'Selection: ' + params.nodes;
});
network.on('stabilized', function (params) {
document.getElementById('stabilization').innerHTML = 'Stabilization took ' + params.iterations + ' iterations.';
});
network.on("animationFinished", function() {
statusUpdateSpan.innerHTML = finishMessage;
})
}
function zoomExtentAnimated() {
var offsetx = parseInt(document.getElementById("offsetx").value);
var offsety = parseInt(document.getElementById("offsety").value);
var duration = parseInt(document.getElementById("duration").value);
var easingFunction = document.getElementById("easingFunction").value;
var options = {offset: {x:offsetx,y:offsety},
duration: duration,
easingFunction: easingFunction
}
statusUpdateSpan.innerHTML = "Doing ZoomExtent() Animation.";
finishMessage = "Animation finished."
network.zoomExtent(options);
}
function doDefaultAnimation() {
var offsetx = parseInt(document.getElementById("offsetx").value);
var offsety = parseInt(document.getElementById("offsety").value);
var scale = parseFloat(document.getElementById("scale").value);
var positionx = parseInt(document.getElementById("positionx").value);
var positiony = parseInt(document.getElementById("positiony").value);
var easingFunction = document.getElementById("easingFunction").value;
var options = {
position: {x:positionx,y:positiony},
scale: scale,
offset: {x:offsetx,y:offsety},
animation: true // default duration is 1000ms and default easingFunction is easeInOutQuad.
}
statusUpdateSpan.innerHTML = "Doing Animation.";
finishMessage = "Animation finished."
network.moveTo(options);
}
function doAnimation() {
var offsetx = parseInt(document.getElementById("offsetx").value);
var offsety = parseInt(document.getElementById("offsety").value);
var duration = parseInt(document.getElementById("duration").value);
var scale = parseFloat(document.getElementById("scale").value);
var positionx = parseInt(document.getElementById("positionx").value);
var positiony = parseInt(document.getElementById("positiony").value);
var easingFunction = document.getElementById("easingFunction").value;
var options = {
position: {x:positionx,y:positiony},
scale: scale,
offset: {x:offsetx,y:offsety},
animation: {
duration: duration,
easingFunction: easingFunction
}
}
statusUpdateSpan.innerHTML = "Doing Animation.";
finishMessage = "Animation finished."
network.moveTo(options);
}
function focusRandom() {
var nodeId = Math.floor(Math.random() * 25);
var offsetx = parseInt(document.getElementById("offsetx").value);
var offsety = parseInt(document.getElementById("offsety").value);
var duration = parseInt(document.getElementById("duration").value);
var scale = parseFloat(document.getElementById("scale").value);
var easingFunction = document.getElementById("easingFunction").value;
var options = {
// position: {x:positionx,y:positiony}, // this is not relevant when focusing on nodes
scale: scale,
offset: {x:offsetx,y:offsety},
animation: {
duration: duration,
easingFunction: easingFunction
}
}
statusUpdateSpan.innerHTML = "Focusing on node: " + nodeId;
finishMessage = "Node: " + nodeId + " in focus.";
network.focusOnNode(nodeId, options);
}
var showInterval = false;
var showPhase = 1;
function startShow() {
if (showInterval !== false) {
showInterval = false;
showButton.value = "Start a show!";
network.zoomExtent();
}
else {
showButton.value = "Stop the show!";
var duration = parseInt(document.getElementById("duration").value);
focusRandom();
window.setTimeout(doTheShow, duration);
showInterval = true;
}
}
function doTheShow() {
if (showInterval == true) {
var duration = parseInt(document.getElementById("duration").value);
if (showPhase == 0) {
focusRandom();
showPhase = 1;
}
else {
zoomExtentAnimated();
showPhase = 0;
}
window.setTimeout(doTheShow, duration);
}
}
</script>
</head>
<body onload="draw();">
<h2>Camera animations</h2>
<div style="width:700px; font-size:14px;">
You can move the view around programmatically using the .moveTo(options) function. The options supplied to this function can
also be (partially) supplied to the zoomExtent() function
</div>
<pre>
var moveToOptions = {
position: {x:x, y:x}, // position to animate to (Numbers)
scale: 1.0, // scale to animate to (Number)
offset: {x:x, y:y}, // offset from the center in DOM pixels (Numbers)
animation: { // animation object, can also be Boolean
duration: 1000, // animation duration in milliseconds (Number)
easingFunction: "easeInOutQuad" // Animation easing function, available are:
} // linear, easeInQuad, easeOutQuad, easeInOutQuad,
} // easeInCubic, easeOutCubic, easeInOutCubic,
// easeInQuart, easeOutQuart, easeInOutQuart,
// easeInQuint, easeOutQuint, easeInOutQuint
</pre>
<div class="left">
<table>
<tr>
<td>position x</td><td><input type="text" value="300" id="positionx" style="width:170px;"></td>
</tr>
<tr>
<td>position y</td><td><input type="text" value="300" id="positiony" style="width:170px;"></td>
</tr>
<tr>
<td>scale</td><td><input type="text" value="1.0" id="scale" style="width:170px;"></td>
</tr>
<tr>
<td>offset x</td><td><input type="text" value="0" id="offsetx" style="width:170px;"> px</td>
</tr>
<tr>
<td>offset y</td><td><input type="text" value="0" id="offsety" style="width:170px;"> px</td>
</tr>
<tr>
<td>duration</td><td><input type="text" value="2500" id="duration" style="width:170px;"> ms</td>
</tr>
<tr>
<td>easingFunction</td><td>
<select id="easingFunction" style="width:174px;">
<option value="linear">linear</option>
<option value="easeInQuad">easeInQuad</option>
<option value="easeOutQuad">easeOutQuad</option>
<option value="easeInOutQuad" selected="selected">easeInOutQuad</option>
<option value="easeInCubic">easeInCubic</option>
<option value="easeOutCubic">easeOutCubic</option>
<option value="easeInOutCubic">easeInOutCubic</option>
<option value="easeInQuart">easeInQuart</option>
<option value="easeOutQuart">easeOutQuart</option>
<option value="easeInOutQuart">easeInOutQuart</option>
<option value="easeInQuint">easeInQuint</option>
<option value="easeOutQuint">easeOutQuint</option>
<option value="easeInOutQuint">easeInOutQuint</option>
</select>
</td>
</tr>
</table>
<div class="bottom">
<span id="statusUpdate"></span><br />
Examples:
<input type="button" onclick="doAnimation();" value="Animate with above settings." style="width:300px;" id="btnDo"> <br/>
<input type="button" onclick="doDefaultAnimation();" value="Animate with default settings." style="width:300px;" id="btnDoDefault"> <br/>
<input type="button" onclick="zoomExtentAnimated();" value="Animated ZoomExtent()." style="width:300px;" id="btnZoom"> <br/>
<input type="button" onclick="focusRandom();" value="Focus on random node." style="width:300px;" id="btnFocus"><br/>
<input type="button" onclick="startShow();" value="Start a show!" style="width:300px;" id="btnShow"><br/>
</div>
</div>
<div class="right">
<div id="mynetwork"></div>
<p id="selection"></p>
<p id="stabilization"></p>
</div>
</body>
</html>

+ 2
- 0
examples/network/index.html View File

@ -43,6 +43,8 @@
<p><a href="29_neighbourhood_highlight.html">29_neighbourhood_highlight.html</a></p>
<p><a href="30_importing_from_gephi.html">30_importing_from_gephi.html</a></p>
<p><a href="31_localization.html">31_localization.html</a></p>
<p><a href="32_hierarchicaLayoutMethods.html">32_hierarchicaLayoutMethods.html</a></p>
<p><a href="33_animation.html">33_animation.html</a></p>
<p><a href="graphviz/graphviz_gallery.html">graphviz_gallery.html</a></p>
</div>

+ 218
- 27
lib/network/Network.js View File

@ -219,6 +219,15 @@ function Network (container, data, options) {
this.hoverObj = {nodes:{},edges:{}};
this.controlNodesActive = false;
// animation properties
this.animationSpeed = 1/this.renderRefreshRate;
this.animationEasingFunction = "easeInOutQuint";
this.easingTime = 0;
this.sourceScale = 0;
this.targetScale = 0;
this.sourceTranslation = 0;
this.targetTranslation = 0;
// Node variables
var network = this;
this.groups = new Groups(); // object with groups
@ -246,6 +255,66 @@ function Network (container, data, options) {
// load the selection system. (mandatory, required by Network)
this._loadHierarchySystem();
/*
* Easing Functions - inspired from http://gizma.com/easing/
* only considering the t value for the range [0, 1] => [0, 1]
* https://gist.github.com/gre/1650294
*/
this.easingFunctions = {
// no easing, no acceleration
linear: function (t) {
return t
},
// accelerating from zero velocity
easeInQuad: function (t) {
return t * t
},
// decelerating to zero velocity
easeOutQuad: function (t) {
return t * (2 - t)
},
// acceleration until halfway, then deceleration
easeInOutQuad: function (t) {
return t < .5 ? 2 * t * t : -1 + (4 - 2 * t) * t
},
// accelerating from zero velocity
easeInCubic: function (t) {
return t * t * t
},
// decelerating to zero velocity
easeOutCubic: function (t) {
return (--t) * t * t + 1
},
// acceleration until halfway, then deceleration
easeInOutCubic: function (t) {
return t < .5 ? 4 * t * t * t : (t - 1) * (2 * t - 2) * (2 * t - 2) + 1
},
// accelerating from zero velocity
easeInQuart: function (t) {
return t * t * t * t
},
// decelerating to zero velocity
easeOutQuart: function (t) {
return 1 - (--t) * t * t * t
},
// acceleration until halfway, then deceleration
easeInOutQuart: function (t) {
return t < .5 ? 8 * t * t * t * t : 1 - 8 * (--t) * t * t * t
},
// accelerating from zero velocity
easeInQuint: function (t) {
return t * t * t * t * t
},
// decelerating to zero velocity
easeOutQuint: function (t) {
return 1 + (--t) * t * t * t * t
},
// acceleration until halfway, then deceleration
easeInOutQuint: function (t) {
return t < .5 ? 16 * t * t * t * t * t : 1 + 16 * (--t) * t * t * t * t
}
};
// apply options
this._setTranslation(this.frame.clientWidth / 2, this.frame.clientHeight / 2);
this._setScale(1);
@ -321,7 +390,7 @@ function Network (container, data, options) {
else {
// zoom so all data will fit on the screen, if clustering is enabled, we do not want start to be called here.
if (this.constants.stabilize == false) {
this.zoomExtent(true,this.constants.clustering.enabled);
this.zoomExtent(undefined, true,this.constants.clustering.enabled);
}
}
@ -396,7 +465,7 @@ Network.prototype._findCenter = function(range) {
*
* @param {object} range = {minX: minX, maxX: maxX, minY: minY, maxY: maxY};
*/
Network.prototype._centerNetwork = function(range) {
Network.prototype._getNetworkCenter = function(range) {
var center = this._findCenter(range);
center.x *= this.scale;
@ -404,7 +473,7 @@ Network.prototype._centerNetwork = function(range) {
center.x -= 0.5 * this.frame.canvas.clientWidth;
center.y -= 0.5 * this.frame.canvas.clientHeight;
this._setTranslation(-center.x,-center.y); // set at 0,0
return {x:-center.x,y:-center.y};
};
@ -414,15 +483,19 @@ Network.prototype._centerNetwork = function(range) {
* @param {Boolean} [initialZoom] | zoom based on fitted formula or range, true = fitted, default = false;
* @param {Boolean} [disableStart] | If true, start is not called.
*/
Network.prototype.zoomExtent = function(initialZoom, disableStart) {
Network.prototype.zoomExtent = function(animationOptions, initialZoom, disableStart) {
if (initialZoom === undefined) {
initialZoom = false;
}
if (disableStart === undefined) {
disableStart = false;
}
if (animationOptions === undefined) {
animationOptions = false;
}
var range = this._getRange();
var scale = this._getScale();
var zoomLevel;
if (initialZoom == true) {
@ -466,11 +539,17 @@ Network.prototype.zoomExtent = function(initialZoom, disableStart) {
this._setScale(zoomLevel);
this._centerNetwork(range);
var center = this._getNetworkCenter(range);
if (disableStart == false) {
this._setScale(scale);
var options = {targetTranslation: center, scale: zoomLevel, animation: animationOptions};
this.moveTo(options);
this.moving = true;
this.start();
}
else {
this._setTranslation(center.x,center.y);
}
};
@ -1804,9 +1883,9 @@ Network.prototype._YconvertCanvasToDOM = function(y) {
* @returns {{x: number, y: number}}
* @constructor
*/
Network.prototype.canvasToDOM = function(pos) {
return {x:this._XconvertCanvasToDOM(pos.x),y:this._YconvertCanvasToDOM(pos.y)};
}
Network.prototype.canvasToDOM = function (pos) {
return {x: this._XconvertCanvasToDOM(pos.x), y: this._YconvertCanvasToDOM(pos.y)};
};
/**
*
@ -1814,9 +1893,9 @@ Network.prototype.canvasToDOM = function(pos) {
* @returns {{x: number, y: number}}
* @constructor
*/
Network.prototype.DOMtoCanvas = function(pos) {
return {x:this._XconvertDOMtoCanvas(pos.x),y:this._YconvertDOMtoCanvas(pos.y)};
}
Network.prototype.DOMtoCanvas = function (pos) {
return {x: this._XconvertDOMtoCanvas(pos.x), y: this._YconvertDOMtoCanvas(pos.y)};
};
/**
* Redraw all nodes
@ -1905,7 +1984,7 @@ Network.prototype._stabilize = function() {
this._physicsTick();
count++;
}
this.zoomExtent(false,true);
this.zoomExtent(undefined,false,true);
if (this.constants.freezeForStabilization == true) {
this._restoreFrozenNodes();
}
@ -2258,33 +2337,143 @@ Network.prototype.storePosition = function() {
* Center a node in view.
*
* @param {Number} nodeId
* @param {Number} [zoomLevel]
* @param {Number} [options]
*/
Network.prototype.focusOnNode = function (nodeId, zoomLevel) {
Network.prototype.focusOnNode = function (nodeId, options) {
if (this.nodes.hasOwnProperty(nodeId)) {
if (zoomLevel === undefined) {
zoomLevel = this._getScale();
if (options === undefined) {
options = {};
}
var nodePosition= {x: this.nodes[nodeId].x, y: this.nodes[nodeId].y};
var nodePosition = {x: this.nodes[nodeId].x, y: this.nodes[nodeId].y};
options.position = nodePosition;
this.moveTo(options)
}
else {
console.log("This nodeId cannot be found.");
}
};
var requiredScale = zoomLevel;
this._setScale(requiredScale);
/**
*
* @param {Object} options | options.offset = {x:Number, y:Number} // offset from the center in DOM pixels
* | options.scale = Number // scale to move to
* | options.position = {x:Number, y:Number} // position to move to
* | options.animation = {duration:Number, easingFunction:String} || Boolean // position to move to
*/
Network.prototype.moveTo = function (options) {
if (options === undefined) {
options = {};
return;
}
if (options.offset === undefined) {options.offset = {x: 0, y: 0}; }
if (options.offset.x === undefined) {options.offset.x = 0; }
if (options.offset.y === undefined) {options.offset.y = 0; }
if (options.scale === undefined) {options.scale = this._getScale(); }
if (options.position === undefined) {options.position = this._getTranslation();}
if (options.animation === undefined) {options.animation = {duration:0}; }
if (options.animation === false ) {options.animation = {duration:0}; }
if (options.animation === true ) {options.animation = {}; }
if (options.animation.duration === undefined) {options.animation.duration = 1000; } // default duration
if (options.animation.easingFunction === undefined) {options.animation.easingFunction = "easeInOutQuad"; } // default easing function
var canvasCenter = this.DOMtoCanvas({x:0.5 * this.frame.canvas.width,y:0.5 * this.frame.canvas.height});
var translation = this._getTranslation();
this.animateView(options);
};
var distanceFromCenter = {x:canvasCenter.x - nodePosition.x,
y:canvasCenter.y - nodePosition.y};
/**
*
* @param {Object} options | options.offset = {x:Number, y:Number} // offset from the center in DOM pixels
* | options.time = Number // animation time in milliseconds
* | options.scale = Number // scale to animate to
* | options.position = {x:Number, y:Number} // position to animate to
* | options.easingFunction = String // linear, easeInQuad, easeOutQuad, easeInOutQuad,
* // easeInCubic, easeOutCubic, easeInOutCubic,
* // easeInQuart, easeOutQuart, easeInOutQuart,
* // easeInQuint, easeOutQuint, easeInOutQuint
*/
Network.prototype.animateView = function (options) {
if (options === undefined) {
options = {};
return;
}
this._setTranslation(translation.x + requiredScale * distanceFromCenter.x,
translation.y + requiredScale * distanceFromCenter.y);
this.redraw();
// forcefully complete the old animation if it was still running
if (this.easingTime != 0) {
this._transitionRedraw(1); // by setting easingtime to 1, we finish the animation.
}
this.sourceScale = this._getScale();
this.sourceTranslation = this._getTranslation();
this.targetScale = options.scale;
// directly supplying the target translation can be useful when using internally, like with zoomExtent()
if (options.targetTranslation !== undefined) {
this.targetTranslation = options.targetTranslation;
}
else {
console.log("This nodeId cannot be found.");
// set the scale so the viewCenter is based on the correct zoom level. This is overridden in the transitionRedraw
// but at least then we'll have the target transition
this._setScale(this.targetScale);
var viewCenter = this.DOMtoCanvas({x: 0.5 * this.frame.canvas.clientWidth, y: 0.5 * this.frame.canvas.clientHeight});
var distanceFromCenter = { // offset from view, distance view has to change by these x and y to center the node
x: viewCenter.x - options.position.x,
y: viewCenter.y - options.position.y
};
this.targetTranslation = {
x: this.sourceTranslation.x + distanceFromCenter.x * this.targetScale + options.offset.x,
y: this.sourceTranslation.y + distanceFromCenter.y * this.targetScale + options.offset.y
};
}
// if the time is set to 0, don't do an animation
if (options.animation.duration == 0) {
this._setScale(this.targetScale);
this._setTranslation(this.targetTranslation.x, this.targetTranslation.y);
this._redraw();
}
else {
this.animationSpeed = 1 / (this.renderRefreshRate * options.animation.duration * 0.001) || 1 / this.renderRefreshRate;
this.animationEasingFunction = options.animation.easingFunction;
this._classicRedraw = this._redraw;
this._redraw = this._transitionRedraw;
this.moving = true;
this.start();
}
};
/**
*
* @param easingTime
* @private
*/
Network.prototype._transitionRedraw = function (easingTime) {
this.easingTime = easingTime || this.easingTime + this.animationSpeed;
this.easingTime += this.animationSpeed;
var progress = this.easingFunctions[this.animationEasingFunction](this.easingTime);
this._setScale(this.sourceScale + (this.targetScale - this.sourceScale) * progress);
this._setTranslation(
this.sourceTranslation.x + (this.targetTranslation.x - this.sourceTranslation.x) * progress,
this.sourceTranslation.y + (this.targetTranslation.y - this.sourceTranslation.y) * progress
);
this._classicRedraw();
this.moving = true;
// cleanup
if (this.easingTime >= 1.0) {
this.easingTime = 0;
this._redraw = this._classicRedraw;
this.emit("animationFinished");
}
};
Network.prototype._classicRedraw = function () {
// placeholder function to be overloaded by animations;
};
/**
* Returns true when the Timeline is active.
* @returns {boolean}
@ -2293,4 +2482,6 @@ Network.prototype.isActive = function () {
return !this.activator || this.activator.active;
};
module.exports = Network;

+ 1
- 1
lib/network/mixins/HierarchicalLayoutMixin.js View File

@ -59,7 +59,7 @@ exports._setupHierarchicalLayout = function() {
// if the user defined some levels but not all, alert and run without hierarchical layout
if (undefinedLevel == true && definedLevel == true) {
throw new Error("To use the hierarchical layout, nodes require either no predefined levels or levels have to be defined for all nodes.");
this.zoomExtent(true,this.constants.clustering.enabled);
this.zoomExtent(undefined,true,this.constants.clustering.enabled);
if (!this.constants.clustering.enabled) {
this.start();
}

Loading…
Cancel
Save