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
7.8 KiB

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