Browse Source

working events like zoom and drag, testing on mobile still to be done.

flowchartTest
Alex de Mulder 9 years ago
parent
commit
a294f98686
12 changed files with 27861 additions and 27861 deletions
  1. +27156
    -27162
      dist/vis.js
  2. +49
    -42
      lib/network/Network.js
  3. +1
    -8
      lib/network/Node.js
  4. +2
    -2
      lib/network/mixins/SelectionMixin.js
  5. +36
    -29
      lib/network/modules/Canvas.js
  6. +41
    -23
      lib/network/modules/CanvasRenderer.js
  7. +462
    -0
      lib/network/modules/InteractionHandler.js
  8. +18
    -9
      lib/network/modules/PhysicsEngine.js
  9. +79
    -100
      lib/network/modules/SelectionHandler.js
  10. +0
    -460
      lib/network/modules/TouchEventHandler.js
  11. +17
    -25
      lib/network/modules/View.js
  12. +0
    -1
      lib/util.js

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


+ 49
- 42
lib/network/Network.js View File

@ -24,7 +24,8 @@ import { ClusterEngine } from './modules/Clustering'
import { CanvasRenderer } from './modules/CanvasRenderer'
import { Canvas } from './modules/Canvas'
import { View } from './modules/View'
import { TouchEventHandler } from './modules/TouchEventHandler'
import { TouchEventHandler } from './modules/InteractionHandler'
import { SelectionHandler } from "./modules/SelectionHandler"
/**
* @constructor Network
@ -132,15 +133,9 @@ function Network (container, data, options) {
inheritColor: "from", // to, from, false, true (== from)
useGradients: false // release in 4.0
},
configurePhysics:false,
navigation: {
enabled: false
},
keyboard: {
enabled: false,
speed: {x: 10, y: 10, zoom: 0.02},
bindToWindow: true
},
dataManipulation: {
enabled: false,
initiallyVisible: false
@ -152,7 +147,31 @@ function Network (container, data, options) {
direction: "UD", // UD, DU, LR, RL
layout: "hubsize" // hubsize, directed
},
interaction: {
dragNodes:true,
dragView: true,
zoomView: true,
hoverEnabled: false,
tooltip: {
delay: 300,
fontColor: 'black',
fontSize: 14, // px
fontFace: 'verdana',
color: {
border: '#666',
background: '#FFFFC6'
}
},
keyboard: {
enabled: false,
speed: {x: 10, y: 10, zoom: 0.02},
bindToWindow: true
}
},
selection: {
enabled: true,
selectConnectedEdges: true
},
smoothCurves: {
enabled: true,
dynamic: true,
@ -161,25 +180,6 @@ function Network (container, data, options) {
},
locale: 'en',
locales: locales,
tooltip: {
delay: 300,
fontColor: 'black',
fontSize: 14, // px
fontFace: 'verdana',
color: {
border: '#666',
background: '#FFFFC6'
}
},
dragNetwork: true,
dragNodes: true,
zoomable: true,
hover: false,
hideEdgesOnDrag: false,
hideNodesOnDrag: false,
width : '100%',
height : '100%',
selectable: true,
useDefaultGroups: true
};
this.constants = util.extend({}, this.defaultOptions);
@ -218,20 +218,24 @@ function Network (container, data, options) {
onMouseMove: function() {},
onRelease: function() {}
},
container: container
container: container,
view: {
scale:1,
translation:{x:0,y:0}
}
};
// modules
this.view = new View(this.body);
this.renderer = new CanvasRenderer(this.body);
this.canvas = new Canvas(this.body);
this.selectionHandler = new SelectionHandler(this.body, this.canvas);
this.touchHandler = new TouchEventHandler(this.body, this.canvas, this.selectionHandler);
this.view = new View(this.body, this.canvas);
this.renderer = new CanvasRenderer(this.body, this.canvas);
this.clustering = new ClusterEngine(this.body);
this.physics = new PhysicsEngine(this.body);
this.canvas = new Canvas(this.body);
this.touchHandler = new TouchEventHandler(this.body);
this.renderer.setCanvas(this.canvas);
this.view.setCanvas(this.canvas);
this.touchHandler.setCanvas(this.canvas);
// create the DOM elements
this.canvas.create();
this.hoverObj = {nodes:{},edges:{}};
this.controlNodesActive = false;
@ -387,7 +391,7 @@ Network.prototype.setData = function(data, disableStart) {
}
// unselect all to ensure no selections from old data are carried over.
this._unselectAll(true);
this.selectionHandler.unselectAll();
// we set initializing to true to ensure that the hierarchical layout is not performed until both nodes and edges are added.
this.initializing = true;
@ -433,7 +437,7 @@ Network.prototype.setData = function(data, disableStart) {
}
else {
// find a stable position or start animating to a stable position
this.physics.startSimulation()
this.body.emitter.emit("stabilize");
}
}
else {
@ -459,7 +463,10 @@ Network.prototype.setOptions = function (options) {
this.groups.useDefaultGroups = this.constants.useDefaultGroups;
this.physics.setOptions(options.physics);
this.canvas.setOptions(this.constants);
this.canvas.setOptions(options.canvas);
this.renderer.setOptions(options.rendering);
this.touchHandler.setOptions(options.interaction);
this.selectionHandler.setOptions(options.selection);
if (options.onAdd) {this.triggerFunctions.add = options.onAdd;}
@ -572,7 +579,7 @@ Network.prototype.setOptions = function (options) {
//this._createKeyBinds();
this._markAllEdgesAsDirty();
this.canvas.setSize(this.constants.width, this.constants.height);
this.canvas.setSize();
if (this.constants.hierarchicalLayout.enabled == true && this.initializing == false) {
this._resetLevels();
this._setupHierarchicalLayout();
@ -783,12 +790,12 @@ Network.prototype._checkHidePopup = function (pointer) {
if (this.popup.popupTargetType == 'node') {
stillOnObj = this.body.nodes[this.popup.popupTargetId].isOverlappingWith(pointerObj);
if (stillOnObj === true) {
var overNode = this._getNodeAt(pointer);
var overNode = this.getNodeAt(pointer);
stillOnObj = overNode.id == this.popup.popupTargetId;
}
}
else {
if (this._getNodeAt(pointer) === null) {
if (this.getNodeAt(pointer) === null) {
stillOnObj = this.body.edges[this.popup.popupTargetId].isOverlappingWith(pointerObj);
}
}
@ -1231,7 +1238,7 @@ Network.prototype._handleNavigation = function() {
x: this.frame.canvas.clientWidth / 2,
y: this.frame.canvas.clientHeight / 2
};
this._zoom(this.scale*(1 + this.zoomIncrement), center);
this.zoom(this.scale*(1 + this.zoomIncrement), center);
}
};

+ 1
- 8
lib/network/Node.js View File

@ -216,13 +216,6 @@ Node.prototype.unselect = function() {
};
/**
* Reset the calculated size of the node, forces it to recalculate its size
*/
Node.prototype.clearSizeCache = function() {
this._reset();
};
/**
* Reset the calculated size of the node, forces it to recalculate its size
* @private
@ -619,7 +612,7 @@ Node.prototype._drawCircle = function (ctx) {
};
Node.prototype._resizeEllipse = function (ctx) {
if (!this.width) {
if (this.width === undefined) {
var textSize = this.getTextSize(ctx);
this.width = textSize.width * 1.5;

+ 2
- 2
lib/network/mixins/SelectionMixin.js View File

@ -104,8 +104,8 @@ exports._getAllEdgesOverlappingWith = function (object) {
};
/**
* Place holder. To implement change the _getNodeAt to a _getObjectAt. Have the _getObjectAt call
* _getNodeAt and _getEdgesAt, then priortize the selection to user preferences.
* Place holder. To implement change the getNodeAt to a _getObjectAt. Have the _getObjectAt call
* getNodeAt and _getEdgesAt, then priortize the selection to user preferences.
*
* @param pointer
* @returns {null}

+ 36
- 29
lib/network/modules/Canvas.js View File

@ -1,26 +1,38 @@
var Hammer = require('../../module/hammer');
var hammerUtil = require('../../hammerUtil');
var util = require('../../util');
/**
* Create the main frame for the Network.
* This function is executed once when a Network object is created. The frame
* contains a canvas, and this canvas contains all objects like the axis and
* nodes.
* @private
*/
class Canvas {
/**
* Create the main frame for the Network.
* This function is executed once when a Network object is created. The frame
* contains a canvas, and this canvas contains all objects like the axis and
* nodes.
* @private
*/
constructor(body, options) {
constructor(body) {
this.body = body;
this.setOptions(options);
this.translation = {x: 0, y: 0};
this.scale = 1.0;
this.body.emitter.on("_setScale", (scale) => {this.scale = scale});
this.body.emitter.on("_setTranslation", (translation) => {this.translation.x = translation.x; this.translation.y = translation.y;});
this.body.emitter.once("resize", (obj) => {this.translation.x = obj.width * 0.5; this.translation.y = obj.height * 0.5; this.body.emitter.emit("_setTranslation", this.translation)});
this.options = {};
this.defaultOptions = {
width:'100%',
height:'100%'
}
util.extend(this.options, this.defaultOptions);
this.body.emitter.once("resize", (obj) => {this.body.view.translation.x = obj.width * 0.5; this.body.view.translation.y = obj.height * 0.5;});
this.pixelRatio = 1;
}
setOptions(options) {
if (options !== undefined) {
util.deepExtend(this.options, options);
}
}
create() {
// remove all elements from the container element.
while (this.body.container.hasChildNodes()) {
this.body.container.removeChild(this.body.container.firstChild);
@ -61,8 +73,8 @@ class Canvas {
// add the frame to the container element
this.body.container.appendChild(this.frame);
this.body.emitter.emit("_setScale", 1);;
this.body.emitter.emit("_setTranslation", {x: 0.5 * this.frame.canvas.clientWidth,y: 0.5 * this.frame.canvas.clientHeight});;
this.body.view.scale = 1;
this.body.view.translation = {x: 0.5 * this.frame.canvas.clientWidth,y: 0.5 * this.frame.canvas.clientHeight};
this._bindHammer();
}
@ -89,11 +101,9 @@ class Canvas {
this.hammer.on('panend', me.body.eventListeners.onDragEnd );
this.hammer.on('pinch', me.body.eventListeners.onPinch.bind(me) );
// TODO: neatly cleanup these handlers when re-creating the Canvas
if (this.options.zoomable == true) {
this.frame.canvas.addEventListener('mousewheel', me.body.eventListeners.onMouseWheel.bind(me));
this.frame.canvas.addEventListener('DOMMouseScroll', me.body.eventListeners.onMouseWheel.bind(me));
}
// TODO: neatly cleanup these handlers when re-creating the Canvas, IF these are done with hammer, event.stopPropagation will not work?
this.frame.canvas.addEventListener('mousewheel', me.body.eventListeners.onMouseWheel.bind(me));
this.frame.canvas.addEventListener('DOMMouseScroll', me.body.eventListeners.onMouseWheel.bind(me));
this.frame.canvas.addEventListener('mousemove', me.body.eventListeners.onMouseMove.bind(me));
@ -101,9 +111,6 @@ class Canvas {
hammerUtil.onRelease(this.hammerFrame, me.body.eventListeners.onRelease.bind(me) );
}
setOptions(options = {}) {
this.options = options;
}
/**
* Set a new size for the network
@ -112,7 +119,7 @@ class Canvas {
* @param {string} height Height in pixels or percentage (for example '400px'
* or '30%')
*/
setSize(width, height) {
setSize(width = this.options.width, height = this.options.height) {
var emitEvent = false;
var oldWidth = this.frame.canvas.width;
var oldHeight = this.frame.canvas.height;
@ -159,7 +166,7 @@ class Canvas {
* @private
*/
_XconvertDOMtoCanvas(x) {
return (x - this.translation.x) / this.scale;
return (x - this.body.view.translation.x) / this.body.view.scale;
}
/**
@ -170,7 +177,7 @@ class Canvas {
* @private
*/
_XconvertCanvasToDOM(x) {
return x * this.scale + this.translation.x;
return x * this.body.view.scale + this.body.view.translation.x;
}
/**
@ -181,7 +188,7 @@ class Canvas {
* @private
*/
_YconvertDOMtoCanvas(y) {
return (y - this.translation.y) / this.scale;
return (y - this.body.view.translation.y) / this.body.view.scale;
}
/**
@ -192,7 +199,7 @@ class Canvas {
* @private
*/
_YconvertCanvasToDOM(y) {
return y * this.scale + this.translation.y ;
return y * this.body.view.scale + this.body.view.translation.y;
}

+ 41
- 23
lib/network/modules/CanvasRenderer.js View File

@ -7,32 +7,49 @@ if (typeof window !== 'undefined') {
window.webkitRequestAnimationFrame || window.msRequestAnimationFrame;
}
var util = require('../../util');
class CanvasRenderer {
constructor(body) {
constructor(body, canvas) {
this.body = body;
this.canvas = canvas;
this.redrawRequested = false;
this.renderTimer = false;
this.requiresTimeout = true;
this.continueRendering = true;
this.continueRendering = false;
this.renderRequests = 0;
this.translation = {x: 0, y: 0};
this.scale = 1.0;
this.canvasTopLeft = {x: 0, y: 0};
this.canvasBottomRight = {x: 0, y: 0};
this.body.emitter.on("_setScale", (scale) => this.scale = scale);
this.body.emitter.on("_setTranslation", (translation) => {this.translation.x = translation.x; this.translation.y = translation.y;});
this.dragging = false;
this.body.emitter.on("dragStart", () => {this.dragging = true; console.log("here")});
this.body.emitter.on("dragEnd", () => this.dragging = false);
this.body.emitter.on("_redraw", this._redraw.bind(this));
this.body.emitter.on("_redrawHidden", this._redraw.bind(this, true));
this.body.emitter.on("_requestRedraw", this._requestRedraw.bind(this));
this.body.emitter.on("_startRendering", () => {this.renderRequests += 1; this.continueRendering = true; this.startRendering();});
this.body.emitter.on("_stopRendering", () => {this.renderRequests -= 1; this.continueRendering = this.renderRequests > 0;});
this.options = {};
this.defaultOptions = {
hideEdgesOnDrag: false,
hideNodesOnDrag: false
}
util.extend(this.options,this.defaultOptions);
this._determineBrowserMethod();
}
setOptions(options) {
if (options !== undefined) {
util.deepExtend(this.options, options);
}
}
startRendering() {
if (this.continueRendering === true) {
@ -67,9 +84,6 @@ class CanvasRenderer {
}
}
setCanvas(canvas) {
this.canvas = canvas;
}
/**
* Redraw the network with the current data
* chart will be resized too.
@ -84,14 +98,14 @@ class CanvasRenderer {
* @param hidden | used to get the first estimate of the node sizes. only the nodes are drawn after which they are quickly drawn over.
* @private
*/
_requestRedraw(hidden) {
_requestRedraw() {
if (this.redrawRequested !== true) {
this.redrawRequested = true;
if (this.requiresTimeout === true) {
window.setTimeout(this._redraw.bind(this, hidden),0);
window.setTimeout(this._redraw.bind(this, false),0);
}
else {
window.requestAnimationFrame(this._redraw.bind(this, hidden, true));
window.requestAnimationFrame(this._redraw.bind(this, false));
}
}
}
@ -109,25 +123,27 @@ class CanvasRenderer {
var h = this.canvas.frame.canvas.clientHeight;
ctx.clearRect(0, 0, w, h);
this.body.emitter.emit("beforeDrawing", ctx);
// set scaling and translation
ctx.save();
ctx.translate(this.translation.x, this.translation.y);
ctx.scale(this.scale, this.scale);
ctx.translate(this.body.view.translation.x, this.body.view.translation.y);
ctx.scale(this.body.view.scale, this.body.view.scale);
this.canvasTopLeft = this.canvas.DOMtoCanvas({x:0,y:0});
this.canvasBottomRight = this.canvas.DOMtoCanvas({x:this.canvas.frame.canvas.clientWidth,y:this.canvas.frame.canvas.clientHeight});
console.log(this.dragging)
if (hidden === false) {
// todo: solve this
//if (this.drag.dragging == false || this.drag.dragging === undefined || this.constants.hideEdgesOnDrag == false) {
if (this.dragging === false || (this.dragging === true && this.options.hideEdgesOnDrag === false)) {
this._drawEdges(ctx);
//}
}
}
// todo: solve this
//if (this.drag.dragging == false || this.drag.dragging === undefined || this.constants.hideNodesOnDrag == false) {
if (this.dragging === false || (this.dragging === true && this.options.hideNodesOnDrag === false)) {
this._drawNodes(ctx, this.body.nodes, hidden);
//}
}
if (hidden === false) {
if (this.controlNodesActive == true) {
@ -144,6 +160,8 @@ class CanvasRenderer {
if (hidden === true) {
ctx.clearRect(0, 0, w, h);
}
this.body.emitter.emit("afterDrawing", ctx);
}
@ -160,7 +178,7 @@ class CanvasRenderer {
for (var id in nodes) {
if (nodes.hasOwnProperty(id)) {
nodes[id].setScaleAndPos(this.scale,this.canvasTopLeft,this.canvasBottomRight);
nodes[id].setScaleAndPos(this.body.view.scale,this.canvasTopLeft,this.canvasBottomRight);
if (nodes[id].isSelected()) {
selected.push(id);
}
@ -195,7 +213,7 @@ class CanvasRenderer {
for (var id in edges) {
if (edges.hasOwnProperty(id)) {
var edge = edges[id];
edge.setScale(this.scale);
edge.setScale(this.body.view.scale);
if (edge.connected === true) {
edges[id].draw(ctx);
}

+ 462
- 0
lib/network/modules/InteractionHandler.js View File

@ -0,0 +1,462 @@
/**
* Created by Alex on 2/27/2015.
*
*/
var util = require('../../util');
class InteractionHandler {
constructor(body, canvas, selectionHandler) {
this.body = body;
this.canvas = canvas;
this.selectionHandler = selectionHandler;
// bind the events from hammer to functions in this object
this.body.eventListeners.onTap = this.onTap.bind(this);
this.body.eventListeners.onTouch = this.onTouch.bind(this);
this.body.eventListeners.onDoubleTap = this.onDoubleTap.bind(this);
this.body.eventListeners.onHold = this.onHold.bind(this);
this.body.eventListeners.onDragStart = this.onDragStart.bind(this);
this.body.eventListeners.onDrag = this.onDrag.bind(this);
this.body.eventListeners.onDragEnd = this.onDragEnd.bind(this);
this.body.eventListeners.onMouseWheel = this.onMouseWheel.bind(this);
this.body.eventListeners.onPinch = this.onPinch.bind(this);
this.body.eventListeners.onMouseMove = this.onMouseMove.bind(this);
this.body.eventListeners.onRelease = this.onRelease.bind(this);
this.touchTime = 0;
this.drag = {};
this.pinch = {};
this.pointerPosition = {x:0,y:0};
this.hoverObj = {nodes:{},edges:{}};
this.options = {};
this.defaultOptions = {
dragNodes:true,
dragView: true,
zoomView: true,
selectEnabled: true,
hoverEnabled: false
}
util.extend(this.options,this.defaultOptions);
}
setOptions(options) {
if (options !== undefined) {
util.deepExtend(this.options, options);
}
}
/**
* Get the pointer location from a touch location
* @param {{x: Number, y: Number}} touch
* @return {{x: Number, y: Number}} pointer
* @private
*/
getPointer(touch) {
return {
x: touch.x - util.getAbsoluteLeft(this.canvas.frame.canvas),
y: touch.y - util.getAbsoluteTop(this.canvas.frame.canvas)
};
}
/**
* On start of a touch gesture, store the pointer
* @param event
* @private
*/
onTouch(event) {
if (new Date().valueOf() - this.touchTime > 100) {
this.drag.pointer = this.getPointer(event.center);
this.drag.pinched = false;
this.pinch.scale = this.body.view.scale;
// to avoid double fireing of this event because we have two hammer instances. (on canvas and on frame)
this.touchTime = new Date().valueOf();
}
}
/**
* handle tap/click event: select/unselect a node
* @private
*/
onTap(event) {
var pointer = this.getPointer(event.center);
var previouslySelected = this.selectionHandler._getSelectedObjectCount() > 0;
var selected = this.selectionHandler.selectOnPoint(pointer);
if (selected === true || (previouslySelected == true && selected === false)) { // select or unselect
this.body.emitter.emit('selected', this.selectionHandler.getSelection());
}
this.selectionHandler._generateClickEvent("click",pointer);
}
/**
* handle doubletap event
* @private
*/
onDoubleTap(event) {
var pointer = this.getPointer(event.center);
this.selectionHandler._generateClickEvent("doubleClick",pointer);
}
/**
* handle long tap event: multi select nodes
* @private
*/
onHold(event) {
var pointer = this.getPointer(event.center);
var selectionChanged = this.selectionHandler.selectAdditionalOnPoint(pointer);
if (selectionChanged === true) { // select or longpress
this.body.emitter.emit('selected', this.selectionHandler.getSelection());
}
this.selectionHandler._generateClickEvent("click",pointer);
}
/**
* handle the release of the screen
*
* @private
*/
onRelease() {}
/**
* This function is called by onDragStart.
* It is separated out because we can then overload it for the datamanipulation system.
*
* @private
*/
onDragStart(event) {
//in case the touch event was triggered on an external div, do the initial touch now.
if (this.drag.pointer === undefined) {
this.onTouch(event);
}
var node = this.selectionHandler.getNodeAt(this.drag.pointer);
// note: drag.pointer is set in onTouch to get the initial touch location
this.drag.dragging = true;
this.drag.selection = [];
this.drag.translation = util.extend({},this.body.view.translation); // copy the object
this.drag.nodeId = null;
this.draggingNodes = false;
this.body.emitter.emit("dragStart", {nodeIds: this.selectionHandler.getSelection().nodes});
if (node != null && this.options.dragNodes === true) {
this.drag.nodeId = node.id;
// select the clicked node if not yet selected
if (node.isSelected() === false) {
this.selectionHandler.selectObject(node);
}
var selection = this.selectionHandler.selectionObj.nodes;
// create an array with the selected nodes and their original location and status
for (let nodeId in selection) {
if (selection.hasOwnProperty(nodeId)) {
var object = selection[nodeId];
var s = {
id: object.id,
node: object,
// store original x, y, xFixed and yFixed, make the node temporarily Fixed
x: object.x,
y: object.y,
xFixed: object.xFixed,
yFixed: object.yFixed
};
object.xFixed = true;
object.yFixed = true;
this.drag.selection.push(s);
}
}
}
}
/**
* handle drag event
* @private
*/
onDrag(event) {
if (this.drag.pinched === true) {
return;
}
// remove the focus on node if it is focussed on by the focusOnNode
this.body.emitter.emit("unlockNode");
var pointer = this.getPointer(event.center);
var selection = this.drag.selection;
if (selection && selection.length && this.options.dragNodes === true) {
// calculate delta's and new location
var deltaX = pointer.x - this.drag.pointer.x;
var deltaY = pointer.y - this.drag.pointer.y;
// update position of all selected nodes
selection.forEach((selection) => {
var node = selection.node;
if (!selection.xFixed) {
node.x = this.canvas._XconvertDOMtoCanvas(this.canvas._XconvertCanvasToDOM(selection.x) + deltaX);
}
if (!selection.yFixed) {
node.y = this.canvas._YconvertDOMtoCanvas(this.canvas._YconvertCanvasToDOM(selection.y) + deltaY);
}
});
// start the simulation of the physics
this.body.emitter.emit("startSimulation");
}
else {
// move the network
if (this.options.dragView === true) {
// if the drag was not started properly because the click started outside the network div, start it now.
if (this.drag.pointer === undefined) {
this._handleDragStart(event);
return;
}
var diffX = pointer.x - this.drag.pointer.x;
var diffY = pointer.y - this.drag.pointer.y;
this.body.view.translation = {x:this.drag.translation.x + diffX, y:this.drag.translation.y + diffY};
this.body.emitter.emit("_redraw");
}
}
}
/**
* handle drag start event
* @private
*/
onDragEnd(event) {
this.drag.dragging = false;
var selection = this.drag.selection;
if (selection && selection.length) {
selection.forEach(function (s) {
// restore original xFixed and yFixed
s.node.xFixed = s.xFixed;
s.node.yFixed = s.yFixed;
});
this.body.emitter.emit("startSimulation");
}
else {
this.body.emitter.emit("_requestRedraw");
}
this.body.emitter.emit("dragEnd", {nodeIds: this.selectionHandler.getSelection().nodes});
}
/**
* Handle pinch event
* @param event
* @private
*/
onPinch(event) {
var pointer = this.getPointer(event.center);
this.drag.pinched = true;
if (this.pinch[scale] === undefined) {
this.pinch.scale = 1;
}
// TODO: enabled moving while pinching?
var scale = this.pinch.scale * event.gesture.scale;
this.zoom(scale, pointer)
}
/**
* Zoom the network in or out
* @param {Number} scale a number around 1, and between 0.01 and 10
* @param {{x: Number, y: Number}} pointer Position on screen
* @return {Number} appliedScale scale is limited within the boundaries
* @private
*/
zoom(scale, pointer) {
if (this.options.zoomView === true) {
var scaleOld = this.body.view.scale;
if (scale < 0.00001) {
scale = 0.00001;
}
if (scale > 10) {
scale = 10;
}
var preScaleDragPointer = null;
if (this.drag !== undefined) {
if (this.drag.dragging === true) {
preScaleDragPointer = this.canvas.DOMtoCanvas(this.drag.pointer);
}
}
// + this.canvas.frame.canvas.clientHeight / 2
var translation = this.body.view.translation;
var scaleFrac = scale / scaleOld;
var tx = (1 - scaleFrac) * pointer.x + translation.x * scaleFrac;
var ty = (1 - scaleFrac) * pointer.y + translation.y * scaleFrac;
this.body.view.scale = scale;
this.body.view.translation = {x:tx, y:ty};
if (preScaleDragPointer != null) {
var postScaleDragPointer = this.canvas.canvasToDOM(preScaleDragPointer);
this.drag.pointer.x = postScaleDragPointer.x;
this.drag.pointer.y = postScaleDragPointer.y;
}
this.body.emitter.emit("_requestRedraw");
if (scaleOld < scale) {
this.body.emitter.emit("zoom", {direction: "+"});
}
else {
this.body.emitter.emit("zoom", {direction: "-"});
}
}
}
/**
* Event handler for mouse wheel event, used to zoom the timeline
* See http://adomas.org/javascript-mouse-wheel/
* https://github.com/EightMedia/hammer.js/issues/256
* @param {MouseEvent} event
* @private
*/
onMouseWheel(event) {
// retrieve delta
var delta = 0;
if (event.wheelDelta) { /* IE/Opera. */
delta = event.wheelDelta / 120;
} else if (event.detail) { /* Mozilla case. */
// In Mozilla, sign of delta is different than in IE.
// Also, delta is multiple of 3.
delta = -event.detail / 3;
}
// If delta is nonzero, handle it.
// Basically, delta is now positive if wheel was scrolled up,
// and negative, if wheel was scrolled down.
if (delta) {
// calculate the new scale
var scale = this.body.view.scale;
var zoom = delta / 10;
if (delta < 0) {
zoom = zoom / (1 - zoom);
}
scale *= (1 + zoom);
// calculate the pointer location
var pointer = {x:event.pageX, y:event.pageY};
// apply the new scale
this.zoom(scale, pointer);
}
// Prevent default actions caused by mouse wheel.
event.preventDefault();
}
/**
* Mouse move handler for checking whether the title moves over a node with a title.
* @param {Event} event
* @private
*/
onMouseMove(event) {
var pointer = {x:event.pageX, y:event.pageY};
var popupVisible = false;
// check if the previously selected node is still selected
if (this.popup !== undefined) {
if (this.popup.hidden === false) {
this._checkHidePopup(pointer);
}
// if the popup was not hidden above
if (this.popup.hidden === false) {
popupVisible = true;
this.popup.setPosition(pointer.x + 3, pointer.y - 5)
this.popup.show();
}
}
// if we bind the keyboard to the div, we have to highlight it to use it. This highlights it on mouse over
if (this.options.keyboard.bindToWindow == false && this.options.keyboard.enabled === true) {
this.canvas.frame.focus();
}
// start a timeout that will check if the mouse is positioned above an element
if (popupVisible === false) {
var me = this;
var checkShow = function() {
me._checkShowPopup(pointer);
};
if (this.popupTimer) {
clearInterval(this.popupTimer); // stop any running calculationTimer
}
if (!this.drag.dragging) {
this.popupTimer = setTimeout(checkShow, this.options.tooltip.delay);
}
}
/**
* Adding hover highlights
*/
if (this.options.hoverEnabled === true) {
// removing all hover highlights
for (var edgeId in this.hoverObj.edges) {
if (this.hoverObj.edges.hasOwnProperty(edgeId)) {
this.hoverObj.edges[edgeId].hover = false;
delete this.hoverObj.edges[edgeId];
}
}
// adding hover highlights
var obj = this.selectionHandler.getNodeAt(pointer);
if (obj == null) {
obj = this.selectionHandler.getEdgeAt(pointer);
}
if (obj != null) {
this._hoverObject(obj);
}
// removing all node hover highlights except for the selected one.
for (var nodeId in this.hoverObj.nodes) {
if (this.hoverObj.nodes.hasOwnProperty(nodeId)) {
if (obj instanceof Node && obj.id != nodeId || obj instanceof Edge || obj == null) {
this._blurObject(this.hoverObj.nodes[nodeId]);
delete this.hoverObj.nodes[nodeId];
}
}
}
this.body.emitter.emit("_requestRedraw");
}
}
}
export {TouchEventHandler};

+ 18
- 9
lib/network/modules/PhysicsEngine.js View File

@ -15,13 +15,9 @@ var util = require('../../util');
class PhysicsEngine {
constructor(body, options) {
constructor(body) {
this.body = body;
this.physicsBody = {calculationNodes: {}, calculationNodeIndices:[], forces: {}, velocities: {}};
this.scale = 1;
this.viewFunction = undefined;
this.body.emitter.on("_setScale", (scale) => this.scale = scale);
this.simulationInterval = 1000 / 60;
this.requiresTimeout = true;
@ -32,7 +28,8 @@ class PhysicsEngine {
this.stabilizationIterations = 0;
// default options
this.options = {
this.options = {};
this.defaultOptions = {
barnesHut: {
thetaInverted: 1 / 0.5, // inverted to save time during calculation
gravitationalConstant: -2000,
@ -67,8 +64,11 @@ class PhysicsEngine {
zoomExtent: true
}
}
util.extend(this.options, this.defaultOptions);
this.setOptions(options);
this.body.emitter.on("stabilize", () => {this.startSimulation();});
this.body.emitter.on("startSimulation", () => {this.stabilized = false; this.runSimulation();});
this.body.emitter.on("stopSimulation", () => {this.stopSimulation();});
}
setOptions(options) {
@ -116,6 +116,15 @@ class PhysicsEngine {
}
}
stopSimulation() {
this.stabilized = true;
if (this.viewFunction !== undefined) {
this.body.emitter.off("_beforeRender", this.viewFunction);
this.viewFunction = undefined;
this.body.emitter.emit("_stopRendering");
}
}
runSimulation() {
if (this.viewFunction === undefined) {
this.viewFunction = this.simulationStep.bind(this);
@ -157,7 +166,7 @@ class PhysicsEngine {
else {
this.stabilizationIterations = 0;
}
this.body.emitter.emit("_stopRendering");
this.stopSimulation();
}
}
@ -261,7 +270,7 @@ class PhysicsEngine {
var nodeIndices = this.physicsBody.calculationNodeIndices;
var maxVelocity = this.options.maxVelocity === 0 ? 1e9 : this.options.maxVelocity;
var stabilized = true;
var vminCorrected = this.options.minVelocity / Math.max(this.scale,0.05);
var vminCorrected = this.options.minVelocity / Math.max(this.body.view.scale,0.05);
for (let i = 0; i < nodeIndices.length; i++) {
let nodeId = nodeIndices[i];

lib/network/modules/components/SelectionHandler.js → lib/network/modules/SelectionHandler.js View File

@ -2,24 +2,31 @@
* Created by Alex on 2/27/2015.
*/
var Node = require("../../Node");
var Node = require("../Node");
var util = require('../../util');
class SelectionHandler {
constructor(body) {
constructor(body, canvas) {
this.body = body;
this.canvas = canvas;
this.selectionObj = {nodes:[], edges:[]};
this.options = {
this.options = {};
this.defaultOptions = {
select: true,
selectConnectedEdges: true
}
util.extend(this.options, this.defaultOptions);
}
setCanvas(canvas) {
this.canvas = canvas;
setOptions(options) {
if (options !== undefined) {
util.deepExtend(this.options, options);
}
}
/**
* handles the selection part of the tap;
*
@ -27,52 +34,84 @@ class SelectionHandler {
* @private
*/
selectOnPoint(pointer) {
var selected = false;
if (this.options.select === true) {
if (this._getSelectedObjectCount() > 0) {this._unselectAll();}
this.selectObject(pointer);
this._generateClickEvent(pointer);
var obj = this.getNodeAt(pointer) || this.getEdgeAt(pointer);;
// deselect
if (obj === undefined) {
this.unselectAll();
}
else {
selected = this.selectObject(obj);
}
this.body.emitter.emit("_requestRedraw");
}
return selected;
}
selectAdditionalOnPoint(pointer) {
var selectionChanged = false;
if (this.options.select === true) {
var obj = this.getNodeAt(pointer) || this.getEdgeAt(pointer);;
if (obj !== undefined) {
selectionChanged = true;
if (obj.isSelected() === true) {
this.deselectObject(obj);
}
else {
this.selectObject(obj);
}
this.body.emitter.emit("_requestRedraw");
}
}
return selectionChanged;
}
_generateClickEvent(pointer) {
_generateClickEvent(eventType,pointer) {
var properties = this.getSelection();
properties['pointer'] = {
DOM: {x: pointer.x, y: pointer.y},
canvas: this.canvas.DOMtoCanvas(pointer)
}
this.body.emitter.emit("click", properties);
this.body.emitter.emit(eventType, properties);
}
selectObject(pointer) {
var obj = this._getNodeAt(pointer);
if (obj != null) {
if (this.options.selectConnectedEdges === true) {
this._selectConnectedEdges(obj);
selectObject(obj) {
if (obj !== undefined) {
if (obj instanceof Node) {
if (this.options.selectConnectedEdges === true) {
this._selectConnectedEdges(obj);
}
}
}
else {
obj = this._getEdgeAt(pointer);
}
if (obj !== null) {
obj.select();
this._addToSelection(obj);
this.body.emitter.emit('selected', this.getSelection())
return true;
}
return false;
}
deselectObject(obj) {
if (obj.isSelected() === true) {
obj.selected = false;
this._removeFromSelection(obj);
}
return obj;
}
/**
*
* @param object
* @param overlappingNodes
* retrieve all nodes overlapping with given object
* @param {Object} object An object with parameters left, top, right, bottom
* @return {Number[]} An array with id's of the overlapping nodes
* @private
*/
_getNodesOverlappingWith(object, overlappingNodes) {
_getAllNodesOverlappingWith(object) {
var overlappingNodes = [];
var nodes = this.body.nodes;
for (var nodeId in nodes) {
if (nodes.hasOwnProperty(nodeId)) {
@ -81,17 +120,6 @@ class SelectionHandler {
}
}
}
}
/**
* retrieve all nodes overlapping with given object
* @param {Object} object An object with parameters left, top, right, bottom
* @return {Number[]} An array with id's of the overlapping nodes
* @private
*/
_getAllNodesOverlappingWith(object) {
var overlappingNodes = [];
this._getNodesOverlappingWith(object,overlappingNodes);
return overlappingNodes;
}
@ -118,10 +146,10 @@ class SelectionHandler {
* Get the top node at the a specific point (like a click)
*
* @param {{x: Number, y: Number}} pointer
* @return {Node | null} node
* @return {Node | undefined} node
* @private
*/
_getNodeAt(pointer) {
getNodeAt(pointer) {
// we first check if this is an navigation controls element
var positionObject = this._pointerToPositionObject(pointer);
var overlappingNodes = this._getAllNodesOverlappingWith(positionObject);
@ -132,7 +160,7 @@ class SelectionHandler {
return this.body.nodes[overlappingNodes[overlappingNodes.length - 1]];
}
else {
return null;
return undefined;
}
}
@ -169,14 +197,14 @@ class SelectionHandler {
/**
* Place holder. To implement change the _getNodeAt to a _getObjectAt. Have the _getObjectAt call
* _getNodeAt and _getEdgesAt, then priortize the selection to user preferences.
* Place holder. To implement change the getNodeAt to a _getObjectAt. Have the _getObjectAt call
* getNodeAt and _getEdgesAt, then priortize the selection to user preferences.
*
* @param pointer
* @returns {null}
* @returns {undefined}
* @private
*/
_getEdgeAt(pointer) {
getEdgeAt(pointer) {
var positionObject = this._pointerToPositionObject(pointer);
var overlappingEdges = this._getAllEdgesOverlappingWith(positionObject);
@ -184,7 +212,7 @@ class SelectionHandler {
return this.body.edges[overlappingEdges[overlappingEdges.length - 1]];
}
else {
return null;
return undefined;
}
}
@ -238,10 +266,9 @@ class SelectionHandler {
/**
* Unselect all. The selectionObj is useful for this.
*
* @param {Boolean} [doNotTrigger] | ignore trigger
* @private
*/
_unselectAll(doNotTrigger = false) {
unselectAll() {
for(var nodeId in this.selectionObj.nodes) {
if(this.selectionObj.nodes.hasOwnProperty(nodeId)) {
this.selectionObj.nodes[nodeId].unselect();
@ -254,10 +281,6 @@ class SelectionHandler {
}
this.selectionObj = {nodes:{},edges:{}};
if (doNotTrigger == false) {
this.body.emitter.emit('select', this.getSelection());
}
}
@ -289,7 +312,7 @@ class SelectionHandler {
return this.selectionObj.nodes[nodeId];
}
}
return null;
return undefined;
}
/**
@ -304,7 +327,7 @@ class SelectionHandler {
return this.selectionObj.edges[edgeId];
}
}
return null;
return undefined;
}
@ -469,50 +492,6 @@ class SelectionHandler {
/**
* handles the selection part of the double tap and opens a cluster if needed
*
* @param {Object} pointer
* @private
*/
_handleDoubleTap(pointer) {
var node = this._getNodeAt(pointer);
if (node != null && node !== undefined) {
// we reset the areaCenter here so the opening of the node will occur
this.areaCenter = {"x" : this._XconvertDOMtoCanvas(pointer.x),
"y" : this._YconvertDOMtoCanvas(pointer.y)};
this.openCluster(node);
}
var properties = this.getSelection();
properties['pointer'] = {
DOM: {x: pointer.x, y: pointer.y},
canvas: {x: this._XconvertDOMtoCanvas(pointer.x), y: this._YconvertDOMtoCanvas(pointer.y)}
}
this.body.emitter.emit("doubleClick", properties);
}
/**
* Handle the onHold selection part
*
* @param pointer
* @private
*/
_handleOnHold(pointer) {
var node = this._getNodeAt(pointer);
if (node != null) {
this._selectObject(node,true);
}
else {
var edge = this._getEdgeAt(pointer);
if (edge != null) {
this._selectObject(edge,true);
}
}
this._requestRedraw();
}
/**
*
* retrieve the currently selected objects
@ -574,7 +553,7 @@ class SelectionHandler {
throw 'Selection must be an array with ids';
// first unselect any selected node
this._unselectAll(true);
this.unselectAll(true);
for (i = 0, iMax = selection.length; i < iMax; i++) {
id = selection[i];
@ -601,7 +580,7 @@ class SelectionHandler {
throw 'Selection must be an array with ids';
// first unselect any selected node
this._unselectAll(true);
this.unselectAll(true);
for (i = 0, iMax = selection.length; i < iMax; i++) {
id = selection[i];

+ 0
- 460
lib/network/modules/TouchEventHandler.js View File

@ -1,460 +0,0 @@
/**
* Created by Alex on 2/27/2015.
*
*/
import {SelectionHandler} from "./components/SelectionHandler"
var util = require('../../util');
class TouchEventHandler {
constructor(body) {
this.body = body;
this.body.eventListeners.onTap = this.onTap.bind(this);
this.body.eventListeners.onTouch = this.onTouch.bind(this);
this.body.eventListeners.onDoubleTap = this.onDoubleTap.bind(this);
this.body.eventListeners.onHold = this.onHold.bind(this);
this.body.eventListeners.onDragStart = this.onDragStart.bind(this);
this.body.eventListeners.onDrag = this.onDrag.bind(this);
this.body.eventListeners.onDragEnd = this.onDragEnd.bind(this);
this.body.eventListeners.onMouseWheel = this.onMouseWheel.bind(this);
this.body.eventListeners.onPinch = this.onPinch.bind(this);
this.body.eventListeners.onMouseMove = this.onMouseMove.bind(this);
this.body.eventListeners.onRelease = this.onRelease.bind(this);
this.touchTime = 0;
this.drag = {};
this.pinch = {};
this.pointerPosition = {x:0,y:0};
this.scale = 1.0;
this.body.emitter.on("_setScale", (scale) => this.scale = scale);
this.selectionHandler = new SelectionHandler(body);
}
setCanvas(canvas) {
this.canvas = canvas;
this.selectionHandler.setCanvas(canvas);
}
/**
* Get the pointer location from a touch location
* @param {{x: Number, y: Number}} touch
* @return {{x: Number, y: Number}} pointer
* @private
*/
getPointer(touch) {
return {
x: touch.x - util.getAbsoluteLeft(this.canvas.frame.canvas),
y: touch.y - util.getAbsoluteTop(this.canvas.frame.canvas)
};
}
/**
* On start of a touch gesture, store the pointer
* @param event
* @private
*/
onTouch(event) {
console.log('touch!')
if (new Date().valueOf() - this.touchTime > 100) {
this.drag.pointer = this.getPointer(event.center);
this.drag.pinched = false;
this.pinch.scale = this.scale;
// to avoid double fireing of this event because we have two hammer instances. (on canvas and on frame)
this.touchTime = new Date().valueOf();
}
}
/**
* handle tap/click event: select/unselect a node
* @private
*/
onTap(event) {
var pointer = this.getPointer(event.center);
this.pointerPosition = pointer;
this.selectionHandler.selectOnPoint(pointer);
}
/**
* handle drag start event
* @private
*/
/**
* This function is called by onDragStart.
* It is separated out because we can then overload it for the datamanipulation system.
*
* @private
*/
onDragStart(event) {
// in case the touch event was triggered on an external div, do the initial touch now.
//if (this.drag.pointer === undefined) {
// this.onTouch(event);
//}
//
//var node = this._getNodeAt(this.drag.pointer);
//// note: drag.pointer is set in onTouch to get the initial touch location
//
//this.drag.dragging = true;
//this.drag.selection = [];
//this.drag.translation = this._getTranslation();
//this.drag.nodeId = null;
//this.draggingNodes = false;
//
//if (node != null && this.constants.dragNodes == true) {
// this.draggingNodes = true;
// this.drag.nodeId = node.id;
// // select the clicked node if not yet selected
// if (!node.isSelected()) {
// this._selectObject(node, false);
// }
//
// this.emit("dragStart", {nodeIds: this.getSelection().nodes});
//
// // create an array with the selected nodes and their original location and status
// for (var objectId in this.selectionObj.nodes) {
// if (this.selectionObj.nodes.hasOwnProperty(objectId)) {
// var object = this.selectionObj.nodes[objectId];
// var s = {
// id: object.id,
// node: object,
//
// // store original x, y, xFixed and yFixed, make the node temporarily Fixed
// x: object.x,
// y: object.y,
// xFixed: object.xFixed,
// yFixed: object.yFixed
// };
//
// object.xFixed = true;
// object.yFixed = true;
//
// this.drag.selection.push(s);
// }
// }
//}
}
/**
* handle drag event
* @private
*/
onDrag(event) {
//if (this.drag.pinched) {
// return;
//}
//
//// remove the focus on node if it is focussed on by the focusOnNode
//this.releaseNode();
//
//var pointer = this.getPointer(event.gesture.center);
//var me = this;
//var drag = this.drag;
//var selection = drag.selection;
//if (selection && selection.length && this.constants.dragNodes == true) {
// // calculate delta's and new location
// var deltaX = pointer.x - drag.pointer.x;
// var deltaY = pointer.y - drag.pointer.y;
//
// // update position of all selected nodes
// selection.forEach(function (s) {
// var node = s.node;
//
// if (!s.xFixed) {
// node.x = me._XconvertDOMtoCanvas(me._XconvertCanvasToDOM(s.x) + deltaX);
// }
//
// if (!s.yFixed) {
// node.y = me._YconvertDOMtoCanvas(me._YconvertCanvasToDOM(s.y) + deltaY);
// }
// });
//
//
// // start _animationStep if not yet running
// if (!this.moving) {
// this.moving = true;
// this.start();
// }
//}
//else {
// // move the network
// if (this.constants.dragNetwork == true) {
// // if the drag was not started properly because the click started outside the network div, start it now.
// if (this.drag.pointer === undefined) {
// this._handleDragStart(event);
// return;
// }
// var diffX = pointer.x - this.drag.pointer.x;
// var diffY = pointer.y - this.drag.pointer.y;
//
// this._setTranslation(
// this.drag.translation.x + diffX,
// this.drag.translation.y + diffY
// );
// this._redraw();
// }
//}
}
/**
* handle drag start event
* @private
*/
onDragEnd(event) {
//this.drag.dragging = false;
//var selection = this.drag.selection;
//if (selection && selection.length) {
// selection.forEach(function (s) {
// // restore original xFixed and yFixed
// s.node.xFixed = s.xFixed;
// s.node.yFixed = s.yFixed;
// });
// this.moving = true;
// this.start();
//}
//else {
// this._redraw();
//}
//if (this.draggingNodes == false) {
// this.emit("dragEnd", {nodeIds: []});
//}
//else {
// this.emit("dragEnd", {nodeIds: this.getSelection().nodes});
//}
}
/**
* handle doubletap event
* @private
*/
onDoubleTap(event) {
//var pointer = this.getPointer(event.gesture.center);
//this._handleDoubleTap(pointer);
}
/**
* handle long tap event: multi select nodes
* @private
*/
onHold(event) {
//var pointer = this.getPointer(event.gesture.center);
//this.pointerPosition = pointer;
//this._handleOnHold(pointer);
}
/**
* handle the release of the screen
*
* @private
*/
onRelease(event) {
//var pointer = this.getPointer(event.gesture.center);
//this._handleOnRelease(pointer);
}
/**
* Handle pinch event
* @param event
* @private
*/
onPinch(event) {
//var pointer = this.getPointer(event.gesture.center);
//
//this.drag.pinched = true;
//if (!('scale' in this.pinch)) {
// this.pinch.scale = 1;
//}
//
//// TODO: enabled moving while pinching?
//var scale = this.pinch.scale * event.gesture.scale;
//this._zoom(scale, pointer)
}
/**
* Zoom the network in or out
* @param {Number} scale a number around 1, and between 0.01 and 10
* @param {{x: Number, y: Number}} pointer Position on screen
* @return {Number} appliedScale scale is limited within the boundaries
* @private
*/
_zoom(scale, pointer) {
//if (this.constants.zoomable == true) {
// var scaleOld = this._getScale();
// if (scale < 0.00001) {
// scale = 0.00001;
// }
// if (scale > 10) {
// scale = 10;
// }
//
// var preScaleDragPointer = null;
// if (this.drag !== undefined) {
// if (this.drag.dragging == true) {
// preScaleDragPointer = this.canvas.DOMtoCanvas(this.drag.pointer);
// }
// }
// // + this.canvas.frame.canvas.clientHeight / 2
// var translation = this._getTranslation();
//
// var scaleFrac = scale / scaleOld;
// var tx = (1 - scaleFrac) * pointer.x + translation.x * scaleFrac;
// var ty = (1 - scaleFrac) * pointer.y + translation.y * scaleFrac;
//
// this._setScale(scale);
// this._setTranslation(tx, ty);
//
// if (preScaleDragPointer != null) {
// var postScaleDragPointer = this.canvas.canvasToDOM(preScaleDragPointer);
// this.drag.pointer.x = postScaleDragPointer.x;
// this.drag.pointer.y = postScaleDragPointer.y;
// }
//
// this._redraw();
//
// if (scaleOld < scale) {
// this.emit("zoom", {direction: "+"});
// }
// else {
// this.emit("zoom", {direction: "-"});
// }
//
// return scale;
//}
}
/**
* Event handler for mouse wheel event, used to zoom the timeline
* See http://adomas.org/javascript-mouse-wheel/
* https://github.com/EightMedia/hammer.js/issues/256
* @param {MouseEvent} event
* @private
*/
onMouseWheel(event) {
//// retrieve delta
//var delta = 0;
//if (event.wheelDelta) { /* IE/Opera. */
// delta = event.wheelDelta / 120;
//} else if (event.detail) { /* Mozilla case. */
// // In Mozilla, sign of delta is different than in IE.
// // Also, delta is multiple of 3.
// delta = -event.detail / 3;
//}
//
//// If delta is nonzero, handle it.
//// Basically, delta is now positive if wheel was scrolled up,
//// and negative, if wheel was scrolled down.
//if (delta) {
//
// // calculate the new scale
// var scale = this._getScale();
// var zoom = delta / 10;
// if (delta < 0) {
// zoom = zoom / (1 - zoom);
// }
// scale *= (1 + zoom);
//
// // calculate the pointer location
// var gesture = hammerUtil.fakeGesture(this, event);
// var pointer = this.getPointer(gesture.center);
//
// // apply the new scale
// this._zoom(scale, pointer);
//}
//
//// Prevent default actions caused by mouse wheel.
//event.preventDefault();
}
/**
* Mouse move handler for checking whether the title moves over a node with a title.
* @param {Event} event
* @private
*/
onMouseMove(event) {
//var gesture = hammerUtil.fakeGesture(this, event);
//var pointer = this.getPointer(gesture.center);
//var popupVisible = false;
//
//// check if the previously selected node is still selected
//if (this.popup !== undefined) {
// if (this.popup.hidden === false) {
// this._checkHidePopup(pointer);
// }
//
// // if the popup was not hidden above
// if (this.popup.hidden === false) {
// popupVisible = true;
// this.popup.setPosition(pointer.x + 3, pointer.y - 5)
// this.popup.show();
// }
//}
//
//// if we bind the keyboard to the div, we have to highlight it to use it. This highlights it on mouse over
//if (this.constants.keyboard.bindToWindow == false && this.constants.keyboard.enabled == true) {
// this.canvas.frame.focus();
//}
//
//// start a timeout that will check if the mouse is positioned above an element
//if (popupVisible === false) {
// var me = this;
// var checkShow = function() {
// me._checkShowPopup(pointer);
// };
//
// if (this.popupTimer) {
// clearInterval(this.popupTimer); // stop any running calculationTimer
// }
// if (!this.drag.dragging) {
// this.popupTimer = setTimeout(checkShow, this.constants.tooltip.delay);
// }
//}
//
///**
// * Adding hover highlights
// */
//if (this.constants.hover == true) {
// // removing all hover highlights
// for (var edgeId in this.hoverObj.edges) {
// if (this.hoverObj.edges.hasOwnProperty(edgeId)) {
// this.hoverObj.edges[edgeId].hover = false;
// delete this.hoverObj.edges[edgeId];
// }
// }
//
// // adding hover highlights
// var obj = this._getNodeAt(pointer);
// if (obj == null) {
// obj = this._getEdgeAt(pointer);
// }
// if (obj != null) {
// this._hoverObject(obj);
// }
//
// // removing all node hover highlights except for the selected one.
// for (var nodeId in this.hoverObj.nodes) {
// if (this.hoverObj.nodes.hasOwnProperty(nodeId)) {
// if (obj instanceof Node && obj.id != nodeId || obj instanceof Edge || obj == null) {
// this._blurObject(this.hoverObj.nodes[nodeId]);
// delete this.hoverObj.nodes[nodeId];
// }
// }
// }
// this.redraw();
//}
}
}
export {TouchEventHandler};

+ 17
- 25
lib/network/modules/View.js View File

@ -5,9 +5,9 @@
var util = require('../../util');
class View {
constructor(body, options) {
constructor(body, canvas) {
this.body = body;
this.setOptions(options);
this.canvas = canvas;
this.animationSpeed = 1/this.renderRefreshRate;
this.animationEasingFunction = "easeInOutQuint";
@ -20,14 +20,9 @@ class View {
this.lockedOnNodeOffset = null;
this.touchTime = 0;
this.translation = {x: 0, y: 0};
this.scale = 1.0;
this.viewFunction = undefined;
this.body.emitter.on("zoomExtent", this.zoomExtent.bind(this));
this.body.emitter.on("_setScale", (scale) => this.scale = scale);
this.body.emitter.on("_setTranslation", (translation) => {this.translation.x = translation.x; this.translation.y = translation.y;});
this.body.emitter.on("animationFinished", () => {this.body.emitter.emit("_stopRendering");});
this.body.emitter.on("unlockNode", this.releaseNode.bind(this));
}
@ -37,9 +32,6 @@ class View {
this.options = options;
}
setCanvas(canvas) {
this.canvas = canvas;
}
// zoomExtent
/**
@ -144,7 +136,7 @@ class View {
zoomLevel *= factor;
}
else {
this.body.emitter.emit("_redrawHidden");
this.body.emitter.emit("_redraw", true);
range = this._getRange(options.nodes);
var xDistance = Math.abs(range.maxX - range.minX) * 1.1;
var yDistance = Math.abs(range.maxY - range.minY) * 1.1;
@ -200,8 +192,8 @@ class View {
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.scale; }
if (options.position === undefined) {options.position = this.translation;}
if (options.scale === undefined) {options.scale = this.body.view.scale; }
if (options.position === undefined) {options.position = this.body.view.translation;}
if (options.animation === undefined) {options.animation = {duration:0}; }
if (options.animation === false ) {options.animation = {duration:0}; }
if (options.animation === true ) {options.animation = {}; }
@ -239,13 +231,13 @@ class View {
this._transitionRedraw(true); // by setting easingtime to 1, we finish the animation.
}
this.sourceScale = this.scale;
this.sourceTranslation = this.translation;
this.sourceScale = this.body.view.scale;
this.sourceTranslation = this.body.view.translation;
this.targetScale = options.scale;
// 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.body.emitter.emit("_setScale",this.targetScale);
this.body.view.scale = this.targetScale;
var viewCenter = this.canvas.DOMtoCanvas({x: 0.5 * this.canvas.frame.canvas.clientWidth, y: 0.5 * this.canvas.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,
@ -263,8 +255,8 @@ class View {
this.body.emitter.on("_beforeRender", this.viewFunction);
}
else {
this.body.emitter.emit("_setScale", this.targetScale);;
this.body.emitter.emit("_setTranslation", this.targetTranslation);
this.body.view.scale = this.targetScale;
this.body.view.translation = this.targetTranslation;
this.body.emitter.emit("_requestRedraw");
}
}
@ -290,13 +282,13 @@ class View {
x: viewCenter.x - nodePosition.x,
y: viewCenter.y - nodePosition.y
};
var sourceTranslation = this.translation;
var sourceTranslation = this.body.view.translation;
var targetTranslation = {
x: sourceTranslation.x + distanceFromCenter.x * this.scale + this.lockedOnNodeOffset.x,
y: sourceTranslation.y + distanceFromCenter.y * this.scale + this.lockedOnNodeOffset.y
x: sourceTranslation.x + distanceFromCenter.x * this.body.view.scale + this.lockedOnNodeOffset.x,
y: sourceTranslation.y + distanceFromCenter.y * this.body.view.scale + this.lockedOnNodeOffset.y
};
this.body.emitter.emit("_setTranslation", targetTranslation);
this.body.view.translation = targetTranslation;
}
releaseNode() {
@ -318,11 +310,11 @@ class View {
var progress = util.easingFunctions[this.animationEasingFunction](this.easingTime);
this.body.emitter.emit("_setScale", this.sourceScale + (this.targetScale - this.sourceScale) * progress);
this.body.emitter.emit("_setTranslation", {
this.body.view.scale = this.sourceScale + (this.targetScale - this.sourceScale) * progress;
this.body.view.translation = {
x: this.sourceTranslation.x + (this.targetTranslation.x - this.sourceTranslation.x) * progress,
y: this.sourceTranslation.y + (this.targetTranslation.y - this.sourceTranslation.y) * progress
});
};
// cleanup
if (this.easingTime >= 1.0) {

+ 0
- 1
lib/util.js View File

@ -114,7 +114,6 @@ exports.extend = function (a, b) {
}
}
}
return a;
};

Loading…
Cancel
Save