funnel.src.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291
  1. /**
  2. * @license Highcharts JS v5.0.6 (2016-12-07)
  3. * Highcharts funnel module
  4. *
  5. * (c) 2010-2016 Torstein Honsi
  6. *
  7. * License: www.highcharts.com/license
  8. */
  9. (function(factory) {
  10. if (typeof module === 'object' && module.exports) {
  11. module.exports = factory;
  12. } else {
  13. factory(Highcharts);
  14. }
  15. }(function(Highcharts) {
  16. (function(Highcharts) {
  17. /**
  18. * Highcharts funnel module
  19. *
  20. * (c) 2010-2016 Torstein Honsi
  21. *
  22. * License: www.highcharts.com/license
  23. */
  24. /* eslint indent:0 */
  25. 'use strict';
  26. // create shortcuts
  27. var seriesType = Highcharts.seriesType,
  28. seriesTypes = Highcharts.seriesTypes,
  29. noop = Highcharts.noop,
  30. each = Highcharts.each;
  31. seriesType('funnel', 'pie', {
  32. animation: false,
  33. center: ['50%', '50%'],
  34. width: '90%',
  35. neckWidth: '30%',
  36. height: '100%',
  37. neckHeight: '25%',
  38. reversed: false,
  39. size: true, // to avoid adapting to data label size in Pie.drawDataLabels
  40. },
  41. // Properties
  42. {
  43. animate: noop,
  44. /**
  45. * Overrides the pie translate method
  46. */
  47. translate: function() {
  48. var
  49. // Get positions - either an integer or a percentage string must be given
  50. getLength = function(length, relativeTo) {
  51. return (/%$/).test(length) ?
  52. relativeTo * parseInt(length, 10) / 100 :
  53. parseInt(length, 10);
  54. },
  55. sum = 0,
  56. series = this,
  57. chart = series.chart,
  58. options = series.options,
  59. reversed = options.reversed,
  60. ignoreHiddenPoint = options.ignoreHiddenPoint,
  61. plotWidth = chart.plotWidth,
  62. plotHeight = chart.plotHeight,
  63. cumulative = 0, // start at top
  64. center = options.center,
  65. centerX = getLength(center[0], plotWidth),
  66. centerY = getLength(center[1], plotHeight),
  67. width = getLength(options.width, plotWidth),
  68. tempWidth,
  69. getWidthAt,
  70. height = getLength(options.height, plotHeight),
  71. neckWidth = getLength(options.neckWidth, plotWidth),
  72. neckHeight = getLength(options.neckHeight, plotHeight),
  73. neckY = (centerY - height / 2) + height - neckHeight,
  74. data = series.data,
  75. path,
  76. fraction,
  77. half = options.dataLabels.position === 'left' ? 1 : 0,
  78. x1,
  79. y1,
  80. x2,
  81. x3,
  82. y3,
  83. x4,
  84. y5;
  85. // Return the width at a specific y coordinate
  86. series.getWidthAt = getWidthAt = function(y) {
  87. var top = (centerY - height / 2);
  88. return y > neckY || height === neckHeight ?
  89. neckWidth :
  90. neckWidth + (width - neckWidth) * (1 - (y - top) / (height - neckHeight));
  91. };
  92. series.getX = function(y, half) {
  93. return centerX + (half ? -1 : 1) * ((getWidthAt(reversed ? 2 * centerY - y : y) / 2) + options.dataLabels.distance);
  94. };
  95. // Expose
  96. series.center = [centerX, centerY, height];
  97. series.centerX = centerX;
  98. /*
  99. * Individual point coordinate naming:
  100. *
  101. * x1,y1 _________________ x2,y1
  102. * \ /
  103. * \ /
  104. * \ /
  105. * \ /
  106. * \ /
  107. * x3,y3 _________ x4,y3
  108. *
  109. * Additional for the base of the neck:
  110. *
  111. * | |
  112. * | |
  113. * | |
  114. * x3,y5 _________ x4,y5
  115. */
  116. // get the total sum
  117. each(data, function(point) {
  118. if (!ignoreHiddenPoint || point.visible !== false) {
  119. sum += point.y;
  120. }
  121. });
  122. each(data, function(point) {
  123. // set start and end positions
  124. y5 = null;
  125. fraction = sum ? point.y / sum : 0;
  126. y1 = centerY - height / 2 + cumulative * height;
  127. y3 = y1 + fraction * height;
  128. //tempWidth = neckWidth + (width - neckWidth) * ((height - neckHeight - y1) / (height - neckHeight));
  129. tempWidth = getWidthAt(y1);
  130. x1 = centerX - tempWidth / 2;
  131. x2 = x1 + tempWidth;
  132. tempWidth = getWidthAt(y3);
  133. x3 = centerX - tempWidth / 2;
  134. x4 = x3 + tempWidth;
  135. // the entire point is within the neck
  136. if (y1 > neckY) {
  137. x1 = x3 = centerX - neckWidth / 2;
  138. x2 = x4 = centerX + neckWidth / 2;
  139. // the base of the neck
  140. } else if (y3 > neckY) {
  141. y5 = y3;
  142. tempWidth = getWidthAt(neckY);
  143. x3 = centerX - tempWidth / 2;
  144. x4 = x3 + tempWidth;
  145. y3 = neckY;
  146. }
  147. if (reversed) {
  148. y1 = 2 * centerY - y1;
  149. y3 = 2 * centerY - y3;
  150. y5 = (y5 ? 2 * centerY - y5 : null);
  151. }
  152. // save the path
  153. path = [
  154. 'M',
  155. x1, y1,
  156. 'L',
  157. x2, y1,
  158. x4, y3
  159. ];
  160. if (y5) {
  161. path.push(x4, y5, x3, y5);
  162. }
  163. path.push(x3, y3, 'Z');
  164. // prepare for using shared dr
  165. point.shapeType = 'path';
  166. point.shapeArgs = {
  167. d: path
  168. };
  169. // for tooltips and data labels
  170. point.percentage = fraction * 100;
  171. point.plotX = centerX;
  172. point.plotY = (y1 + (y5 || y3)) / 2;
  173. // Placement of tooltips and data labels
  174. point.tooltipPos = [
  175. centerX,
  176. point.plotY
  177. ];
  178. // Slice is a noop on funnel points
  179. point.slice = noop;
  180. // Mimicking pie data label placement logic
  181. point.half = half;
  182. if (!ignoreHiddenPoint || point.visible !== false) {
  183. cumulative += fraction;
  184. }
  185. });
  186. },
  187. /**
  188. * Draw a single point (wedge)
  189. * @param {Object} point The point object
  190. * @param {Object} color The color of the point
  191. * @param {Number} brightness The brightness relative to the color
  192. */
  193. drawPoints: seriesTypes.column.prototype.drawPoints,
  194. /**
  195. * Funnel items don't have angles (#2289)
  196. */
  197. sortByAngle: function(points) {
  198. points.sort(function(a, b) {
  199. return a.plotY - b.plotY;
  200. });
  201. },
  202. /**
  203. * Extend the pie data label method
  204. */
  205. drawDataLabels: function() {
  206. var data = this.data,
  207. labelDistance = this.options.dataLabels.distance,
  208. leftSide,
  209. sign,
  210. point,
  211. i = data.length,
  212. x,
  213. y;
  214. // In the original pie label anticollision logic, the slots are distributed
  215. // from one labelDistance above to one labelDistance below the pie. In funnels
  216. // we don't want this.
  217. this.center[2] -= 2 * labelDistance;
  218. // Set the label position array for each point.
  219. while (i--) {
  220. point = data[i];
  221. leftSide = point.half;
  222. sign = leftSide ? 1 : -1;
  223. y = point.plotY;
  224. x = this.getX(y, leftSide);
  225. // set the anchor point for data labels
  226. point.labelPos = [
  227. 0, // first break of connector
  228. y, // a/a
  229. x + (labelDistance - 5) * sign, // second break, right outside point shape
  230. y, // a/a
  231. x + labelDistance * sign, // landing point for connector
  232. y, // a/a
  233. leftSide ? 'right' : 'left', // alignment
  234. 0 // center angle
  235. ];
  236. }
  237. seriesTypes.pie.prototype.drawDataLabels.call(this);
  238. }
  239. });
  240. /**
  241. * Pyramid series type.
  242. * A pyramid series is a special type of funnel, without neck and reversed by default.
  243. */
  244. seriesType('pyramid', 'funnel', {
  245. neckWidth: '0%',
  246. neckHeight: '0%',
  247. reversed: true
  248. });
  249. }(Highcharts));
  250. }));