ba6b58b4fbfa8c90ed800f66421a50060a34ddee8fd2d402ec9cd3e0d756fa4d2ddf9d1af96c32cdb6b881de6520ea04021b61676289b6b065c1572d07e3c9 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258
  1. 'use strict';
  2. import Chart from 'chart.js';
  3. import HitBox from './hitbox';
  4. import utils from './utils';
  5. import positioners from './positioners';
  6. var helpers = Chart.helpers;
  7. function boundingRects(size, padding) {
  8. var th = size.height;
  9. var tw = size.width;
  10. var tx = -tw / 2;
  11. var ty = -th / 2;
  12. return {
  13. frame: {
  14. x: tx - padding.left,
  15. y: ty - padding.top,
  16. w: tw + padding.width,
  17. h: th + padding.height,
  18. },
  19. text: {
  20. x: tx,
  21. y: ty,
  22. w: tw,
  23. h: th
  24. }
  25. };
  26. }
  27. function getScaleOrigin(el) {
  28. var horizontal = el._model.horizontal;
  29. var scale = el._scale || (horizontal && el._xScale) || el._yScale;
  30. if (!scale) {
  31. return null;
  32. }
  33. if (scale.xCenter !== undefined && scale.yCenter !== undefined) {
  34. return {x: scale.xCenter, y: scale.yCenter};
  35. }
  36. var pixel = scale.getBasePixel();
  37. return horizontal ?
  38. {x: pixel, y: null} :
  39. {x: null, y: pixel};
  40. }
  41. function getPositioner(el) {
  42. if (el instanceof Chart.elements.Arc) {
  43. return positioners.arc;
  44. }
  45. if (el instanceof Chart.elements.Point) {
  46. return positioners.point;
  47. }
  48. if (el instanceof Chart.elements.Rectangle) {
  49. return positioners.rect;
  50. }
  51. return positioners.fallback;
  52. }
  53. function coordinates(el, model, rect) {
  54. var point = model.positioner(el._view, model.anchor, model.align, model.origin);
  55. var vx = point.vx;
  56. var vy = point.vy;
  57. if (!vx && !vy) {
  58. // if aligned center, we don't want to offset the center point
  59. return {x: point.x, y: point.y};
  60. }
  61. // include borders to the bounding rect
  62. var borderWidth = model.borderWidth || 0;
  63. var w = (rect.w + borderWidth * 2);
  64. var h = (rect.h + borderWidth * 2);
  65. // take in account the label rotation
  66. var rotation = model.rotation;
  67. var dx = Math.abs(w / 2 * Math.cos(rotation)) + Math.abs(h / 2 * Math.sin(rotation));
  68. var dy = Math.abs(w / 2 * Math.sin(rotation)) + Math.abs(h / 2 * Math.cos(rotation));
  69. // scale the unit vector (vx, vy) to get at least dx or dy equal to w or h respectively
  70. // (else we would calculate the distance to the ellipse inscribed in the bounding rect)
  71. var vs = 1 / Math.max(Math.abs(vx), Math.abs(vy));
  72. dx *= vx * vs;
  73. dy *= vy * vs;
  74. // finally, include the explicit offset
  75. dx += model.offset * vx;
  76. dy += model.offset * vy;
  77. return {
  78. x: point.x + dx,
  79. y: point.y + dy
  80. };
  81. }
  82. function drawFrame(ctx, rect, model) {
  83. var bgColor = model.backgroundColor;
  84. var borderColor = model.borderColor;
  85. var borderWidth = model.borderWidth;
  86. if (!bgColor && (!borderColor || !borderWidth)) {
  87. return;
  88. }
  89. ctx.beginPath();
  90. helpers.canvas.roundedRect(
  91. ctx,
  92. Math.round(rect.x) - borderWidth / 2,
  93. Math.round(rect.y) - borderWidth / 2,
  94. Math.round(rect.w) + borderWidth,
  95. Math.round(rect.h) + borderWidth,
  96. model.borderRadius);
  97. ctx.closePath();
  98. if (bgColor) {
  99. ctx.fillStyle = bgColor;
  100. ctx.fill();
  101. }
  102. if (borderColor && borderWidth) {
  103. ctx.strokeStyle = borderColor;
  104. ctx.lineWidth = borderWidth;
  105. ctx.lineJoin = 'miter';
  106. ctx.stroke();
  107. }
  108. }
  109. function drawText(ctx, lines, rect, model) {
  110. var align = model.textAlign;
  111. var font = model.font;
  112. var lh = font.lineHeight;
  113. var color = model.color;
  114. var ilen = lines.length;
  115. var x, y, i;
  116. if (!ilen || !color) {
  117. return;
  118. }
  119. x = rect.x;
  120. y = rect.y + lh / 2;
  121. if (align === 'center') {
  122. x += rect.w / 2;
  123. } else if (align === 'end' || align === 'right') {
  124. x += rect.w;
  125. }
  126. ctx.font = model.font.string;
  127. ctx.fillStyle = color;
  128. ctx.textAlign = align;
  129. ctx.textBaseline = 'middle';
  130. for (i = 0; i < ilen; ++i) {
  131. ctx.fillText(
  132. lines[i],
  133. Math.round(x),
  134. Math.round(y),
  135. Math.round(rect.w));
  136. y += lh;
  137. }
  138. }
  139. var Label = function(config, ctx, el, index) {
  140. var me = this;
  141. me._hitbox = new HitBox();
  142. me._config = config;
  143. me._index = index;
  144. me._model = null;
  145. me._ctx = ctx;
  146. me._el = el;
  147. };
  148. helpers.extend(Label.prototype, {
  149. /**
  150. * @private
  151. */
  152. _modelize: function(lines, config, context) {
  153. var me = this;
  154. var index = me._index;
  155. var resolve = helpers.options.resolve;
  156. var font = utils.parseFont(resolve([config.font, {}], context, index));
  157. return {
  158. align: resolve([config.align, 'center'], context, index),
  159. anchor: resolve([config.anchor, 'center'], context, index),
  160. backgroundColor: resolve([config.backgroundColor, null], context, index),
  161. borderColor: resolve([config.borderColor, null], context, index),
  162. borderRadius: resolve([config.borderRadius, 0], context, index),
  163. borderWidth: resolve([config.borderWidth, 0], context, index),
  164. color: resolve([config.color, Chart.defaults.global.defaultFontColor], context, index),
  165. font: font,
  166. lines: lines,
  167. offset: resolve([config.offset, 0], context, index),
  168. opacity: resolve([config.opacity, 1], context, index),
  169. origin: getScaleOrigin(me._el),
  170. padding: helpers.options.toPadding(resolve([config.padding, 0], context, index)),
  171. positioner: getPositioner(me._el),
  172. rotation: resolve([config.rotation, 0], context, index) * (Math.PI / 180),
  173. size: utils.textSize(me._ctx, lines, font),
  174. textAlign: resolve([config.textAlign, 'start'], context, index)
  175. };
  176. },
  177. update: function(context) {
  178. var me = this;
  179. var model = null;
  180. var index = me._index;
  181. var config = me._config;
  182. var value, label, lines;
  183. if (helpers.options.resolve([config.display, true], context, index)) {
  184. value = context.dataset.data[index];
  185. label = helpers.valueOrDefault(helpers.callback(config.formatter, [value, context]), value);
  186. lines = helpers.isNullOrUndef(label) ? [] : utils.toTextLines(label);
  187. model = lines.length ? me._modelize(lines, config, context) : null;
  188. }
  189. me._model = model;
  190. },
  191. draw: function(ctx) {
  192. var me = this;
  193. var model = me._model;
  194. var rects, center;
  195. if (!model || !model.opacity) {
  196. return;
  197. }
  198. rects = boundingRects(model.size, model.padding);
  199. center = coordinates(me._el, model, rects.frame);
  200. me._hitbox.update(center, rects.frame, model.rotation);
  201. ctx.save();
  202. ctx.globalAlpha = utils.bound(0, model.opacity, 1);
  203. ctx.translate(Math.round(center.x), Math.round(center.y));
  204. ctx.rotate(model.rotation);
  205. drawFrame(ctx, rects.frame, model);
  206. drawText(ctx, model.lines, rects.text, model);
  207. ctx.restore();
  208. },
  209. contains: function(x, y) {
  210. return this._hitbox.contains(x, y);
  211. }
  212. });
  213. export default Label;