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

10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 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;