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.

295 lines
8.1 KiB

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