AreaSeries.js 19 KB

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