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.

186 lines
6.1 KiB

  1. let util = require('../../../../util');
  2. /**
  3. * Created by Alex on 3/17/2015.
  4. */
  5. class Label {
  6. constructor(body,options) {
  7. this.body = body;
  8. this.setOptions(options);
  9. this.size = {top: 0, left: 0, width: 0, height: 0, yLine: 0}; // could be cached
  10. }
  11. setOptions(options) {
  12. this.options = options;
  13. if (options.label !== undefined) {
  14. this.labelDirty = true;
  15. }
  16. }
  17. draw(ctx, x, y, selected, baseline = 'middle') {
  18. if (this.options.label !== undefined) {
  19. // check if we have to render the label
  20. let relativeFontSize = Number(this.options.font.size) * this.body.view.scale;
  21. if (this.options.label && relativeFontSize >= this.options.scaling.label.drawThreshold - 1) {
  22. // this ensures that there will not be HUGE letters on screen by setting an upper limit on the visible text size (regardless of zoomLevel)
  23. let fontSize = Number(this.options.font.size);
  24. if (relativeFontSize >= this.options.scaling.label.maxVisible) {
  25. fontSize = Number(this.options.scaling.label.maxVisible) / this.body.view.scale;
  26. }
  27. // notify the canvas of the fontsize and thickness
  28. ctx.font = (selected ? "bold " : "") + fontSize + "px " + this.options.font.face;
  29. // update the size cache if required
  30. if (this.labelDirty == true) {
  31. this.calculateLabelSize(ctx, selected, x, y, baseline);
  32. }
  33. // create some of the local variables
  34. let yLine = this.size.yLine;
  35. let lines = String(this.options.label).split('\n');
  36. let lineCount = lines.length;
  37. // create the fontfill background
  38. this._drawLabelRect(ctx);
  39. // draw text
  40. this._drawLabelText(ctx, x, yLine, lines, lineCount, fontSize, baseline, relativeFontSize);
  41. }
  42. }
  43. }
  44. getTextSize(ctx, selected) {
  45. if (this.options.label !== undefined) {
  46. this._calculateLabelSize(ctx,selected);
  47. }
  48. else {
  49. this.size = {top: 0, left: 0, width: 0, height: 0, yLine: 0};
  50. }
  51. return this.size;
  52. }
  53. _calculateLabelSize(ctx,selected,x,y,baseline) {
  54. ctx.font = (selected ? "bold " : "") + this.options.font.size + "px " + this.options.font.face;
  55. let lines = String(this.options.label).split('\n');
  56. let lineCount = lines.length;
  57. let yLine = y + (1 - lineCount) * 0.5 * this.options.font.size;
  58. let width = ctx.measureText(lines[0]).width;
  59. for (let i = 1; i < lineCount; i++) {
  60. let lineWidth = ctx.measureText(lines[i]).width;
  61. width = lineWidth > width ? lineWidth : width;
  62. }
  63. let height = this.options.font.size * lineCount;
  64. let left = x - width * 0.5;
  65. let top = y - height * 0.5;
  66. if (baseline == "hanging") {
  67. top += 0.5 * this.options.font.size;
  68. top += 4; // distance from node, required because we use hanging. Hanging has less difference between browsers
  69. yLine += 4; // distance from node
  70. }
  71. // cache
  72. this.size = {top: top, left: left, width: width, height: height, yLine: yLine};
  73. }
  74. calculateLabelSize(ctx,selected,x=0,y=0,baseline='middle') {
  75. if (this.labelDirty == true) {
  76. this._calculateLabelSize(ctx, selected, x, y, baseline);
  77. }
  78. }
  79. /**
  80. * Draws the label rectangle
  81. * @param {CanvasRenderingContext2D} ctx
  82. * @private
  83. */
  84. _drawLabelRect(ctx) {
  85. if (this.options.font.background !== undefined && this.options.font.background !== "none") {
  86. ctx.fillStyle = this.options.font.background;
  87. let lineMargin = 2;
  88. switch (this.options.font.align) {
  89. case 'middle':
  90. ctx.fillRect(-this.size.width * 0.5, -this.size.height * 0.5, this.size.width, this.size.height);
  91. break;
  92. case 'top':
  93. ctx.fillRect(-this.size.width * 0.5, -(this.size.height + lineMargin), this.size.width, this.size.height);
  94. break;
  95. case 'bottom':
  96. ctx.fillRect(-this.size.width * 0.5, lineMargin, this.size.width, this.size.height);
  97. break
  98. default:
  99. ctx.fillRect(this.size.left, this.size.top, this.size.width, this.size.height);
  100. break;
  101. }
  102. }
  103. }
  104. /**
  105. * Draws the label text
  106. * @param {CanvasRenderingContext2D} ctx
  107. * @param {Number} x
  108. * @param {Number} yLine
  109. * @param {Array} lines
  110. * @param {Number} lineCount
  111. * @param {Number} fontSize
  112. * @private
  113. */
  114. _drawLabelText(ctx, x, yLine, lines, lineCount, fontSize, baseline = 'middle', relativeFontSize = this.options.font.size) {
  115. // fade in when relative scale is between threshold and threshold - 1
  116. let fontColor = this.options.font.color || "#000000";
  117. let strokeColor = this.options.font.strokeColor;
  118. if (relativeFontSize <= this.options.scaling.label.drawThreshold) {
  119. let opacity = Math.max(0, Math.min(1, 1 - (this.options.scaling.label.drawThreshold - relativeFontSize)));
  120. fontColor = util.overrideOpacity(fontColor, opacity);
  121. strokeColor = util.overrideOpacity(strokeColor, opacity);
  122. }
  123. // draw text
  124. ctx.fillStyle = fontColor;
  125. ctx.textAlign = 'center';
  126. // check for label alignment (for edges)
  127. // TODO: make alignment for nodes
  128. if (this.options.font.align !== 'horizontal') {
  129. x = 0;
  130. yLine = 0;
  131. let lineMargin = 2;
  132. if (this.options.font.align === 'top') {
  133. ctx.textBaseline = 'alphabetic';
  134. yLine -= 2 * lineMargin; // distance from edge, required because we use alphabetic. Alphabetic has less difference between browsers
  135. }
  136. else if (this.options.font.align === 'bottom') {
  137. ctx.textBaseline = 'hanging';
  138. yLine += 2 * lineMargin;// distance from edge, required because we use hanging. Hanging has less difference between browsers
  139. }
  140. else {
  141. ctx.textBaseline = 'middle';
  142. }
  143. }
  144. else {
  145. ctx.textBaseline = baseline;
  146. }
  147. // check for strokeWidth
  148. if (this.options.font.stroke > 0) {
  149. ctx.lineWidth = this.options.font.stroke;
  150. ctx.strokeStyle = strokeColor;
  151. ctx.lineJoin = 'round';
  152. }
  153. for (let i = 0; i < lineCount; i++) {
  154. if (this.options.font.stroke > 0) {
  155. ctx.strokeText(lines[i], x, yLine);
  156. }
  157. ctx.fillText(lines[i], x, yLine);
  158. yLine += fontSize;
  159. }
  160. }
  161. }
  162. export default Label;