vis.js is a dynamic, browser-based visualization library
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

228 lines
9.2 KiB

9 years ago
9 years ago
  1. var util = require('../../../util');
  2. var Hammer = require('../../../module/hammer');
  3. var hammerUtil = require('../../../hammerUtil');
  4. var keycharm = require('keycharm');
  5. class NavigationHandler {
  6. constructor(body, canvas) {
  7. this.body = body;
  8. this.canvas = canvas;
  9. this.iconsCreated = false;
  10. this.navigationHammers = [];
  11. this.boundFunctions = {};
  12. this.touchTime = 0;
  13. this.activated = false;
  14. this.body.emitter.on("activate", () => {this.activated = true; this.configureKeyboardBindings();});
  15. this.body.emitter.on("deactivate", () => {this.activated = false; this.configureKeyboardBindings();});
  16. this.body.emitter.on("destroy", () => {if (this.keycharm !== undefined) {this.keycharm.destroy();}});
  17. this.options = {}
  18. }
  19. setOptions(options) {
  20. if (options !== undefined) {
  21. this.options = options;
  22. this.create();
  23. }
  24. }
  25. create() {
  26. if (this.options.navigationButtons === true) {
  27. if (this.iconsCreated === false) {
  28. this.loadNavigationElements();
  29. }
  30. }
  31. else if (this.iconsCreated === true) {
  32. this.cleanNavigation();
  33. }
  34. this.configureKeyboardBindings();
  35. }
  36. cleanNavigation() {
  37. // clean hammer bindings
  38. if (this.navigationHammers.length != 0) {
  39. for (var i = 0; i < this.navigationHammers.length; i++) {
  40. this.navigationHammers[i].destroy();
  41. }
  42. this.navigationHammers = [];
  43. }
  44. // clean up previous navigation items
  45. if (this.navigationDOM && this.navigationDOM['wrapper'] && this.navigationDOM['wrapper'].parentNode) {
  46. this.navigationDOM['wrapper'].parentNode.removeChild(this.navigationDOM['wrapper']);
  47. }
  48. this.iconsCreated = false;
  49. }
  50. /**
  51. * Creation of the navigation controls nodes. They are drawn over the rest of the nodes and are not affected by scale and translation
  52. * they have a triggerFunction which is called on click. If the position of the navigation controls is dependent
  53. * on this.frame.canvas.clientWidth or this.frame.canvas.clientHeight, we flag horizontalAlignLeft and verticalAlignTop false.
  54. * This means that the location will be corrected by the _relocateNavigation function on a size change of the canvas.
  55. *
  56. * @private
  57. */
  58. loadNavigationElements() {
  59. this.cleanNavigation();
  60. this.navigationDOM = {};
  61. var navigationDivs = ['up','down','left','right','zoomIn','zoomOut','zoomExtends'];
  62. var navigationDivActions = ['_moveUp','_moveDown','_moveLeft','_moveRight','_zoomIn','_zoomOut','_fit'];
  63. this.navigationDOM['wrapper'] = document.createElement('div');
  64. this.navigationDOM['wrapper'].className = 'vis-navigation';
  65. this.canvas.frame.appendChild(this.navigationDOM['wrapper']);
  66. for (var i = 0; i < navigationDivs.length; i++) {
  67. this.navigationDOM[navigationDivs[i]] = document.createElement('div');
  68. this.navigationDOM[navigationDivs[i]].className = 'vis-button vis-' + navigationDivs[i];
  69. this.navigationDOM['wrapper'].appendChild(this.navigationDOM[navigationDivs[i]]);
  70. var hammer = new Hammer(this.navigationDOM[navigationDivs[i]]);
  71. if (navigationDivActions[i] === "_fit") {
  72. hammerUtil.onTouch(hammer, this._fit.bind(this));
  73. }
  74. else {
  75. hammerUtil.onTouch(hammer, this.bindToRedraw.bind(this,navigationDivActions[i]));
  76. }
  77. this.navigationHammers.push(hammer);
  78. }
  79. // use a hammer for the release so we do not require the one used in the rest of the network
  80. // the one the rest uses can be overloaded by the manipulation system.
  81. var hammerFrame = new Hammer(this.canvas.frame);
  82. hammerUtil.onRelease(hammerFrame, () => {this._stopMovement();});
  83. this.navigationHammers.push(hammerFrame);
  84. this.iconsCreated = true;
  85. }
  86. bindToRedraw(action) {
  87. if (this.boundFunctions[action] === undefined) {
  88. this.boundFunctions[action] = this[action].bind(this);
  89. this.body.emitter.on("initRedraw", this.boundFunctions[action]);
  90. this.body.emitter.emit("_startRendering");
  91. }
  92. }
  93. unbindFromRedraw(action) {
  94. if (this.boundFunctions[action] !== undefined) {
  95. this.body.emitter.off("initRedraw", this.boundFunctions[action]);
  96. this.body.emitter.emit("_stopRendering");
  97. delete this.boundFunctions[action];
  98. }
  99. }
  100. /**
  101. * this stops all movement induced by the navigation buttons
  102. *
  103. * @private
  104. */
  105. _fit() {
  106. if (new Date().valueOf() - this.touchTime > 700) { // TODO: fix ugly hack to avoid hammer's double fireing of event (because we use release?)
  107. this.body.emitter.emit("fit", {duration: 700});
  108. this.touchTime = new Date().valueOf();
  109. }
  110. }
  111. /**
  112. * this stops all movement induced by the navigation buttons
  113. *
  114. * @private
  115. */
  116. _stopMovement() {
  117. for (let boundAction in this.boundFunctions) {
  118. if (this.boundFunctions.hasOwnProperty(boundAction)) {
  119. this.body.emitter.off("initRedraw", this.boundFunctions[boundAction]);
  120. this.body.emitter.emit("_stopRendering");
  121. }
  122. }
  123. this.boundFunctions = {};
  124. }
  125. _moveUp() {this.body.view.translation.y += this.options.keyboard.speed.y;}
  126. _moveDown() {this.body.view.translation.y -= this.options.keyboard.speed.y;}
  127. _moveLeft() {this.body.view.translation.x += this.options.keyboard.speed.x;}
  128. _moveRight(){this.body.view.translation.x -= this.options.keyboard.speed.x;}
  129. _zoomIn() {
  130. var scaleOld = this.body.view.scale;
  131. var scale = this.body.view.scale * (1 + this.options.keyboard.speed.zoom);
  132. var translation = this.body.view.translation;
  133. var scaleFrac = scale / scaleOld;
  134. var tx = (1 - scaleFrac) * this.canvas.canvasViewCenter.x + translation.x * scaleFrac;
  135. var ty = (1 - scaleFrac) * this.canvas.canvasViewCenter.y + translation.y * scaleFrac;
  136. this.body.view.scale = scale;
  137. this.body.view.translation = { x: tx, y: ty };
  138. this.body.emitter.emit('zoom', { direction: '+', scale: this.body.view.scale, pointer: pointer });
  139. }
  140. _zoomOut() {
  141. var scaleOld = this.body.view.scale;
  142. var scale = this.body.view.scale / (1 + this.options.keyboard.speed.zoom);
  143. var translation = this.body.view.translation;
  144. var scaleFrac = scale / scaleOld;
  145. var tx = (1 - scaleFrac) * this.canvas.canvasViewCenter.x + translation.x * scaleFrac;
  146. var ty = (1 - scaleFrac) * this.canvas.canvasViewCenter.y + translation.y * scaleFrac;
  147. this.body.view.scale = scale;
  148. this.body.view.translation = { x: tx, y: ty };
  149. this.body.emitter.emit('zoom', { direction: '-', scale: this.body.view.scale, pointer: pointer });
  150. }
  151. /**
  152. * bind all keys using keycharm.
  153. */
  154. configureKeyboardBindings() {
  155. if (this.keycharm !== undefined) {
  156. this.keycharm.destroy();
  157. }
  158. if (this.options.keyboard.enabled === true) {
  159. if (this.options.keyboard.bindToWindow === true) {
  160. this.keycharm = keycharm({container: window, preventDefault: true});
  161. }
  162. else {
  163. this.keycharm = keycharm({container: this.canvas.frame, preventDefault: true});
  164. }
  165. this.keycharm.reset();
  166. if (this.activated === true) {
  167. this.keycharm.bind("up", () => {this.bindToRedraw("_moveUp") ;}, "keydown");
  168. this.keycharm.bind("down", () => {this.bindToRedraw("_moveDown") ;}, "keydown");
  169. this.keycharm.bind("left", () => {this.bindToRedraw("_moveLeft") ;}, "keydown");
  170. this.keycharm.bind("right", () => {this.bindToRedraw("_moveRight");}, "keydown");
  171. this.keycharm.bind("=", () => {this.bindToRedraw("_zoomIn") ;}, "keydown");
  172. this.keycharm.bind("num+", () => {this.bindToRedraw("_zoomIn") ;}, "keydown");
  173. this.keycharm.bind("num-", () => {this.bindToRedraw("_zoomOut") ;}, "keydown");
  174. this.keycharm.bind("-", () => {this.bindToRedraw("_zoomOut") ;}, "keydown");
  175. this.keycharm.bind("[", () => {this.bindToRedraw("_zoomOut") ;}, "keydown");
  176. this.keycharm.bind("]", () => {this.bindToRedraw("_zoomIn") ;}, "keydown");
  177. this.keycharm.bind("pageup", () => {this.bindToRedraw("_zoomIn") ;}, "keydown");
  178. this.keycharm.bind("pagedown", () => {this.bindToRedraw("_zoomOut") ;}, "keydown");
  179. this.keycharm.bind("up", () => {this.unbindFromRedraw("_moveUp") ;}, "keyup");
  180. this.keycharm.bind("down", () => {this.unbindFromRedraw("_moveDown") ;}, "keyup");
  181. this.keycharm.bind("left", () => {this.unbindFromRedraw("_moveLeft") ;}, "keyup");
  182. this.keycharm.bind("right", () => {this.unbindFromRedraw("_moveRight");}, "keyup");
  183. this.keycharm.bind("=", () => {this.unbindFromRedraw("_zoomIn") ;}, "keyup");
  184. this.keycharm.bind("num+", () => {this.unbindFromRedraw("_zoomIn") ;}, "keyup");
  185. this.keycharm.bind("num-", () => {this.unbindFromRedraw("_zoomOut") ;}, "keyup");
  186. this.keycharm.bind("-", () => {this.unbindFromRedraw("_zoomOut") ;}, "keyup");
  187. this.keycharm.bind("[", () => {this.unbindFromRedraw("_zoomOut") ;}, "keyup");
  188. this.keycharm.bind("]", () => {this.unbindFromRedraw("_zoomIn") ;}, "keyup");
  189. this.keycharm.bind("pageup", () => {this.unbindFromRedraw("_zoomIn") ;}, "keyup");
  190. this.keycharm.bind("pagedown", () => {this.unbindFromRedraw("_zoomOut") ;}, "keyup");
  191. }
  192. }
  193. }
  194. }
  195. export default NavigationHandler;