funnel.src.js 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576
  1. /**
  2. * @license Highcharts JS v7.0.2 (2019-01-17)
  3. * Highcharts funnel module
  4. *
  5. * (c) 2010-2019 Torstein Honsi
  6. *
  7. * License: www.highcharts.com/license
  8. */
  9. 'use strict';
  10. (function (factory) {
  11. if (typeof module === 'object' && module.exports) {
  12. factory['default'] = factory;
  13. module.exports = factory;
  14. } else if (typeof define === 'function' && define.amd) {
  15. define(function () {
  16. return factory;
  17. });
  18. } else {
  19. factory(typeof Highcharts !== 'undefined' ? Highcharts : undefined);
  20. }
  21. }(function (Highcharts) {
  22. (function (Highcharts) {
  23. /* *
  24. * Highcharts funnel module
  25. *
  26. * (c) 2010-2019 Torstein Honsi
  27. *
  28. * License: www.highcharts.com/license
  29. */
  30. /* eslint indent: 0 */
  31. // create shortcuts
  32. var seriesType = Highcharts.seriesType,
  33. seriesTypes = Highcharts.seriesTypes,
  34. noop = Highcharts.noop,
  35. pick = Highcharts.pick;
  36. /**
  37. * @private
  38. * @class
  39. * @name Highcharts.seriesTypes.funnel
  40. *
  41. * @augments Highcharts.Series
  42. */
  43. seriesType('funnel', 'pie',
  44. /**
  45. * Funnel charts are a type of chart often used to visualize stages in a sales
  46. * project, where the top are the initial stages with the most clients.
  47. * It requires that the modules/funnel.js file is loaded.
  48. *
  49. * @sample highcharts/demo/funnel/
  50. * Funnel demo
  51. *
  52. * @extends plotOptions.pie
  53. * @excluding size
  54. * @product highcharts
  55. * @optionparent plotOptions.funnel
  56. */
  57. {
  58. /**
  59. * Initial animation is by default disabled for the funnel chart.
  60. */
  61. animation: false,
  62. /**
  63. * The center of the series. By default, it is centered in the middle
  64. * of the plot area, so it fills the plot area height.
  65. *
  66. * @type {Array<number|string>}
  67. * @default ["50%", "50%"]
  68. * @since 3.0
  69. */
  70. center: ['50%', '50%'],
  71. /**
  72. * The width of the funnel compared to the width of the plot area,
  73. * or the pixel width if it is a number.
  74. *
  75. * @type {number|string}
  76. * @since 3.0
  77. */
  78. width: '90%',
  79. /**
  80. * The width of the neck, the lower part of the funnel. A number defines
  81. * pixel width, a percentage string defines a percentage of the plot
  82. * area width.
  83. *
  84. * @sample {highcharts} highcharts/demo/funnel/
  85. * Funnel demo
  86. *
  87. * @type {number|string}
  88. * @since 3.0
  89. */
  90. neckWidth: '30%',
  91. /**
  92. * The height of the funnel or pyramid. If it is a number it defines
  93. * the pixel height, if it is a percentage string it is the percentage
  94. * of the plot area height.
  95. *
  96. * @sample {highcharts} highcharts/demo/funnel/
  97. * Funnel demo
  98. *
  99. * @type {number|string}
  100. * @since 3.0
  101. */
  102. height: '100%',
  103. /**
  104. * The height of the neck, the lower part of the funnel. A number defines
  105. * pixel width, a percentage string defines a percentage of the plot
  106. * area height.
  107. *
  108. * @type {number|string}
  109. */
  110. neckHeight: '25%',
  111. /**
  112. * A reversed funnel has the widest area down. A reversed funnel with
  113. * no neck width and neck height is a pyramid.
  114. *
  115. * @since 3.0.10
  116. */
  117. reversed: false,
  118. /** @ignore */
  119. size: true, // to avoid adapting to data label size in Pie.drawDataLabels
  120. dataLabels: {
  121. connectorWidth: 1
  122. },
  123. /**
  124. * Options for the series states.
  125. */
  126. states: {
  127. /**
  128. * @excluding halo, marker, lineWidth, lineWidthPlus
  129. * @apioption plotOptions.funnel.states.hover
  130. */
  131. /**
  132. * Options for a selected funnel item.
  133. *
  134. * @excluding halo, marker, lineWidth, lineWidthPlus
  135. */
  136. select: {
  137. /**
  138. * A specific color for the selected point.
  139. *
  140. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  141. */
  142. color: '#cccccc',
  143. /**
  144. * A specific border color for the selected point.
  145. *
  146. * @type {Highcharts.ColorString}
  147. */
  148. borderColor: '#000000'
  149. }
  150. }
  151. },
  152. // Properties
  153. {
  154. animate: noop,
  155. // Overrides the pie translate method
  156. translate: function () {
  157. var
  158. // Get positions - either an integer or a percentage string
  159. // must be given
  160. getLength = function (length, relativeTo) {
  161. return (/%$/).test(length) ?
  162. relativeTo * parseInt(length, 10) / 100 :
  163. parseInt(length, 10);
  164. },
  165. sum = 0,
  166. series = this,
  167. chart = series.chart,
  168. options = series.options,
  169. reversed = options.reversed,
  170. ignoreHiddenPoint = options.ignoreHiddenPoint,
  171. plotWidth = chart.plotWidth,
  172. plotHeight = chart.plotHeight,
  173. cumulative = 0, // start at top
  174. center = options.center,
  175. centerX = getLength(center[0], plotWidth),
  176. centerY = getLength(center[1], plotHeight),
  177. width = getLength(options.width, plotWidth),
  178. tempWidth,
  179. getWidthAt,
  180. height = getLength(options.height, plotHeight),
  181. neckWidth = getLength(options.neckWidth, plotWidth),
  182. neckHeight = getLength(options.neckHeight, plotHeight),
  183. neckY = (centerY - height / 2) + height - neckHeight,
  184. data = series.data,
  185. path,
  186. fraction,
  187. half = options.dataLabels.position === 'left' ? 1 : 0,
  188. x1,
  189. y1,
  190. x2,
  191. x3,
  192. y3,
  193. x4,
  194. y5;
  195. // Return the width at a specific y coordinate
  196. series.getWidthAt = getWidthAt = function (y) {
  197. var top = (centerY - height / 2);
  198. return (y > neckY || height === neckHeight) ?
  199. neckWidth :
  200. neckWidth + (width - neckWidth) *
  201. (1 - (y - top) / (height - neckHeight));
  202. };
  203. series.getX = function (y, half, point) {
  204. return centerX + (half ? -1 : 1) *
  205. ((getWidthAt(reversed ? 2 * centerY - y : y) / 2) +
  206. point.labelDistance);
  207. };
  208. // Expose
  209. series.center = [centerX, centerY, height];
  210. series.centerX = centerX;
  211. /*
  212. Individual point coordinate naming:
  213. x1,y1 _________________ x2,y1
  214. \ /
  215. \ /
  216. \ /
  217. \ /
  218. \ /
  219. x3,y3 _________ x4,y3
  220. Additional for the base of the neck:
  221. | |
  222. | |
  223. | |
  224. x3,y5 _________ x4,y5
  225. */
  226. // get the total sum
  227. data.forEach(function (point) {
  228. if (!ignoreHiddenPoint || point.visible !== false) {
  229. sum += point.y;
  230. }
  231. });
  232. data.forEach(function (point) {
  233. // set start and end positions
  234. y5 = null;
  235. fraction = sum ? point.y / sum : 0;
  236. y1 = centerY - height / 2 + cumulative * height;
  237. y3 = y1 + fraction * height;
  238. tempWidth = getWidthAt(y1);
  239. x1 = centerX - tempWidth / 2;
  240. x2 = x1 + tempWidth;
  241. tempWidth = getWidthAt(y3);
  242. x3 = centerX - tempWidth / 2;
  243. x4 = x3 + tempWidth;
  244. // the entire point is within the neck
  245. if (y1 > neckY) {
  246. x1 = x3 = centerX - neckWidth / 2;
  247. x2 = x4 = centerX + neckWidth / 2;
  248. // the base of the neck
  249. } else if (y3 > neckY) {
  250. y5 = y3;
  251. tempWidth = getWidthAt(neckY);
  252. x3 = centerX - tempWidth / 2;
  253. x4 = x3 + tempWidth;
  254. y3 = neckY;
  255. }
  256. if (reversed) {
  257. y1 = 2 * centerY - y1;
  258. y3 = 2 * centerY - y3;
  259. if (y5 !== null) {
  260. y5 = 2 * centerY - y5;
  261. }
  262. }
  263. // save the path
  264. path = [
  265. 'M',
  266. x1, y1,
  267. 'L',
  268. x2, y1,
  269. x4, y3
  270. ];
  271. if (y5 !== null) {
  272. path.push(x4, y5, x3, y5);
  273. }
  274. path.push(x3, y3, 'Z');
  275. // prepare for using shared dr
  276. point.shapeType = 'path';
  277. point.shapeArgs = { d: path };
  278. // for tooltips and data labels
  279. point.percentage = fraction * 100;
  280. point.plotX = centerX;
  281. point.plotY = (y1 + (y5 || y3)) / 2;
  282. // Placement of tooltips and data labels
  283. point.tooltipPos = [
  284. centerX,
  285. point.plotY
  286. ];
  287. // Slice is a noop on funnel points
  288. point.slice = noop;
  289. // Mimicking pie data label placement logic
  290. point.half = half;
  291. if (!ignoreHiddenPoint || point.visible !== false) {
  292. cumulative += fraction;
  293. }
  294. });
  295. },
  296. // Funnel items don't have angles (#2289)
  297. sortByAngle: function (points) {
  298. points.sort(function (a, b) {
  299. return a.plotY - b.plotY;
  300. });
  301. },
  302. // Extend the pie data label method
  303. drawDataLabels: function () {
  304. var series = this,
  305. data = series.data,
  306. labelDistance = series.options.dataLabels.distance,
  307. leftSide,
  308. sign,
  309. point,
  310. i = data.length,
  311. x,
  312. y;
  313. // In the original pie label anticollision logic, the slots are
  314. // distributed from one labelDistance above to one labelDistance below
  315. // the pie. In funnels we don't want this.
  316. series.center[2] -= 2 * labelDistance;
  317. // Set the label position array for each point.
  318. while (i--) {
  319. point = data[i];
  320. leftSide = point.half;
  321. sign = leftSide ? 1 : -1;
  322. y = point.plotY;
  323. point.labelDistance = pick(
  324. point.options.dataLabels && point.options.dataLabels.distance,
  325. labelDistance
  326. );
  327. series.maxLabelDistance = Math.max(
  328. point.labelDistance,
  329. series.maxLabelDistance || 0
  330. );
  331. x = series.getX(y, leftSide, point);
  332. // set the anchor point for data labels
  333. point.labelPosition = {
  334. // initial position of the data label - it's utilized for
  335. // finding the final position for the label
  336. natural: {
  337. x: 0,
  338. y: y
  339. },
  340. 'final': {
  341. // used for generating connector path -
  342. // initialized later in drawDataLabels function
  343. // x: undefined,
  344. // y: undefined
  345. },
  346. // left - funnel on the left side of the data label
  347. // right - funnel on the right side of the data label
  348. alignment: leftSide ? 'right' : 'left',
  349. connectorPosition: {
  350. breakAt: { // used in connectorShapes.fixedOffset
  351. x: x + (point.labelDistance - 5) * sign,
  352. y: y
  353. },
  354. touchingSliceAt: {
  355. x: x + point.labelDistance * sign,
  356. y: y
  357. }
  358. }
  359. };
  360. }
  361. seriesTypes.pie.prototype.drawDataLabels.call(this);
  362. }
  363. });
  364. /**
  365. * A `funnel` series. If the [type](#series.funnel.type) option is
  366. * not specified, it is inherited from [chart.type](#chart.type).
  367. *
  368. * @extends series,plotOptions.funnel
  369. * @excluding dataParser, dataURL, stack, xAxis, yAxis
  370. * @product highcharts
  371. * @apioption series.funnel
  372. */
  373. /**
  374. * An array of data points for the series. For the `funnel` series type,
  375. * points can be given in the following ways:
  376. *
  377. * 1. An array of numerical values. In this case, the numerical values
  378. * will be interpreted as `y` options. Example:
  379. *
  380. * ```js
  381. * data: [0, 5, 3, 5]
  382. * ```
  383. *
  384. * 2. An array of objects with named values. The following snippet shows only a
  385. * few settings, see the complete options set below. If the total number of data
  386. * points exceeds the series' [turboThreshold](#series.funnel.turboThreshold),
  387. * this option is not available.
  388. *
  389. * ```js
  390. * data: [{
  391. * y: 3,
  392. * name: "Point2",
  393. * color: "#00FF00"
  394. * }, {
  395. * y: 1,
  396. * name: "Point1",
  397. * color: "#FF00FF"
  398. * }]
  399. * ```
  400. *
  401. * @sample {highcharts} highcharts/chart/reflow-true/
  402. * Numerical values
  403. * @sample {highcharts} highcharts/series/data-array-of-arrays/
  404. * Arrays of numeric x and y
  405. * @sample {highcharts} highcharts/series/data-array-of-arrays-datetime/
  406. * Arrays of datetime x and y
  407. * @sample {highcharts} highcharts/series/data-array-of-name-value/
  408. * Arrays of point.name and y
  409. * @sample {highcharts} highcharts/series/data-array-of-objects/
  410. * Config objects
  411. *
  412. * @type {Array<number|*>}
  413. * @extends series.pie.data
  414. * @excluding sliced
  415. * @product highcharts
  416. * @apioption series.funnel.data
  417. */
  418. /**
  419. * Pyramid series type.
  420. *
  421. * @private
  422. * @class
  423. * @name Highcharts.seriesTypes.pyramid
  424. *
  425. * @augments Highcharts.Series
  426. */
  427. seriesType('pyramid', 'funnel',
  428. /**
  429. * A pyramid series is a special type of funnel, without neck and reversed by
  430. * default. Requires the funnel module.
  431. *
  432. * @sample highcharts/demo/pyramid/
  433. * Pyramid chart
  434. *
  435. * @extends plotOptions.funnel
  436. * @product highcharts
  437. * @optionparent plotOptions.pyramid
  438. */
  439. {
  440. /**
  441. * The pyramid neck width is zero by default, as opposed to the funnel,
  442. * which shares the same layout logic.
  443. *
  444. * @since 3.0.10
  445. */
  446. neckWidth: '0%',
  447. /**
  448. * The pyramid neck width is zero by default, as opposed to the funnel,
  449. * which shares the same layout logic.
  450. *
  451. * @since 3.0.10
  452. */
  453. neckHeight: '0%',
  454. /**
  455. * The pyramid is reversed by default, as opposed to the funnel, which
  456. * shares the layout engine, and is not reversed.
  457. *
  458. * @since 3.0.10
  459. */
  460. reversed: true
  461. });
  462. /**
  463. * A `pyramid` series. If the [type](#series.pyramid.type) option is
  464. * not specified, it is inherited from [chart.type](#chart.type).
  465. *
  466. * @extends series,plotOptions.pyramid
  467. * @excluding dataParser, dataURL, stack, xAxis, yAxis
  468. * @product highcharts
  469. * @apioption series.pyramid
  470. */
  471. /**
  472. * An array of data points for the series. For the `pyramid` series
  473. * type, points can be given in the following ways:
  474. *
  475. * 1. An array of numerical values. In this case, the numerical values will be
  476. * interpreted as `y` options. Example:
  477. * ```js
  478. * data: [0, 5, 3, 5]
  479. * ```
  480. *
  481. * 2. An array of objects with named values. The following snippet shows only a
  482. * few settings, see the complete options set below. If the total number of
  483. * data points exceeds the series'
  484. * [turboThreshold](#series.pyramid.turboThreshold), this option is not
  485. * available.
  486. * ```js
  487. * data: [{
  488. * y: 9,
  489. * name: "Point2",
  490. * color: "#00FF00"
  491. * }, {
  492. * y: 6,
  493. * name: "Point1",
  494. * color: "#FF00FF"
  495. * }]
  496. * ```
  497. *
  498. * @sample {highcharts} highcharts/chart/reflow-true/
  499. * Numerical values
  500. * @sample {highcharts} highcharts/series/data-array-of-objects/
  501. * Config objects
  502. *
  503. * @type {Array<number|*>}
  504. * @extends series.pie.data
  505. * @excluding sliced
  506. * @product highcharts
  507. * @apioption series.pyramid.data
  508. */
  509. }(Highcharts));
  510. return (function () {
  511. }());
  512. }));