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.

282 lines
7.6 KiB

  1. if (typeof window !== 'undefined') {
  2. window.requestAnimationFrame = window.requestAnimationFrame || window.mozRequestAnimationFrame ||
  3. window.webkitRequestAnimationFrame || window.msRequestAnimationFrame;
  4. }
  5. var util = require('../../util');
  6. class CanvasRenderer {
  7. constructor(body, canvas) {
  8. this.body = body;
  9. this.canvas = canvas;
  10. this.redrawRequested = false;
  11. this.renderTimer = false;
  12. this.requiresTimeout = true;
  13. this.renderingActive = false;
  14. this.renderRequests = 0;
  15. this.pixelRatio = undefined;
  16. this.allowRedrawRequests = true;
  17. this.dragging = false;
  18. this.body.emitter.on("dragStart", () => {
  19. this.dragging = true;
  20. });
  21. this.body.emitter.on("dragEnd", () => this.dragging = false);
  22. this.body.emitter.on("_redraw", () => {
  23. if (this.renderingActive === false) {
  24. this._redraw();
  25. }
  26. });
  27. this.body.emitter.on("_blockRedrawRequests", () => {this.allowRedrawRequests = false;});
  28. this.body.emitter.on("_allowRedrawRequests", () => {this.allowRedrawRequests = true; });
  29. this.body.emitter.on("_requestRedraw", this._requestRedraw.bind(this));
  30. this.body.emitter.on("_startRendering", () => {
  31. this.renderRequests += 1;
  32. this.renderingActive = true;
  33. this._startRendering();
  34. });
  35. this.body.emitter.on("_stopRendering", () => {
  36. this.renderRequests -= 1;
  37. this.renderingActive = this.renderRequests > 0;
  38. });
  39. this.options = {};
  40. this.defaultOptions = {
  41. hideEdgesOnDrag: false,
  42. hideNodesOnDrag: false
  43. }
  44. util.extend(this.options, this.defaultOptions);
  45. this._determineBrowserMethod();
  46. }
  47. setOptions(options) {
  48. if (options !== undefined) {
  49. util.deepExtend(this.options, options);
  50. }
  51. }
  52. _startRendering() {
  53. if (this.renderingActive === true) {
  54. if (!this.renderTimer) {
  55. if (this.requiresTimeout === true) {
  56. this.renderTimer = window.setTimeout(this._renderStep.bind(this), this.simulationInterval); // wait this.renderTimeStep milliseconds and perform the animation step function
  57. }
  58. else {
  59. this.renderTimer = window.requestAnimationFrame(this._renderStep.bind(this)); // wait this.renderTimeStep milliseconds and perform the animation step function
  60. }
  61. }
  62. }
  63. else {
  64. }
  65. }
  66. _renderStep() {
  67. // reset the renderTimer so a new scheduled animation step can be set
  68. this.renderTimer = undefined;
  69. if (this.requiresTimeout === true) {
  70. // this schedules a new simulation step
  71. this._startRendering();
  72. }
  73. this._redraw();
  74. if (this.requiresTimeout === false) {
  75. // this schedules a new simulation step
  76. this._startRendering();
  77. }
  78. }
  79. /**
  80. * Redraw the network with the current data
  81. * chart will be resized too.
  82. */
  83. redraw() {
  84. this._redraw();
  85. }
  86. /**
  87. * Redraw the network with the current data
  88. * @param hidden | used to get the first estimate of the node sizes. only the nodes are drawn after which they are quickly drawn over.
  89. * @private
  90. */
  91. _requestRedraw() {
  92. if (this.redrawRequested !== true && this.renderingActive === false && this.allowRedrawRequests === true) {
  93. this.redrawRequested = true;
  94. if (this.requiresTimeout === true) {
  95. window.setTimeout(this._redraw.bind(this, false), 0);
  96. }
  97. else {
  98. window.requestAnimationFrame(this._redraw.bind(this, false));
  99. }
  100. }
  101. }
  102. _redraw(hidden = false) {
  103. this.body.emitter.emit("initRedraw");
  104. this.redrawRequested = false;
  105. var ctx = this.canvas.frame.canvas.getContext('2d');
  106. // when the container div was hidden, this fixes it back up!
  107. if (this.canvas.frame.canvas.width === 0 || this.canvas.frame.canvas.height === 0) {
  108. this.canvas.setSize();
  109. }
  110. if (this.pixelRation === undefined) {
  111. this.pixelRatio = (window.devicePixelRatio || 1) / (ctx.webkitBackingStorePixelRatio ||
  112. ctx.mozBackingStorePixelRatio ||
  113. ctx.msBackingStorePixelRatio ||
  114. ctx.oBackingStorePixelRatio ||
  115. ctx.backingStorePixelRatio || 1);
  116. }
  117. ctx.setTransform(this.pixelRatio, 0, 0, this.pixelRatio, 0, 0);
  118. // clear the canvas
  119. var w = this.canvas.frame.canvas.clientWidth;
  120. var h = this.canvas.frame.canvas.clientHeight;
  121. ctx.clearRect(0, 0, w, h);
  122. this.body.emitter.emit("beforeDrawing", ctx);
  123. // set scaling and translation
  124. ctx.save();
  125. ctx.translate(this.body.view.translation.x, this.body.view.translation.y);
  126. ctx.scale(this.body.view.scale, this.body.view.scale);
  127. if (hidden === false) {
  128. if (this.dragging === false || (this.dragging === true && this.options.hideEdgesOnDrag === false)) {
  129. this._drawEdges(ctx);
  130. }
  131. }
  132. if (this.dragging === false || (this.dragging === true && this.options.hideNodesOnDrag === false)) {
  133. this._drawNodes(ctx, hidden);
  134. }
  135. if (this.controlNodesActive === true) {
  136. this._drawControlNodes(ctx);
  137. }
  138. //this.physics.nodesSolver._debug(ctx,"#F00F0F");
  139. this.body.emitter.emit("afterDrawing", ctx);
  140. // restore original scaling and translation
  141. ctx.restore();
  142. if (hidden === true) {
  143. ctx.clearRect(0, 0, w, h);
  144. }
  145. }
  146. /**
  147. * Redraw all nodes
  148. * The 2d context of a HTML canvas can be retrieved by canvas.getContext('2d');
  149. * @param {CanvasRenderingContext2D} ctx
  150. * @param {Boolean} [alwaysShow]
  151. * @private
  152. */
  153. _drawNodes(ctx, alwaysShow = false) {
  154. var nodes = this.body.nodes;
  155. var nodeIndices = this.body.nodeIndices;
  156. var node;
  157. var selected = [];
  158. // draw unselected nodes;
  159. for (let i = 0; i < nodeIndices.length; i++) {
  160. node = nodes[nodeIndices[i]];
  161. // set selected nodes aside
  162. if (node.isSelected()) {
  163. selected.push(nodeIndices[i]);
  164. }
  165. else {
  166. if (alwaysShow === true) {
  167. node.draw(ctx);
  168. }
  169. // todo: replace check
  170. //else if (node.inArea() === true) {
  171. node.draw(ctx);
  172. //}
  173. }
  174. }
  175. // draw the selected nodes on top
  176. for (let i = 0; i < selected.length; i++) {
  177. node = nodes[selected[i]];
  178. node.draw(ctx);
  179. }
  180. }
  181. /**
  182. * Redraw all edges
  183. * The 2d context of a HTML canvas can be retrieved by canvas.getContext('2d');
  184. * @param {CanvasRenderingContext2D} ctx
  185. * @private
  186. */
  187. _drawEdges(ctx) {
  188. var edges = this.body.edges;
  189. var edgeIndices = this.body.edgeIndices;
  190. var edge;
  191. for (let i = 0; i < edgeIndices.length; i++) {
  192. edge = edges[edgeIndices[i]];
  193. if (edge.connected === true) {
  194. edge.draw(ctx);
  195. }
  196. }
  197. }
  198. /**
  199. * Redraw all edges
  200. * The 2d context of a HTML canvas can be retrieved by canvas.getContext('2d');
  201. * @param {CanvasRenderingContext2D} ctx
  202. * @private
  203. */
  204. _drawControlNodes(ctx) {
  205. var edges = this.body.edges;
  206. var edgeIndices = this.body.edgeIndices;
  207. var edge;
  208. for (let i = 0; i < edgeIndices.length; i++) {
  209. edge = edges[edgeIndices[i]];
  210. edge._drawControlNodes(ctx);
  211. }
  212. }
  213. /**
  214. * Determine if the browser requires a setTimeout or a requestAnimationFrame. This was required because
  215. * some implementations (safari and IE9) did not support requestAnimationFrame
  216. * @private
  217. */
  218. _determineBrowserMethod() {
  219. if (typeof window !== 'undefined') {
  220. var browserType = navigator.userAgent.toLowerCase();
  221. this.requiresTimeout = false;
  222. if (browserType.indexOf('msie 9.0') != -1) { // IE 9
  223. this.requiresTimeout = true;
  224. }
  225. else if (browserType.indexOf('safari') != -1) { // safari
  226. if (browserType.indexOf('chrome') <= -1) {
  227. this.requiresTimeout = true;
  228. }
  229. }
  230. }
  231. else {
  232. this.requiresTimeout = true;
  233. }
  234. }
  235. }
  236. export default CanvasRenderer;