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.

220 lines
7.8 KiB

  1. var Hammer = require('../../module/hammer');
  2. var hammerUtil = require('../../hammerUtil');
  3. class Canvas {
  4. /**
  5. * Create the main frame for the Network.
  6. * This function is executed once when a Network object is created. The frame
  7. * contains a canvas, and this canvas contains all objects like the axis and
  8. * nodes.
  9. * @private
  10. */
  11. constructor(body, options) {
  12. this.body = body;
  13. this.setOptions(options);
  14. this.translation = {x: 0, y: 0};
  15. this.scale = 1.0;
  16. this.body.emitter.on("_setScale", (scale) => {this.scale = scale});
  17. this.body.emitter.on("_setTranslation", (translation) => {this.translation.x = translation.x; this.translation.y = translation.y;});
  18. 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)});
  19. this.pixelRatio = 1;
  20. // remove all elements from the container element.
  21. while (this.body.container.hasChildNodes()) {
  22. this.body.container.removeChild(this.body.container.firstChild);
  23. }
  24. this.frame = document.createElement('div');
  25. this.frame.className = 'vis network-frame';
  26. this.frame.style.position = 'relative';
  27. this.frame.style.overflow = 'hidden';
  28. this.frame.tabIndex = 900;
  29. //////////////////////////////////////////////////////////////////
  30. this.frame.canvas = document.createElement("canvas");
  31. this.frame.canvas.style.position = 'relative';
  32. this.frame.appendChild(this.frame.canvas);
  33. if (!this.frame.canvas.getContext) {
  34. var noCanvas = document.createElement( 'DIV' );
  35. noCanvas.style.color = 'red';
  36. noCanvas.style.fontWeight = 'bold' ;
  37. noCanvas.style.padding = '10px';
  38. noCanvas.innerHTML = 'Error: your browser does not support HTML canvas';
  39. this.frame.canvas.appendChild(noCanvas);
  40. }
  41. else {
  42. var ctx = this.frame.canvas.getContext("2d");
  43. this.pixelRatio = (window.devicePixelRatio || 1) / (ctx.webkitBackingStorePixelRatio ||
  44. ctx.mozBackingStorePixelRatio ||
  45. ctx.msBackingStorePixelRatio ||
  46. ctx.oBackingStorePixelRatio ||
  47. ctx.backingStorePixelRatio || 1);
  48. //this.pixelRatio = Math.max(1,this.pixelRatio); // this is to account for browser zooming out. The pixel ratio is ment to switch between 1 and 2 for HD screens.
  49. this.frame.canvas.getContext("2d").setTransform(this.pixelRatio, 0, 0, this.pixelRatio, 0, 0);
  50. }
  51. // add the frame to the container element
  52. this.body.container.appendChild(this.frame);
  53. this.body.emitter.emit("_setScale", 1);;
  54. this.body.emitter.emit("_setTranslation", {x: 0.5 * this.frame.canvas.clientWidth,y: 0.5 * this.frame.canvas.clientHeight});;
  55. this._bindHammer();
  56. }
  57. /**
  58. * This function binds hammer, it can be repeated over and over due to the uniqueness check.
  59. * @private
  60. */
  61. _bindHammer() {
  62. var me = this;
  63. if (this.hammer !== undefined) {
  64. this.hammer.destroy();
  65. }
  66. this.drag = {};
  67. this.pinch = {};
  68. this.hammer = new Hammer(this.frame.canvas);
  69. this.hammer.on('tap', me.body.eventListeners.onTap );
  70. this.hammer.on('doubletap', me.body.eventListeners.onDoubleTap );
  71. this.hammer.on('press', me.body.eventListeners.onHold );
  72. hammerUtil.onTouch(this.hammer, me.body.eventListeners.onTouch );
  73. this.hammer.on('panstart', me.body.eventListeners.onDragStart );
  74. this.hammer.on('panmove', me.body.eventListeners.onDrag );
  75. this.hammer.on('panend', me.body.eventListeners.onDragEnd );
  76. this.hammer.on('pinch', me.body.eventListeners.onPinch.bind(me) );
  77. // TODO: neatly cleanup these handlers when re-creating the Canvas
  78. if (this.options.zoomable == true) {
  79. this.frame.canvas.addEventListener('mousewheel', me.body.eventListeners.onMouseWheel.bind(me));
  80. this.frame.canvas.addEventListener('DOMMouseScroll', me.body.eventListeners.onMouseWheel.bind(me));
  81. }
  82. this.frame.canvas.addEventListener('mousemove', me.body.eventListeners.onMouseMove.bind(me));
  83. this.hammerFrame = new Hammer(this.frame);
  84. hammerUtil.onRelease(this.hammerFrame, me.body.eventListeners.onRelease.bind(me) );
  85. }
  86. setOptions(options = {}) {
  87. this.options = options;
  88. }
  89. /**
  90. * Set a new size for the network
  91. * @param {string} width Width in pixels or percentage (for example '800px'
  92. * or '50%')
  93. * @param {string} height Height in pixels or percentage (for example '400px'
  94. * or '30%')
  95. */
  96. setSize(width, height) {
  97. var emitEvent = false;
  98. var oldWidth = this.frame.canvas.width;
  99. var oldHeight = this.frame.canvas.height;
  100. if (width != this.options.width || height != this.options.height || this.frame.style.width != width || this.frame.style.height != height) {
  101. this.frame.style.width = width;
  102. this.frame.style.height = height;
  103. this.frame.canvas.style.width = '100%';
  104. this.frame.canvas.style.height = '100%';
  105. this.frame.canvas.width = this.frame.canvas.clientWidth * this.pixelRatio;
  106. this.frame.canvas.height = this.frame.canvas.clientHeight * this.pixelRatio;
  107. this.options.width = width;
  108. this.options.height = height;
  109. emitEvent = true;
  110. }
  111. else {
  112. // this would adapt the width of the canvas to the width from 100% if and only if
  113. // there is a change.
  114. if (this.frame.canvas.width != this.frame.canvas.clientWidth * this.pixelRatio) {
  115. this.frame.canvas.width = this.frame.canvas.clientWidth * this.pixelRatio;
  116. emitEvent = true;
  117. }
  118. if (this.frame.canvas.height != this.frame.canvas.clientHeight * this.pixelRatio) {
  119. this.frame.canvas.height = this.frame.canvas.clientHeight * this.pixelRatio;
  120. emitEvent = true;
  121. }
  122. }
  123. if (emitEvent === true) {
  124. this.body.emitter.emit('resize', {width:this.frame.canvas.width * this.pixelRatio,height:this.frame.canvas.height * this.pixelRatio, oldWidth: oldWidth * this.pixelRatio, oldHeight: oldHeight * this.pixelRatio});
  125. }
  126. };
  127. /**
  128. * Convert the X coordinate in DOM-space (coordinate point in browser relative to the container div) to
  129. * the X coordinate in canvas-space (the simulation sandbox, which the camera looks upon)
  130. * @param {number} x
  131. * @returns {number}
  132. * @private
  133. */
  134. _XconvertDOMtoCanvas(x) {
  135. return (x - this.translation.x) / this.scale;
  136. }
  137. /**
  138. * Convert the X coordinate in canvas-space (the simulation sandbox, which the camera looks upon) to
  139. * the X coordinate in DOM-space (coordinate point in browser relative to the container div)
  140. * @param {number} x
  141. * @returns {number}
  142. * @private
  143. */
  144. _XconvertCanvasToDOM(x) {
  145. return x * this.scale + this.translation.x;
  146. }
  147. /**
  148. * Convert the Y coordinate in DOM-space (coordinate point in browser relative to the container div) to
  149. * the Y coordinate in canvas-space (the simulation sandbox, which the camera looks upon)
  150. * @param {number} y
  151. * @returns {number}
  152. * @private
  153. */
  154. _YconvertDOMtoCanvas(y) {
  155. return (y - this.translation.y) / this.scale;
  156. }
  157. /**
  158. * Convert the Y coordinate in canvas-space (the simulation sandbox, which the camera looks upon) to
  159. * the Y coordinate in DOM-space (coordinate point in browser relative to the container div)
  160. * @param {number} y
  161. * @returns {number}
  162. * @private
  163. */
  164. _YconvertCanvasToDOM(y) {
  165. return y * this.scale + this.translation.y ;
  166. }
  167. /**
  168. *
  169. * @param {object} pos = {x: number, y: number}
  170. * @returns {{x: number, y: number}}
  171. * @constructor
  172. */
  173. canvasToDOM (pos) {
  174. return {x: this._XconvertCanvasToDOM(pos.x), y: this._YconvertCanvasToDOM(pos.y)};
  175. }
  176. /**
  177. *
  178. * @param {object} pos = {x: number, y: number}
  179. * @returns {{x: number, y: number}}
  180. * @constructor
  181. */
  182. DOMtoCanvas (pos) {
  183. return {x: this._XconvertDOMtoCanvas(pos.x), y: this._YconvertDOMtoCanvas(pos.y)};
  184. }
  185. }
  186. export {Canvas};