AreaSeries.js 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496
  1. /* *
  2. *
  3. * (c) 2010-2019 Torstein Honsi
  4. *
  5. * License: www.highcharts.com/license
  6. *
  7. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  8. *
  9. * */
  10. 'use strict';
  11. import H from './Globals.js';
  12. import U from './Utilities.js';
  13. var objectEach = U.objectEach, pick = U.pick;
  14. import './Color.js';
  15. import './Legend.js';
  16. import './Series.js';
  17. import './Options.js';
  18. var color = H.color, LegendSymbolMixin = H.LegendSymbolMixin, Series = H.Series, seriesType = H.seriesType;
  19. /**
  20. * Area series type.
  21. *
  22. * @private
  23. * @class
  24. * @name Highcharts.seriesTypes.area
  25. *
  26. * @augments Highcharts.Series
  27. */
  28. seriesType('area', 'line',
  29. /**
  30. * The area series type.
  31. *
  32. * @sample {highcharts} highcharts/demo/area-basic/
  33. * Area chart
  34. * @sample {highstock} stock/demo/area/
  35. * Area chart
  36. *
  37. * @extends plotOptions.line
  38. * @excluding useOhlcData
  39. * @product highcharts highstock
  40. * @optionparent plotOptions.area
  41. */
  42. {
  43. /**
  44. * Fill color or gradient for the area. When `null`, the series' `color`
  45. * is used with the series' `fillOpacity`.
  46. *
  47. * In styled mode, the fill color can be set with the `.highcharts-area`
  48. * class name.
  49. *
  50. * @sample {highcharts} highcharts/plotoptions/area-fillcolor-default/
  51. * Null by default
  52. * @sample {highcharts} highcharts/plotoptions/area-fillcolor-gradient/
  53. * Gradient
  54. *
  55. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  56. * @product highcharts highstock
  57. * @apioption plotOptions.area.fillColor
  58. */
  59. /**
  60. * Fill opacity for the area. When you set an explicit `fillColor`,
  61. * the `fillOpacity` is not applied. Instead, you should define the
  62. * opacity in the `fillColor` with an rgba color definition. The
  63. * `fillOpacity` setting, also the default setting, overrides the alpha
  64. * component of the `color` setting.
  65. *
  66. * In styled mode, the fill opacity can be set with the
  67. * `.highcharts-area` class name.
  68. *
  69. * @sample {highcharts} highcharts/plotoptions/area-fillopacity/
  70. * Automatic fill color and fill opacity of 0.1
  71. *
  72. * @type {number}
  73. * @default {highcharts} 0.75
  74. * @default {highstock} 0.75
  75. * @product highcharts highstock
  76. * @apioption plotOptions.area.fillOpacity
  77. */
  78. /**
  79. * A separate color for the graph line. By default the line takes the
  80. * `color` of the series, but the lineColor setting allows setting a
  81. * separate color for the line without altering the `fillColor`.
  82. *
  83. * In styled mode, the line stroke can be set with the
  84. * `.highcharts-graph` class name.
  85. *
  86. * @sample {highcharts} highcharts/plotoptions/area-linecolor/
  87. * Dark gray line
  88. *
  89. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  90. * @product highcharts highstock
  91. * @apioption plotOptions.area.lineColor
  92. */
  93. /**
  94. * A separate color for the negative part of the area.
  95. *
  96. * In styled mode, a negative color is set with the
  97. * `.highcharts-negative` class name.
  98. *
  99. * @see [negativeColor](#plotOptions.area.negativeColor)
  100. *
  101. * @sample {highcharts} highcharts/css/series-negative-color/
  102. * Negative color in styled mode
  103. *
  104. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  105. * @since 3.0
  106. * @product highcharts
  107. * @apioption plotOptions.area.negativeFillColor
  108. */
  109. /**
  110. * Whether the whole area or just the line should respond to mouseover
  111. * tooltips and other mouse or touch events.
  112. *
  113. * @sample {highcharts|highstock} highcharts/plotoptions/area-trackbyarea/
  114. * Display the tooltip when the area is hovered
  115. *
  116. * @type {boolean}
  117. * @default false
  118. * @since 1.1.6
  119. * @product highcharts highstock
  120. * @apioption plotOptions.area.trackByArea
  121. */
  122. /**
  123. * When this is true, the series will not cause the Y axis to cross
  124. * the zero plane (or [threshold](#plotOptions.series.threshold) option)
  125. * unless the data actually crosses the plane.
  126. *
  127. * For example, if `softThreshold` is `false`, a series of 0, 1, 2,
  128. * 3 will make the Y axis show negative values according to the
  129. * `minPadding` option. If `softThreshold` is `true`, the Y axis starts
  130. * at 0.
  131. *
  132. * @since 4.1.9
  133. * @product highcharts highstock
  134. */
  135. softThreshold: false,
  136. /**
  137. * The Y axis value to serve as the base for the area, for
  138. * distinguishing between values above and below a threshold. The area
  139. * between the graph and the threshold is filled.
  140. *
  141. * * If a number is given, the Y axis will scale to the threshold.
  142. * * If `null`, the scaling behaves like a line series with fill between
  143. * the graph and the Y axis minimum.
  144. * * If `Infinity` or `-Infinity`, the area between the graph and the
  145. * corresponing Y axis extreme is filled (since v6.1.0).
  146. *
  147. * @sample {highcharts} highcharts/plotoptions/area-threshold/
  148. * A threshold of 100
  149. * @sample {highcharts} highcharts/plotoptions/area-threshold-infinity/
  150. * A threshold of Infinity
  151. *
  152. * @since 2.0
  153. * @product highcharts highstock
  154. */
  155. threshold: 0
  156. },
  157. /* eslint-disable valid-jsdoc */
  158. /**
  159. * @lends seriesTypes.area.prototype
  160. */
  161. {
  162. singleStacks: false,
  163. /**
  164. * Return an array of stacked points, where null and missing points are
  165. * replaced by dummy points in order for gaps to be drawn correctly in
  166. * stacks.
  167. * @private
  168. */
  169. getStackPoints: function (points) {
  170. var series = this, segment = [], keys = [], xAxis = this.xAxis, yAxis = this.yAxis, stack = yAxis.stacks[this.stackKey], pointMap = {}, seriesIndex = series.index, yAxisSeries = yAxis.series, seriesLength = yAxisSeries.length, visibleSeries, upOrDown = pick(yAxis.options.reversedStacks, true) ? 1 : -1, i;
  171. points = points || this.points;
  172. if (this.options.stacking) {
  173. for (i = 0; i < points.length; i++) {
  174. // Reset after point update (#7326)
  175. points[i].leftNull = points[i].rightNull = void 0;
  176. // Create a map where we can quickly look up the points by
  177. // their X values.
  178. pointMap[points[i].x] = points[i];
  179. }
  180. // Sort the keys (#1651)
  181. objectEach(stack, function (stackX, x) {
  182. // nulled after switching between
  183. // grouping and not (#1651, #2336)
  184. if (stackX.total !== null) {
  185. keys.push(x);
  186. }
  187. });
  188. keys.sort(function (a, b) {
  189. return a - b;
  190. });
  191. visibleSeries = yAxisSeries.map(function (s) {
  192. return s.visible;
  193. });
  194. keys.forEach(function (x, idx) {
  195. var y = 0, stackPoint, stackedValues;
  196. if (pointMap[x] && !pointMap[x].isNull) {
  197. segment.push(pointMap[x]);
  198. // Find left and right cliff. -1 goes left, 1 goes
  199. // right.
  200. [-1, 1].forEach(function (direction) {
  201. var nullName = direction === 1 ?
  202. 'rightNull' :
  203. 'leftNull', cliffName = direction === 1 ?
  204. 'rightCliff' :
  205. 'leftCliff', cliff = 0, otherStack = stack[keys[idx + direction]];
  206. // If there is a stack next to this one,
  207. // to the left or to the right...
  208. if (otherStack) {
  209. i = seriesIndex;
  210. // Can go either up or down,
  211. // depending on reversedStacks
  212. while (i >= 0 && i < seriesLength) {
  213. stackPoint = otherStack.points[i];
  214. if (!stackPoint) {
  215. // If the next point in this series
  216. // is missing, mark the point
  217. // with point.leftNull or
  218. // point.rightNull = true.
  219. if (i === seriesIndex) {
  220. pointMap[x][nullName] =
  221. true;
  222. // If there are missing points in
  223. // the next stack in any of the
  224. // series below this one, we need
  225. // to substract the missing values
  226. // and add a hiatus to the left or
  227. // right.
  228. }
  229. else if (visibleSeries[i]) {
  230. stackedValues =
  231. stack[x].points[i];
  232. if (stackedValues) {
  233. cliff -=
  234. stackedValues[1] -
  235. stackedValues[0];
  236. }
  237. }
  238. }
  239. // When reversedStacks is true, loop up,
  240. // else loop down
  241. i += upOrDown;
  242. }
  243. }
  244. pointMap[x][cliffName] = cliff;
  245. });
  246. // There is no point for this X value in this series, so we
  247. // insert a dummy point in order for the areas to be drawn
  248. // correctly.
  249. }
  250. else {
  251. // Loop down the stack to find the series below this
  252. // one that has a value (#1991)
  253. i = seriesIndex;
  254. while (i >= 0 && i < seriesLength) {
  255. stackPoint = stack[x].points[i];
  256. if (stackPoint) {
  257. y = stackPoint[1];
  258. break;
  259. }
  260. // When reversedStacks is true, loop up, else loop
  261. // down
  262. i += upOrDown;
  263. }
  264. y = yAxis.translate(// #6272
  265. y, 0, 1, 0, 1);
  266. segment.push({
  267. isNull: true,
  268. plotX: xAxis.translate(// #6272
  269. x, 0, 0, 0, 1),
  270. x: x,
  271. plotY: y,
  272. yBottom: y
  273. });
  274. }
  275. });
  276. }
  277. return segment;
  278. },
  279. /**
  280. * @private
  281. */
  282. getGraphPath: function (points) {
  283. var getGraphPath = Series.prototype.getGraphPath, graphPath, options = this.options, stacking = options.stacking, yAxis = this.yAxis, topPath, bottomPath, bottomPoints = [], graphPoints = [], seriesIndex = this.index, i, areaPath, plotX, stacks = yAxis.stacks[this.stackKey], threshold = options.threshold, translatedThreshold = Math.round(// #10909
  284. yAxis.getThreshold(options.threshold)), isNull, yBottom, connectNulls = pick(// #10574
  285. options.connectNulls, stacking === 'percent'),
  286. // To display null points in underlying stacked series, this
  287. // series graph must be broken, and the area also fall down to
  288. // fill the gap left by the null point. #2069
  289. addDummyPoints = function (i, otherI, side) {
  290. var point = points[i], stackedValues = stacking &&
  291. stacks[point.x].points[seriesIndex], nullVal = point[side + 'Null'] || 0, cliffVal = point[side + 'Cliff'] || 0, top, bottom, isNull = true;
  292. if (cliffVal || nullVal) {
  293. top = (nullVal ?
  294. stackedValues[0] :
  295. stackedValues[1]) + cliffVal;
  296. bottom = stackedValues[0] + cliffVal;
  297. isNull = !!nullVal;
  298. }
  299. else if (!stacking &&
  300. points[otherI] &&
  301. points[otherI].isNull) {
  302. top = bottom = threshold;
  303. }
  304. // Add to the top and bottom line of the area
  305. if (typeof top !== 'undefined') {
  306. graphPoints.push({
  307. plotX: plotX,
  308. plotY: top === null ?
  309. translatedThreshold :
  310. yAxis.getThreshold(top),
  311. isNull: isNull,
  312. isCliff: true
  313. });
  314. bottomPoints.push({
  315. plotX: plotX,
  316. plotY: bottom === null ?
  317. translatedThreshold :
  318. yAxis.getThreshold(bottom),
  319. doCurve: false // #1041, gaps in areaspline areas
  320. });
  321. }
  322. };
  323. // Find what points to use
  324. points = points || this.points;
  325. // Fill in missing points
  326. if (stacking) {
  327. points = this.getStackPoints(points);
  328. }
  329. for (i = 0; i < points.length; i++) {
  330. // Reset after series.update of stacking property (#12033)
  331. if (!stacking) {
  332. points[i].leftCliff = points[i].rightCliff =
  333. points[i].leftNull = points[i].rightNull = void 0;
  334. }
  335. isNull = points[i].isNull;
  336. plotX = pick(points[i].rectPlotX, points[i].plotX);
  337. yBottom = pick(points[i].yBottom, translatedThreshold);
  338. if (!isNull || connectNulls) {
  339. if (!connectNulls) {
  340. addDummyPoints(i, i - 1, 'left');
  341. }
  342. // Skip null point when stacking is false and connectNulls
  343. // true
  344. if (!(isNull && !stacking && connectNulls)) {
  345. graphPoints.push(points[i]);
  346. bottomPoints.push({
  347. x: i,
  348. plotX: plotX,
  349. plotY: yBottom
  350. });
  351. }
  352. if (!connectNulls) {
  353. addDummyPoints(i, i + 1, 'right');
  354. }
  355. }
  356. }
  357. topPath = getGraphPath.call(this, graphPoints, true, true);
  358. bottomPoints.reversed = true;
  359. bottomPath = getGraphPath.call(this, bottomPoints, true, true);
  360. if (bottomPath.length) {
  361. bottomPath[0] = 'L';
  362. }
  363. areaPath = topPath.concat(bottomPath);
  364. // TODO: don't set leftCliff and rightCliff when connectNulls?
  365. graphPath = getGraphPath
  366. .call(this, graphPoints, false, connectNulls);
  367. areaPath.xMap = topPath.xMap;
  368. this.areaPath = areaPath;
  369. return graphPath;
  370. },
  371. /**
  372. * Draw the graph and the underlying area. This method calls the Series
  373. * base function and adds the area. The areaPath is calculated in the
  374. * getSegmentPath method called from Series.prototype.drawGraph.
  375. * @private
  376. */
  377. drawGraph: function () {
  378. // Define or reset areaPath
  379. this.areaPath = [];
  380. // Call the base method
  381. Series.prototype.drawGraph.apply(this);
  382. // Define local variables
  383. var series = this, areaPath = this.areaPath, options = this.options, zones = this.zones, props = [[
  384. 'area',
  385. 'highcharts-area',
  386. this.color,
  387. options.fillColor
  388. ]]; // area name, main color, fill color
  389. zones.forEach(function (zone, i) {
  390. props.push([
  391. 'zone-area-' + i,
  392. 'highcharts-area highcharts-zone-area-' + i + ' ' +
  393. zone.className,
  394. zone.color || series.color,
  395. zone.fillColor || options.fillColor
  396. ]);
  397. });
  398. props.forEach(function (prop) {
  399. var areaKey = prop[0], area = series[areaKey], verb = area ? 'animate' : 'attr', attribs = {};
  400. // Create or update the area
  401. if (area) { // update
  402. area.endX = series.preventGraphAnimation ?
  403. null :
  404. areaPath.xMap;
  405. area.animate({ d: areaPath });
  406. }
  407. else { // create
  408. attribs.zIndex = 0; // #1069
  409. area = series[areaKey] = series.chart.renderer
  410. .path(areaPath)
  411. .addClass(prop[1])
  412. .add(series.group);
  413. area.isArea = true;
  414. }
  415. if (!series.chart.styledMode) {
  416. attribs.fill = pick(prop[3], color(prop[2])
  417. .setOpacity(pick(options.fillOpacity, 0.75))
  418. .get());
  419. }
  420. area[verb](attribs);
  421. area.startX = areaPath.xMap;
  422. area.shiftUnit = options.step ? 2 : 1;
  423. });
  424. },
  425. drawLegendSymbol: LegendSymbolMixin.drawRectangle
  426. });
  427. /* eslint-enable valid-jsdoc */
  428. /**
  429. * A `area` series. If the [type](#series.area.type) option is not
  430. * specified, it is inherited from [chart.type](#chart.type).
  431. *
  432. * @extends series,plotOptions.area
  433. * @excluding dataParser, dataURL, useOhlcData
  434. * @product highcharts highstock
  435. * @apioption series.area
  436. */
  437. /**
  438. * An array of data points for the series. For the `area` series type,
  439. * points can be given in the following ways:
  440. *
  441. * 1. An array of numerical values. In this case, the numerical values will be
  442. * interpreted as `y` options. The `x` values will be automatically
  443. * calculated, either starting at 0 and incremented by 1, or from
  444. * `pointStart` * and `pointInterval` given in the series options. If the
  445. * axis has categories, these will be used. Example:
  446. * ```js
  447. * data: [0, 5, 3, 5]
  448. * ```
  449. *
  450. * 2. An array of arrays with 2 values. In this case, the values correspond to
  451. * `x,y`. If the first value is a string, it is applied as the name of the
  452. * point, and the `x` value is inferred.
  453. * ```js
  454. * data: [
  455. * [0, 9],
  456. * [1, 7],
  457. * [2, 6]
  458. * ]
  459. * ```
  460. *
  461. * 3. An array of objects with named values. The following snippet shows only a
  462. * few settings, see the complete options set below. If the total number of
  463. * data points exceeds the series'
  464. * [turboThreshold](#series.area.turboThreshold), this option is not
  465. * available.
  466. * ```js
  467. * data: [{
  468. * x: 1,
  469. * y: 9,
  470. * name: "Point2",
  471. * color: "#00FF00"
  472. * }, {
  473. * x: 1,
  474. * y: 6,
  475. * name: "Point1",
  476. * color: "#FF00FF"
  477. * }]
  478. * ```
  479. *
  480. * @sample {highcharts} highcharts/chart/reflow-true/
  481. * Numerical values
  482. * @sample {highcharts} highcharts/series/data-array-of-arrays/
  483. * Arrays of numeric x and y
  484. * @sample {highcharts} highcharts/series/data-array-of-arrays-datetime/
  485. * Arrays of datetime x and y
  486. * @sample {highcharts} highcharts/series/data-array-of-name-value/
  487. * Arrays of point.name and y
  488. * @sample {highcharts} highcharts/series/data-array-of-objects/
  489. * Config objects
  490. *
  491. * @type {Array<number|Array<(number|string),(number|null)>|null|*>}
  492. * @extends series.line.data
  493. * @product highcharts highstock
  494. * @apioption series.area.data
  495. */
  496. ''; // adds doclets above to transpilat