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.

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