variwide.src.js 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350
  1. /* *
  2. * Highcharts variwide module
  3. *
  4. * (c) 2010-2019 Torstein Honsi
  5. *
  6. * License: www.highcharts.com/license
  7. */
  8. 'use strict';
  9. import H from '../parts/Globals.js';
  10. import '../parts/AreaSeries.js';
  11. var addEvent = H.addEvent,
  12. seriesType = H.seriesType,
  13. seriesTypes = H.seriesTypes,
  14. pick = H.pick;
  15. /**
  16. * @private
  17. * @class
  18. * @name Highcharts.seriesTypes.variwide
  19. *
  20. * @augments Highcharts.Series
  21. */
  22. seriesType('variwide', 'column'
  23. /**
  24. * A variwide chart (related to marimekko chart) is a column chart with a
  25. * variable width expressing a third dimension.
  26. *
  27. * @sample {highcharts} highcharts/demo/variwide/
  28. * Variwide chart
  29. * @sample {highcharts} highcharts/series-variwide/inverted/
  30. * Inverted variwide chart
  31. * @sample {highcharts} highcharts/series-variwide/datetime/
  32. * Variwide columns on a datetime axis
  33. *
  34. * @extends plotOptions.column
  35. * @since 6.0.0
  36. * @product highcharts
  37. * @excluding boostThreshold, crisp, depth, edgeColor, edgeWidth,
  38. * groupZPadding
  39. * @optionparent plotOptions.variwide
  40. */
  41. , {
  42. /**
  43. * In a variwide chart, the point padding is 0 in order to express the
  44. * horizontal stacking of items.
  45. */
  46. pointPadding: 0,
  47. /**
  48. * In a variwide chart, the group padding is 0 in order to express the
  49. * horizontal stacking of items.
  50. */
  51. groupPadding: 0
  52. }, {
  53. pointArrayMap: ['y', 'z'],
  54. parallelArrays: ['x', 'y', 'z'],
  55. processData: function (force) {
  56. this.totalZ = 0;
  57. this.relZ = [];
  58. seriesTypes.column.prototype.processData.call(this, force);
  59. (this.xAxis.reversed ?
  60. this.zData.slice().reverse() :
  61. this.zData).forEach(
  62. function (z, i) {
  63. this.relZ[i] = this.totalZ;
  64. this.totalZ += z;
  65. },
  66. this
  67. );
  68. if (this.xAxis.categories) {
  69. this.xAxis.variwide = true;
  70. this.xAxis.zData = this.zData; // Used for label rank
  71. }
  72. },
  73. /**
  74. * Translate an x value inside a given category index into the distorted
  75. * axis translation.
  76. *
  77. * @private
  78. * @function Highcharts.Series#postTranslate
  79. *
  80. * @param {number} index
  81. * The category index
  82. *
  83. * @param {number} x
  84. * The X pixel position in undistorted axis pixels
  85. *
  86. * @return {number}
  87. * Distorted X position
  88. */
  89. postTranslate: function (index, x, point) {
  90. var axis = this.xAxis,
  91. relZ = this.relZ,
  92. i = axis.reversed ? relZ.length - index : index,
  93. goRight = axis.reversed ? -1 : 1,
  94. len = axis.len,
  95. totalZ = this.totalZ,
  96. linearSlotLeft = i / relZ.length * len,
  97. linearSlotRight = (i + goRight) / relZ.length * len,
  98. slotLeft = (pick(relZ[i], totalZ) / totalZ) * len,
  99. slotRight = (pick(relZ[i + goRight], totalZ) / totalZ) * len,
  100. xInsideLinearSlot = x - linearSlotLeft,
  101. ret;
  102. // Set crosshairWidth for every point (#8173)
  103. if (point) {
  104. point.crosshairWidth = slotRight - slotLeft;
  105. }
  106. ret = slotLeft +
  107. xInsideLinearSlot * (slotRight - slotLeft) /
  108. (linearSlotRight - linearSlotLeft);
  109. return ret;
  110. },
  111. // Extend translation by distoring X position based on Z.
  112. translate: function () {
  113. // Temporarily disable crisping when computing original shapeArgs
  114. var crispOption = this.options.crisp,
  115. xAxis = this.xAxis;
  116. this.options.crisp = false;
  117. seriesTypes.column.prototype.translate.call(this);
  118. // Reset option
  119. this.options.crisp = crispOption;
  120. var inverted = this.chart.inverted,
  121. crisp = this.borderWidth % 2 / 2;
  122. // Distort the points to reflect z dimension
  123. this.points.forEach(function (point, i) {
  124. var left, right;
  125. if (xAxis.variwide) {
  126. left = this.postTranslate(
  127. i,
  128. point.shapeArgs.x,
  129. point
  130. );
  131. right = this.postTranslate(
  132. i,
  133. point.shapeArgs.x + point.shapeArgs.width
  134. );
  135. // For linear or datetime axes, the variwide column should
  136. // start with X and extend Z units, without modifying the
  137. // axis.
  138. } else {
  139. left = point.plotX;
  140. right = xAxis.translate(
  141. point.x + point.z,
  142. 0,
  143. 0,
  144. 0,
  145. 1
  146. );
  147. }
  148. if (this.options.crisp) {
  149. left = Math.round(left) - crisp;
  150. right = Math.round(right) - crisp;
  151. }
  152. point.shapeArgs.x = left;
  153. point.shapeArgs.width = right - left;
  154. // Crosshair position (#8083)
  155. point.plotX = (left + right) / 2;
  156. // Adjust the tooltip position
  157. if (!inverted) {
  158. point.tooltipPos[0] =
  159. point.shapeArgs.x + point.shapeArgs.width / 2;
  160. } else {
  161. point.tooltipPos[1] =
  162. xAxis.len - point.shapeArgs.x - point.shapeArgs.width / 2;
  163. }
  164. }, this);
  165. }
  166. // Point functions
  167. }, {
  168. isValid: function () {
  169. return H.isNumber(this.y, true) && H.isNumber(this.z, true);
  170. }
  171. });
  172. H.Tick.prototype.postTranslate = function (xy, xOrY, index) {
  173. var axis = this.axis,
  174. pos = xy[xOrY] - axis.pos;
  175. if (!axis.horiz) {
  176. pos = axis.len - pos;
  177. }
  178. pos = axis.series[0].postTranslate(index, pos);
  179. if (!axis.horiz) {
  180. pos = axis.len - pos;
  181. }
  182. xy[xOrY] = axis.pos + pos;
  183. };
  184. // Same width as the category (#8083)
  185. addEvent(H.Axis, 'afterDrawCrosshair', function (e) {
  186. if (this.variwide && this.cross) {
  187. this.cross.attr('stroke-width', e.point && e.point.crosshairWidth);
  188. }
  189. });
  190. // On a vertical axis, apply anti-collision logic to the labels.
  191. addEvent(H.Axis, 'afterRender', function () {
  192. var axis = this;
  193. if (!this.horiz && this.variwide) {
  194. this.chart.labelCollectors.push(function () {
  195. return axis.tickPositions.map(function (pos, i) {
  196. var label = axis.ticks[pos].label;
  197. label.labelrank = axis.zData[i];
  198. return label;
  199. });
  200. });
  201. }
  202. });
  203. addEvent(H.Tick, 'afterGetPosition', function (e) {
  204. var axis = this.axis,
  205. xOrY = axis.horiz ? 'x' : 'y';
  206. if (axis.variwide) {
  207. this[xOrY + 'Orig'] = e.pos[xOrY];
  208. this.postTranslate(e.pos, xOrY, this.pos);
  209. }
  210. });
  211. H.wrap(H.Tick.prototype, 'getLabelPosition', function (
  212. proceed,
  213. x,
  214. y,
  215. label,
  216. horiz,
  217. labelOptions,
  218. tickmarkOffset,
  219. index
  220. ) {
  221. var args = Array.prototype.slice.call(arguments, 1),
  222. xy,
  223. xOrY = horiz ? 'x' : 'y';
  224. // Replace the x with the original x
  225. if (this.axis.variwide && typeof this[xOrY + 'Orig'] === 'number') {
  226. args[horiz ? 0 : 1] = this[xOrY + 'Orig'];
  227. }
  228. xy = proceed.apply(this, args);
  229. // Post-translate
  230. if (this.axis.variwide && this.axis.categories) {
  231. this.postTranslate(xy, xOrY, index);
  232. }
  233. return xy;
  234. });
  235. /**
  236. * A `variwide` series. If the [type](#series.variwide.type) option is not
  237. * specified, it is inherited from [chart.type](#chart.type).
  238. *
  239. * @extends series,plotOptions.variwide
  240. * @product highcharts
  241. * @apioption series.variwide
  242. */
  243. /**
  244. * An array of data points for the series. For the `variwide` series type,
  245. * points can be given in the following ways:
  246. *
  247. * 1. An array of arrays with 3 or 2 values. In this case, the values correspond
  248. * to `x,y,z`. If the first value is a string, it is applied as the name of
  249. * the point, and the `x` value is inferred. The `x` value can also be
  250. * omitted, in which case the inner arrays should be of length 2. Then the
  251. * `x` value is automatically calculated, either starting at 0 and
  252. * incremented by 1, or from `pointStart` and `pointInterval` given in the
  253. * series options.
  254. * ```js
  255. * data: [
  256. * [0, 1, 2],
  257. * [1, 5, 5],
  258. * [2, 0, 2]
  259. * ]
  260. * ```
  261. *
  262. * 2. An array of objects with named values. The following snippet shows only a
  263. * few settings, see the complete options set below. If the total number of
  264. * data points exceeds the series'
  265. * [turboThreshold](#series.variwide.turboThreshold), this option is not
  266. * available.
  267. * ```js
  268. * data: [{
  269. * x: 1,
  270. * y: 1,
  271. * z: 1,
  272. * name: "Point2",
  273. * color: "#00FF00"
  274. * }, {
  275. * x: 1,
  276. * y: 5,
  277. * z: 4,
  278. * name: "Point1",
  279. * color: "#FF00FF"
  280. * }]
  281. * ```
  282. *
  283. * @sample {highcharts} highcharts/series/data-array-of-arrays/
  284. * Arrays of numeric x and y
  285. * @sample {highcharts} highcharts/series/data-array-of-arrays-datetime/
  286. * Arrays of datetime x and y
  287. * @sample {highcharts} highcharts/series/data-array-of-name-value/
  288. * Arrays of point.name and y
  289. * @sample {highcharts} highcharts/series/data-array-of-objects/
  290. * Config objects
  291. *
  292. * @type {Array<Array<(number|string),number>|Array<(number|string),number,number>|*>}
  293. * @extends series.line.data
  294. * @excluding marker
  295. * @product highcharts
  296. * @apioption series.variwide.data
  297. */
  298. /**
  299. * The relative width for each column. On a category axis, the widths are
  300. * distributed so they sum up to the X axis length. On linear and datetime axes,
  301. * the columns will be laid out from the X value and Z units along the axis.
  302. *
  303. * @type {number}
  304. * @product highcharts
  305. * @apioption series.variwide.data.z
  306. */