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.

279 lines
9.7 KiB

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