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.

286 lines
7.7 KiB

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