apexcharts 6.1.0 → 6.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (88) hide show
  1. package/dist/apexcharts.common.js +2 -2
  2. package/dist/apexcharts.esm.js +423 -63
  3. package/dist/apexcharts.js +423 -63
  4. package/dist/apexcharts.min.js +2 -2
  5. package/dist/apexcharts.ssr.common.js +2 -2
  6. package/dist/apexcharts.ssr.esm.js +423 -63
  7. package/dist/area.common.js +2 -2
  8. package/dist/area.esm.js +170 -2
  9. package/dist/bar.common.js +1 -1
  10. package/dist/bar.esm.js +1 -1
  11. package/dist/boxPlot.common.js +1 -1
  12. package/dist/boxPlot.esm.js +1 -1
  13. package/dist/bubble.common.js +2 -2
  14. package/dist/bubble.esm.js +170 -2
  15. package/dist/candlestick.common.js +1 -1
  16. package/dist/candlestick.esm.js +1 -1
  17. package/dist/column.common.js +1 -1
  18. package/dist/column.esm.js +1 -1
  19. package/dist/core.common.js +2 -2
  20. package/dist/core.esm.js +150 -50
  21. package/dist/donut.common.js +1 -1
  22. package/dist/donut.esm.js +1 -1
  23. package/dist/features/all.common.js +2 -2
  24. package/dist/features/all.esm.js +105 -13
  25. package/dist/features/annotations.common.js +1 -1
  26. package/dist/features/annotations.esm.js +1 -1
  27. package/dist/features/context-menu.common.js +1 -1
  28. package/dist/features/context-menu.esm.js +1 -1
  29. package/dist/features/drilldown.common.js +1 -1
  30. package/dist/features/drilldown.esm.js +1 -1
  31. package/dist/features/exports.common.js +1 -1
  32. package/dist/features/exports.esm.js +1 -1
  33. package/dist/features/facet.common.js +1 -1
  34. package/dist/features/facet.esm.js +1 -1
  35. package/dist/features/history.common.js +1 -1
  36. package/dist/features/history.esm.js +1 -1
  37. package/dist/features/ink.common.js +1 -1
  38. package/dist/features/ink.esm.js +1 -1
  39. package/dist/features/keyboard.common.js +1 -1
  40. package/dist/features/keyboard.esm.js +1 -1
  41. package/dist/features/legend.common.js +1 -1
  42. package/dist/features/legend.esm.js +1 -1
  43. package/dist/features/link.common.js +1 -1
  44. package/dist/features/link.esm.js +1 -1
  45. package/dist/features/marks.common.js +1 -1
  46. package/dist/features/marks.esm.js +1 -1
  47. package/dist/features/measure.common.js +1 -1
  48. package/dist/features/measure.esm.js +1 -1
  49. package/dist/features/morph.common.js +1 -1
  50. package/dist/features/morph.esm.js +1 -1
  51. package/dist/features/perspectives.common.js +1 -1
  52. package/dist/features/perspectives.esm.js +1 -1
  53. package/dist/features/renderer-canvas.common.js +2 -2
  54. package/dist/features/renderer-canvas.esm.js +105 -13
  55. package/dist/features/storyboard.common.js +1 -1
  56. package/dist/features/storyboard.esm.js +1 -1
  57. package/dist/features/toolbar.common.js +1 -1
  58. package/dist/features/toolbar.esm.js +1 -1
  59. package/dist/features/weave.common.js +1 -1
  60. package/dist/features/weave.esm.js +1 -1
  61. package/dist/heatmap.common.js +1 -1
  62. package/dist/heatmap.esm.js +1 -1
  63. package/dist/line.common.js +2 -2
  64. package/dist/line.esm.js +170 -2
  65. package/dist/pie.common.js +1 -1
  66. package/dist/pie.esm.js +1 -1
  67. package/dist/polarArea.common.js +1 -1
  68. package/dist/polarArea.esm.js +1 -1
  69. package/dist/radar.common.js +1 -1
  70. package/dist/radar.esm.js +1 -1
  71. package/dist/radialBar.common.js +1 -1
  72. package/dist/radialBar.esm.js +1 -1
  73. package/dist/rangeArea.common.js +2 -2
  74. package/dist/rangeArea.esm.js +170 -2
  75. package/dist/rangeBar.common.js +1 -1
  76. package/dist/rangeBar.esm.js +1 -1
  77. package/dist/scatter.common.js +2 -2
  78. package/dist/scatter.esm.js +170 -2
  79. package/dist/treemap.common.js +1 -1
  80. package/dist/treemap.esm.js +1 -1
  81. package/dist/violin.common.js +1 -1
  82. package/dist/violin.esm.js +1 -1
  83. package/package.json +1 -1
  84. package/src/charts/Line.js +220 -1
  85. package/src/charts/Scatter.js +159 -56
  86. package/src/modules/Markers.js +44 -12
  87. package/src/renderers/canvas/CanvasCompositor.js +125 -22
  88. package/src/renderers/canvas/CanvasGraphics.js +8 -0
@@ -51,7 +51,7 @@ var __async = (__this, __arguments, generator) => {
51
51
  });
52
52
  };
53
53
  /*!
54
- * ApexCharts v6.1.0
54
+ * ApexCharts v6.2.0
55
55
  * (c) 2018-2026 ApexCharts
56
56
  */
57
57
  class Environment {
@@ -9907,6 +9907,18 @@ class Markers {
9907
9907
  this.ctx = ctx;
9908
9908
  this._filters = new Filters(this.w);
9909
9909
  this._graphics = new Graphics(this.w, this.ctx);
9910
+ this._seriesWrap = null;
9911
+ this._seriesWrapIndex = -1;
9912
+ }
9913
+ /**
9914
+ * Invalidate the cached per-series wrap group. Callers that drive
9915
+ * plotChartMarkers point-by-point (Line) must call this when a series'
9916
+ * element tree is (re)created, so a later render never appends markers to a
9917
+ * detached group from the previous pass.
9918
+ */
9919
+ resetSeriesWrapCache() {
9920
+ this._seriesWrap = null;
9921
+ this._seriesWrapIndex = -1;
9910
9922
  }
9911
9923
  setGlobalMarkerSize() {
9912
9924
  const w = this.w;
@@ -9995,14 +10007,23 @@ class Markers {
9995
10007
  if (!invalidMarker) {
9996
10008
  const shouldCreateMarkerWrap = w.globals.markers.size[seriesIndex] > 0 || alwaysDrawMarker || hasDiscreteMarkers;
9997
10009
  if (shouldCreateMarkerWrap && !elMarkersWrap) {
9998
- elMarkersWrap = emit.group({
9999
- class: alwaysDrawMarker || hasDiscreteMarkers ? "" : "apexcharts-series-markers"
10000
- });
10001
- elMarkersWrap.attr(
10002
- "clip-path",
10003
- `url(#gridRectMarkerMask${w.globals.cuid})`
10004
- );
10005
- this.setupMarkerDelegation(elMarkersWrap);
10010
+ const standardWrap = !alwaysDrawMarker && !hasDiscreteMarkers;
10011
+ if (standardWrap && this._seriesWrap && this._seriesWrapIndex === seriesIndex) {
10012
+ elMarkersWrap = this._seriesWrap;
10013
+ } else {
10014
+ elMarkersWrap = emit.group({
10015
+ class: standardWrap ? "apexcharts-series-markers" : ""
10016
+ });
10017
+ elMarkersWrap.attr(
10018
+ "clip-path",
10019
+ `url(#gridRectMarkerMask${w.globals.cuid})`
10020
+ );
10021
+ this.setupMarkerDelegation(elMarkersWrap);
10022
+ if (standardWrap) {
10023
+ this._seriesWrap = elMarkersWrap;
10024
+ this._seriesWrapIndex = seriesIndex;
10025
+ }
10026
+ }
10006
10027
  }
10007
10028
  markerElement = emit.drawMarker(p.x[q], p.y[q], opts);
10008
10029
  markerElement.attr("rel", dataPointIndex);
@@ -10162,6 +10183,9 @@ class Scatter {
10162
10183
  this.fill = new Fill(this.w);
10163
10184
  this.markers = new Markers(this.w, this.ctx);
10164
10185
  this.graphics = new Graphics(this.w);
10186
+ this._elPointsWrap = null;
10187
+ this._elPointsWrapParent = null;
10188
+ this._perSeries = null;
10165
10189
  }
10166
10190
  /**
10167
10191
  * @param {Element} elSeries
@@ -10176,11 +10200,21 @@ class Scatter {
10176
10200
  const pointsPos = opts.pointsPos;
10177
10201
  const zRatio = opts.zRatio;
10178
10202
  const elPointsMain = opts.elParent;
10179
- const elPointsWrap = emit.group({
10180
- class: `apexcharts-series-markers apexcharts-series-${w.config.chart.type}`
10181
- });
10182
- elPointsWrap.attr("clip-path", `url(#gridRectMarkerMask${w.globals.cuid})`);
10183
- this.markers.setupMarkerDelegation(elPointsWrap);
10203
+ let elPointsWrap = this._elPointsWrap;
10204
+ if (!elPointsWrap || this._elPointsWrapParent !== elPointsMain) {
10205
+ elPointsWrap = emit.group({
10206
+ class: `apexcharts-series-markers apexcharts-series-${w.config.chart.type}`
10207
+ });
10208
+ elPointsWrap.attr(
10209
+ "clip-path",
10210
+ `url(#gridRectMarkerMask${w.globals.cuid})`
10211
+ );
10212
+ this.markers.setupMarkerDelegation(elPointsWrap);
10213
+ elPointsMain.add(elPointsWrap);
10214
+ this._elPointsWrap = elPointsWrap;
10215
+ this._elPointsWrapParent = elPointsMain;
10216
+ this._perSeries = this._buildPerSeriesCache(realIndex, emit);
10217
+ }
10184
10218
  if (Array.isArray(pointsPos.x)) {
10185
10219
  for (let q = 0; q < pointsPos.x.length; q++) {
10186
10220
  let dataPointIndex = j + 1;
@@ -10224,10 +10258,39 @@ class Scatter {
10224
10258
  w.globals.pointsArray[realIndex][dataPointIndex] = [x, y];
10225
10259
  }
10226
10260
  }
10227
- elPointsMain.add(elPointsWrap);
10228
10261
  }
10229
10262
  }
10230
10263
  }
10264
+ /**
10265
+ * Per-series constants for drawPoint's hot path. Everything here is uniform
10266
+ * across the points of one series; computing or allocating it per point is
10267
+ * measurable overhead at 20k-50k points.
10268
+ * @param {number} realIndex
10269
+ * @param {any} emit
10270
+ */
10271
+ _buildPerSeriesCache(realIndex, emit) {
10272
+ var _a;
10273
+ const w = this.w;
10274
+ return {
10275
+ realIndex,
10276
+ emit,
10277
+ isBubble: w.config.chart.type === "bubble" || w.globals.comboCharts && w.config.series[realIndex] && /** @type {Record<string,any>} */
10278
+ w.config.series[realIndex].type === "bubble",
10279
+ // discrete markers vary per point; they disable both caches below
10280
+ canCacheConfig: !w.config.markers.discrete.length,
10281
+ /** @type {any} lazily built shared marker config (first point) */
10282
+ markerConfig: null,
10283
+ /** @type {boolean|undefined} lazily resolved on the first fillPath call */
10284
+ fillCacheable: void 0,
10285
+ /** @type {any} cached fillPath result (undefined = not cached yet) */
10286
+ fillCircle: void 0,
10287
+ dropShadowEnabled: w.config.chart.dropShadow.enabled,
10288
+ doInitialAnim: this.initialAnim && !w.globals.dataChanged && !w.globals.resized,
10289
+ jitter: (_a = w.config.plotOptions.scatter) == null ? void 0 : _a.jitter,
10290
+ /** @type {any} lazily built pop-animation constants */
10291
+ anim: null
10292
+ };
10293
+ }
10231
10294
  /**
10232
10295
  * @param {number} x
10233
10296
  * @param {number} y
@@ -10237,42 +10300,76 @@ class Scatter {
10237
10300
  * @param {number} j
10238
10301
  */
10239
10302
  drawPoint(x, y, radius, realIndex, dataPointIndex, j) {
10240
- var _a, _b;
10303
+ var _a;
10241
10304
  const w = this.w;
10242
10305
  const i = realIndex;
10243
10306
  const anim = this.anim;
10244
10307
  const filters = this.filters;
10245
10308
  const fill = this.fill;
10246
10309
  const markers = this.markers;
10247
- const graphics = this.graphics;
10248
- const emit = seriesEmitter(this.ctx, graphics);
10249
- const markerConfig = markers.getMarkerConfig({
10250
- cssClass: "apexcharts-marker",
10251
- seriesIndex: i,
10252
- dataPointIndex,
10253
- radius: w.config.chart.type === "bubble" || w.globals.comboCharts && w.config.series[realIndex] && /** @type {Record<string,any>} */
10254
- w.config.series[realIndex].type === "bubble" ? radius : null
10255
- });
10256
- let pathFillCircle = fill.fillPath({
10257
- seriesNumber: realIndex,
10258
- dataPointIndex,
10259
- color: markerConfig.pointFillColor,
10260
- patternUnits: "objectBoundingBox",
10261
- value: w.seriesData.series[realIndex][j]
10262
- });
10263
- const el = emit.drawMarker(x, y, markerConfig);
10310
+ let ps = this._perSeries;
10311
+ if (!ps || ps.realIndex !== realIndex) {
10312
+ ps = this._perSeries = this._buildPerSeriesCache(
10313
+ realIndex,
10314
+ seriesEmitter(this.ctx, this.graphics)
10315
+ );
10316
+ }
10317
+ const emit = ps.emit;
10318
+ let markerConfig;
10319
+ if (ps.canCacheConfig) {
10320
+ if (!ps.markerConfig) {
10321
+ ps.markerConfig = markers.getMarkerConfig({
10322
+ cssClass: "apexcharts-marker",
10323
+ seriesIndex: i,
10324
+ dataPointIndex,
10325
+ radius: ps.isBubble ? radius : null
10326
+ });
10327
+ }
10328
+ markerConfig = ps.markerConfig;
10329
+ if (ps.isBubble) {
10330
+ markerConfig.pSize = radius;
10331
+ markerConfig.pRadius = radius;
10332
+ }
10333
+ } else {
10334
+ markerConfig = markers.getMarkerConfig({
10335
+ cssClass: "apexcharts-marker",
10336
+ seriesIndex: i,
10337
+ dataPointIndex,
10338
+ radius: ps.isBubble ? radius : null
10339
+ });
10340
+ }
10264
10341
  const _si = (
10265
10342
  /** @type {Record<string,any>} */
10266
10343
  w.config.series[i]
10267
10344
  );
10268
- if (_si.data[dataPointIndex]) {
10269
- if (_si.data[dataPointIndex].fillColor) {
10270
- pathFillCircle = _si.data[dataPointIndex].fillColor;
10345
+ const dataItem = _si.data[dataPointIndex];
10346
+ let pathFillCircle;
10347
+ if (ps.fillCircle !== void 0) {
10348
+ pathFillCircle = ps.fillCircle;
10349
+ } else {
10350
+ pathFillCircle = fill.fillPath({
10351
+ seriesNumber: realIndex,
10352
+ dataPointIndex,
10353
+ color: markerConfig.pointFillColor,
10354
+ patternUnits: "objectBoundingBox",
10355
+ value: w.seriesData.series[realIndex][j]
10356
+ });
10357
+ if (ps.fillCacheable === void 0) {
10358
+ ps.fillCacheable = ps.canCacheConfig && fill.getFillType(realIndex) === "solid" && typeof markerConfig.pointFillColor === "string" && !!markerConfig.pointFillColor;
10359
+ }
10360
+ if (ps.fillCacheable && !(dataItem == null ? void 0 : dataItem.fillColor)) {
10361
+ ps.fillCircle = pathFillCircle;
10271
10362
  }
10272
10363
  }
10273
- const jt = (_a = w.config.plotOptions.scatter) == null ? void 0 : _a.jitter;
10364
+ const el = emit.drawMarker(x, y, markerConfig);
10365
+ if (dataItem == null ? void 0 : dataItem.fillColor) {
10366
+ pathFillCircle = dataItem.fillColor;
10367
+ }
10368
+ const jt = ps.jitter;
10274
10369
  if ((jt == null ? void 0 : jt.enabled) && jt.distributed && w.globals.colors.length) {
10275
- const bandIdx = Math.round((_b = w.seriesData.seriesX[realIndex]) == null ? void 0 : _b[dataPointIndex]);
10370
+ const bandIdx = Math.round(
10371
+ (_a = w.seriesData.seriesX[realIndex]) == null ? void 0 : _a[dataPointIndex]
10372
+ );
10276
10373
  if (!isNaN(bandIdx)) {
10277
10374
  pathFillCircle = w.globals.colors[bandIdx % w.globals.colors.length];
10278
10375
  }
@@ -10280,24 +10377,28 @@ class Scatter {
10280
10377
  el.attr({
10281
10378
  fill: pathFillCircle
10282
10379
  });
10283
- if (w.config.chart.dropShadow.enabled) {
10380
+ if (ps.dropShadowEnabled) {
10284
10381
  const dropShadow = w.config.chart.dropShadow;
10285
10382
  filters.dropShadow(el, dropShadow, realIndex);
10286
10383
  }
10287
- if (this.initialAnim && !w.globals.dataChanged && !w.globals.resized) {
10288
- const animCfg = w.config.chart.animations;
10289
- const popSpeed = animCfg.speed;
10290
- const totalPoints = w.globals.dataPoints || 1;
10291
- const gradCfg = animCfg.animateGradually;
10292
- const gradEnabled = gradCfg && gradCfg.enabled !== false;
10293
- const baseDelay = gradEnabled ? Math.min(20, popSpeed * 0.5 / Math.max(1, totalPoints)) : 0;
10384
+ if (ps.doInitialAnim) {
10385
+ if (!ps.anim) {
10386
+ const animCfg = w.config.chart.animations;
10387
+ const totalPoints = w.globals.dataPoints || 1;
10388
+ const gradCfg = animCfg.animateGradually;
10389
+ const gradEnabled = gradCfg && gradCfg.enabled !== false;
10390
+ ps.anim = {
10391
+ popSpeed: animCfg.speed,
10392
+ baseDelay: gradEnabled ? Math.min(20, animCfg.speed * 0.5 / Math.max(1, totalPoints)) : 0
10393
+ };
10394
+ }
10294
10395
  const delay = computeStagger({
10295
- style: baseDelay > 0 ? "sequential" : "none",
10396
+ style: ps.anim.baseDelay > 0 ? "sequential" : "none",
10296
10397
  index: dataPointIndex,
10297
- baseDelay
10398
+ baseDelay: ps.anim.baseDelay
10298
10399
  });
10299
10400
  anim.animatePop(el, {
10300
- speed: popSpeed,
10401
+ speed: ps.anim.popSpeed,
10301
10402
  delay,
10302
10403
  onComplete: () => anim.animationCompleted(el)
10303
10404
  });
@@ -10311,7 +10412,6 @@ class Scatter {
10311
10412
  "default-marker-size": markerConfig.pSize
10312
10413
  });
10313
10414
  filters.setSelectionFilter(el, realIndex, dataPointIndex);
10314
- el.node.classList.add("apexcharts-marker");
10315
10415
  return el;
10316
10416
  }
10317
10417
  /**
@@ -34689,6 +34789,11 @@ class CanvasGraphics {
34689
34789
  renderPaths(opts) {
34690
34790
  const cmd = this._cmd("path", opts.realIndex);
34691
34791
  cmd.d = opts.pathTo;
34792
+ if (opts.pathToNumeric) {
34793
+ cmd.nxs = opts.pathToNumeric.xs;
34794
+ cmd.nys = opts.pathToNumeric.ys;
34795
+ cmd.ncloseY = opts.pathToNumeric.closeY;
34796
+ }
34692
34797
  cmd.stroke = opts.stroke;
34693
34798
  cmd.strokeWidth = opts.strokeWidth;
34694
34799
  cmd.fill = opts.fill;
@@ -34799,6 +34904,12 @@ class CanvasCompositor {
34799
34904
  this._dpr = 1;
34800
34905
  this._dim = null;
34801
34906
  this._alpha = 1;
34907
+ this._unitPaths = /* @__PURE__ */ new Map();
34908
+ this._markerBatches = 0;
34909
+ }
34910
+ /** Marker style-batches applied during the last paint() (dev/test hook). */
34911
+ markerBatchCount() {
34912
+ return this._markerBatches;
34802
34913
  }
34803
34914
  /**
34804
34915
  * Opacity multiplier for a series index under the active dim spec: 1 for the
@@ -34913,10 +35024,38 @@ class CanvasCompositor {
34913
35024
  this._alpha = 1;
34914
35025
  }
34915
35026
  /**
35027
+ * Reusable unit Path2D for a (shape, size): the shape's geometry built at the
35028
+ * origin once, then translated per marker via setTransform. Returns null when
35029
+ * the geometry string cannot be parsed.
35030
+ * @param {any} shim
35031
+ * @param {number} shapeId
35032
+ * @param {number} size
35033
+ * @returns {any}
35034
+ */
35035
+ _unitPath(shim2, shapeId, size) {
35036
+ const key = shapeId + "|" + size;
35037
+ let p = this._unitPaths.get(key);
35038
+ if (p === void 0) {
35039
+ try {
35040
+ p = new Path2D(shim2.markerPath(0, 0, shapeId, size));
35041
+ } catch (e) {
35042
+ p = null;
35043
+ }
35044
+ this._unitPaths.set(key, p);
35045
+ }
35046
+ return p;
35047
+ }
35048
+ /**
35049
+ * Markers paint as STYLE BATCHES: one fill/stroke state application per run
35050
+ * of consecutive same-style markers (a uniform single-series scatter is
35051
+ * exactly one batch), then per-marker geometry inside the run. Per-marker
35052
+ * geometry stays painter's-ordered (fill+stroke per marker) so overlapping
35053
+ * semi-transparent markers composite exactly as SVG does.
34916
35054
  * @param {any} ctx
34917
35055
  * @param {any} shim
34918
35056
  */
34919
35057
  _paintMarkers(ctx, shim2) {
35058
+ this._markerBatches = 0;
34920
35059
  const n = shim2.markerCount();
34921
35060
  if (!n) return;
34922
35061
  const mx = shim2._mx;
@@ -34935,11 +35074,12 @@ class CanvasCompositor {
34935
35074
  i++;
34936
35075
  continue;
34937
35076
  }
35077
+ const doFill = this._applyFill(ctx, style);
35078
+ const doStroke = this._applyStroke(ctx, style);
35079
+ this._markerBatches++;
35080
+ const baseFillA = style.fillOpacity == null ? 1 : Number(style.fillOpacity);
35081
+ const baseStrokeA = style.strokeOpacity == null ? 1 : Number(style.strokeOpacity);
34938
35082
  if (shapeId === 0) {
34939
- const doFill = this._applyFill(ctx, style);
34940
- const doStroke = this._applyStroke(ctx, style);
34941
- const baseFillA = style.fillOpacity == null ? 1 : Number(style.fillOpacity);
34942
- const baseStrokeA = style.strokeOpacity == null ? 1 : Number(style.strokeOpacity);
34943
35083
  let j = i;
34944
35084
  while (j < n && mshape[j] === 0 && mstyle[j] === styleId) {
34945
35085
  const r = msize[j] || 0;
@@ -34967,19 +35107,66 @@ class CanvasCompositor {
34967
35107
  ctx.globalAlpha = 1;
34968
35108
  i = j;
34969
35109
  } else {
34970
- const y = my[i];
34971
- if (y === y && msize[i] > 0) {
34972
- this._alpha = dimming ? this._seriesAlpha(shim2.markerSeries(i)) : 1;
34973
- try {
34974
- const p = new Path2D(shim2.markerPath(mx[i], y, shapeId, msize[i]));
34975
- this._fillStrokePath(ctx, style, p);
34976
- } catch (e) {
35110
+ const dpr = this._dpr;
35111
+ const m = this._margin;
35112
+ let j = i;
35113
+ while (j < n && mshape[j] === shapeId && mstyle[j] === styleId) {
35114
+ const y = my[j];
35115
+ const size = msize[j];
35116
+ if (y === y && size > 0) {
35117
+ const p = this._unitPath(shim2, shapeId, size);
35118
+ if (p) {
35119
+ ctx.setTransform(dpr, 0, 0, dpr, (m + mx[j]) * dpr, (m + y) * dpr);
35120
+ const f = dimming ? this._seriesAlpha(shim2.markerSeries(j)) : 1;
35121
+ if (doFill) {
35122
+ ctx.globalAlpha = baseFillA * f;
35123
+ ctx.fill(p);
35124
+ }
35125
+ if (doStroke) {
35126
+ ctx.globalAlpha = baseStrokeA * f;
35127
+ ctx.stroke(p);
35128
+ }
35129
+ }
34977
35130
  }
35131
+ j++;
34978
35132
  }
34979
- i++;
35133
+ ctx.setTransform(dpr, 0, 0, dpr, m * dpr, m * dpr);
35134
+ ctx.globalAlpha = 1;
35135
+ i = j;
34980
35136
  }
34981
35137
  }
34982
35138
  }
35139
+ /**
35140
+ * Paint a series path from its numeric fast-path coords: a direct
35141
+ * moveTo/lineTo loop over the typed arrays, no Path2D and no d-string
35142
+ * parse. `ncloseY` (areas) closes the polygon down to the baseline exactly
35143
+ * like the string form's `L xLast bottom L x0 bottom z` tail.
35144
+ * @param {any} ctx
35145
+ * @param {any} cmd
35146
+ */
35147
+ _paintNumericPath(ctx, cmd) {
35148
+ const xs = cmd.nxs;
35149
+ const ys = cmd.nys;
35150
+ const n = xs.length;
35151
+ if (!n) return;
35152
+ ctx.beginPath();
35153
+ ctx.moveTo(xs[0], ys[0]);
35154
+ for (let k = 1; k < n; k++) {
35155
+ ctx.lineTo(xs[k], ys[k]);
35156
+ }
35157
+ if (cmd.ncloseY != null) {
35158
+ ctx.lineTo(xs[n - 1], cmd.ncloseY);
35159
+ ctx.lineTo(xs[0], cmd.ncloseY);
35160
+ ctx.closePath();
35161
+ }
35162
+ if (this._applyFill(ctx, cmd)) {
35163
+ ctx.fill(cmd.fillRule === "evenodd" ? "evenodd" : "nonzero");
35164
+ }
35165
+ if (this._applyStroke(ctx, cmd)) {
35166
+ ctx.stroke();
35167
+ }
35168
+ ctx.globalAlpha = 1;
35169
+ }
34983
35170
  /**
34984
35171
  * @param {any} ctx
34985
35172
  * @param {any} cmd style-bearing flat command
@@ -34987,6 +35174,10 @@ class CanvasCompositor {
34987
35174
  _paintOne(ctx, cmd) {
34988
35175
  switch (cmd.tag) {
34989
35176
  case "path": {
35177
+ if (cmd.nxs) {
35178
+ this._paintNumericPath(ctx, cmd);
35179
+ break;
35180
+ }
34990
35181
  if (!cmd.d) return;
34991
35182
  if (!cmd.path2d) {
34992
35183
  try {
@@ -35111,6 +35302,7 @@ class CanvasCompositor {
35111
35302
  this._host = null;
35112
35303
  this._canvas = null;
35113
35304
  this._c2d = null;
35305
+ this._unitPaths.clear();
35114
35306
  }
35115
35307
  }
35116
35308
  class CanvasRenderer {
@@ -42992,6 +43184,7 @@ class Line {
42992
43184
  this.elSeries = null;
42993
43185
  this.elPointsMain = null;
42994
43186
  this.elDataLabelsWrap = null;
43187
+ this._elLastPointsWrap = null;
42995
43188
  }
42996
43189
  /**
42997
43190
  * @param {any[]} series
@@ -43192,6 +43385,8 @@ class Line {
43192
43385
  class: "apexcharts-series-markers-wrap",
43193
43386
  "data:realIndex": realIndex
43194
43387
  });
43388
+ this.markers.resetSeriesWrapCache();
43389
+ this._elLastPointsWrap = null;
43195
43390
  if (w.globals.hasNullValues) {
43196
43391
  const firstPoint = this.markers.plotChartMarkers({
43197
43392
  pointsPos: {
@@ -43348,6 +43543,7 @@ class Line {
43348
43543
  dataChangeSpeed: w.config.chart.animations.dynamicAnimation.speed,
43349
43544
  className: `apexcharts-${type}`
43350
43545
  };
43546
+ const numericXY = paths.numericXY;
43351
43547
  if (type === "area") {
43352
43548
  const pathFill = fill.fillPath({
43353
43549
  seriesNumber: realIndex
@@ -43356,6 +43552,11 @@ class Line {
43356
43552
  const renderedPath = emit.renderPaths(__spreadProps(__spreadValues({}, defaultRenderedPathOptions), {
43357
43553
  pathFrom: streamScroll ? projectPathToPrevFrame(paths.areaPaths[p], streamScroll) : (_c = (_b = reconcile == null ? void 0 : reconcile.area) == null ? void 0 : _b.from) != null ? _c : paths.pathFromArea,
43358
43554
  pathTo: paths.areaPaths[p],
43555
+ pathToNumeric: numericXY ? {
43556
+ xs: numericXY.xs,
43557
+ ys: numericXY.ys,
43558
+ closeY: numericXY.areaCloseY
43559
+ } : void 0,
43359
43560
  pathToInterp: (_d = reconcile == null ? void 0 : reconcile.area) == null ? void 0 : _d.toInterp,
43360
43561
  scrollMorph: !!streamScroll,
43361
43562
  stroke: "none",
@@ -43396,6 +43597,7 @@ class Line {
43396
43597
  const linePathCommonOpts = __spreadProps(__spreadValues({}, defaultRenderedPathOptions), {
43397
43598
  pathFrom: streamScroll ? projectPathToPrevFrame(paths.linePaths[p], streamScroll) : (_f = (_e = reconcile == null ? void 0 : reconcile.line) == null ? void 0 : _e.from) != null ? _f : paths.pathFromLine,
43398
43599
  pathTo: paths.linePaths[p],
43600
+ pathToNumeric: numericXY ? { xs: numericXY.xs, ys: numericXY.ys } : void 0,
43399
43601
  pathToInterp: (_g = reconcile == null ? void 0 : reconcile.line) == null ? void 0 : _g.toInterp,
43400
43602
  scrollMorph: !!streamScroll,
43401
43603
  stroke: lineFill,
@@ -43480,6 +43682,28 @@ class Line {
43480
43682
  let pathState = 0;
43481
43683
  let segmentStartX;
43482
43684
  const jitterPx = this.pointsChart ? this._scatterJitterPx(realIndex) : null;
43685
+ if (curve === "straight" && !this.pointsChart) {
43686
+ const fast = this._fastStraightPath({
43687
+ type,
43688
+ series,
43689
+ i,
43690
+ realIndex,
43691
+ translationsIndex,
43692
+ iterations,
43693
+ x,
43694
+ y,
43695
+ pX,
43696
+ pY,
43697
+ pathsFrom,
43698
+ linePaths,
43699
+ areaPaths,
43700
+ xArrj,
43701
+ yArrj,
43702
+ y2Arrj,
43703
+ stackSeries
43704
+ });
43705
+ if (fast) return fast;
43706
+ }
43483
43707
  for (let j = 0; j < iterations; j++) {
43484
43708
  if (series[i].length === 0) break;
43485
43709
  const isNull = typeof series[i][j + 1] === "undefined" || series[i][j + 1] === null;
@@ -43614,8 +43838,9 @@ class Line {
43614
43838
  seriesIndex: realIndex,
43615
43839
  j: j + 1
43616
43840
  });
43617
- if (elPointsWrap !== null) {
43841
+ if (elPointsWrap !== null && elPointsWrap !== this._elLastPointsWrap) {
43618
43842
  this.elPointsMain.add(elPointsWrap);
43843
+ this._elLastPointsWrap = elPointsWrap;
43619
43844
  }
43620
43845
  } else {
43621
43846
  this.scatter.draw(this.elSeries, j, {
@@ -43658,6 +43883,141 @@ class Line {
43658
43883
  y: (jt.y || 0) * yUnitPx
43659
43884
  };
43660
43885
  }
43886
+ /**
43887
+ * Numeric geometry fast path for plain straight line/area series (the
43888
+ * render-2026 perf work). Eligibility is strict: everything the per-point
43889
+ * slow loop can do beyond plain geometry (null gaps, markers, data labels,
43890
+ * discrete markers, stacking, combos, range areas) bails to the state
43891
+ * machine. When eligible it produces the SAME outputs as the slow loop
43892
+ * (byte-identical d strings via join, the same xArrj/yArrj/y2Arrj
43893
+ * pushes, and the same pointsArray tooltip cache) in one tight loop.
43894
+ * In canvas mode it additionally emits typed-array coordinates so the
43895
+ * renderer can paint via moveTo/lineTo without a Path2D d-string parse.
43896
+ *
43897
+ * @param {{type: any, series: any, i: number, realIndex: number,
43898
+ * translationsIndex: number, iterations: number, x: number, y: number,
43899
+ * pX: number, pY: number, pathsFrom: any, linePaths: any[],
43900
+ * areaPaths: any[], xArrj: any[], yArrj: any[], y2Arrj: any[],
43901
+ * stackSeries: boolean}} opts
43902
+ * @returns {any} the _iterateOverDataPoints result, or null when ineligible
43903
+ */
43904
+ _fastStraightPath({
43905
+ type,
43906
+ series,
43907
+ i,
43908
+ realIndex,
43909
+ translationsIndex,
43910
+ iterations,
43911
+ x,
43912
+ y,
43913
+ pX,
43914
+ pY,
43915
+ pathsFrom,
43916
+ linePaths,
43917
+ areaPaths,
43918
+ xArrj,
43919
+ yArrj,
43920
+ y2Arrj,
43921
+ stackSeries
43922
+ }) {
43923
+ const w = this.w;
43924
+ if (type !== "line" && type !== "area") return null;
43925
+ if (w.globals.comboCharts || stackSeries) return null;
43926
+ if (w.config.dataLabels.enabled) return null;
43927
+ if (w.config.markers.discrete.length) return null;
43928
+ if (w.globals.markers.size[realIndex] > 0) return null;
43929
+ const s = series[i];
43930
+ const n = s.length;
43931
+ if (!iterations || n < 2 || n - 1 !== iterations) return null;
43932
+ for (let k = 0; k <= iterations; k++) {
43933
+ const v = s[k];
43934
+ if (v === null || typeof v === "undefined") return null;
43935
+ }
43936
+ const isXNumeric = w.axisFlags.isXNumeric;
43937
+ const sx = isXNumeric ? w.seriesData.seriesX[realIndex] : null;
43938
+ if (isXNumeric && (!sx || sx.length < n)) return null;
43939
+ const yR = this.yRatio[translationsIndex];
43940
+ const isReversed = this.isReversed;
43941
+ const zeroY = this.zeroY;
43942
+ const bottomY = this.areaBottomY;
43943
+ const xRatio = this.xRatio;
43944
+ const minX = w.globals.minX;
43945
+ const xDivision = this.xDivision;
43946
+ const offX = w.config.markers.offsetX;
43947
+ const offY = w.config.markers.offsetY;
43948
+ const appendFrom = this.appendPathFrom && !w.globals.hasNullValues;
43949
+ let { pathFromLine, pathFromArea } = pathsFrom;
43950
+ const r = this.ctx && this.ctx.renderer;
43951
+ const canvasMode = !!(r && r.kind && r.kind !== "svg");
43952
+ const buildStrings = !canvasMode || w.globals.dataChanged;
43953
+ const nxs = canvasMode ? new Float64Array(n) : null;
43954
+ const nys = canvasMode ? new Float64Array(n) : null;
43955
+ if (nxs && nys) {
43956
+ nxs[0] = pX;
43957
+ nys[0] = pY;
43958
+ }
43959
+ if (typeof w.globals.pointsArray[realIndex] === "undefined") {
43960
+ w.globals.pointsArray[realIndex] = [];
43961
+ }
43962
+ const pts = w.globals.pointsArray[realIndex];
43963
+ const xPT1st = sx ? (sx[0] - minX) / xRatio + offX : this.categoryAxisCorrection + offX;
43964
+ pts.push([xPT1st, pathsFrom.prevY + offY]);
43965
+ const parts = buildStrings ? new Array(iterations + 1) : [];
43966
+ if (buildStrings) parts[0] = "M " + pX + " " + pY;
43967
+ const fromParts = buildStrings && appendFrom ? new Array(iterations) : null;
43968
+ let xv = x;
43969
+ let xj = pX;
43970
+ let yj = pY;
43971
+ for (let j = 0; j < iterations; j++) {
43972
+ if (sx) {
43973
+ xj = (sx[j + 1] - minX) / xRatio;
43974
+ } else {
43975
+ xv = xv + xDivision;
43976
+ xj = xv;
43977
+ }
43978
+ const v = s[j + 1];
43979
+ yj = zeroY - v / yR + (isReversed ? v / yR : 0) * 2;
43980
+ xArrj.push(xj);
43981
+ yArrj.push(yj);
43982
+ y2Arrj.push(y);
43983
+ pts.push([xj + offX, yj + offY]);
43984
+ if (buildStrings) {
43985
+ parts[j + 1] = " L " + xj + " " + yj;
43986
+ if (fromParts) fromParts[j] = " L " + xj + " " + bottomY;
43987
+ }
43988
+ if (nxs && nys) {
43989
+ nxs[j + 1] = xj;
43990
+ nys[j + 1] = yj;
43991
+ }
43992
+ }
43993
+ let linePath = "";
43994
+ let areaPath = "";
43995
+ if (buildStrings) {
43996
+ linePath = parts.join("");
43997
+ areaPath = linePath + " L " + xj + " " + bottomY + " L " + pX + " " + bottomY + "z";
43998
+ }
43999
+ if (fromParts) {
44000
+ const fromAppend = fromParts.join("");
44001
+ pathFromLine += fromAppend;
44002
+ pathFromArea += fromAppend;
44003
+ } else if (!buildStrings && appendFrom) {
44004
+ pathFromLine += " L " + xj + " " + bottomY;
44005
+ pathFromArea += " L " + xj + " " + bottomY;
44006
+ }
44007
+ linePaths.push(linePath);
44008
+ areaPaths.push(areaPath);
44009
+ return {
44010
+ yArrj,
44011
+ xArrj,
44012
+ pathFromArea,
44013
+ areaPaths,
44014
+ pathFromLine,
44015
+ linePaths,
44016
+ linePath,
44017
+ areaPath,
44018
+ numericXY: nxs ? { xs: nxs, ys: nys, areaCloseY: bottomY } : void 0
44019
+ };
44020
+ }
43661
44021
  /** @param {{type: any, series: any, i: any, j: any, x: any, y: any, xArrj: any, yArrj: any, y2: any, y2Arrj: any, pX: any, pY: any, pathState: any, segmentStartX: any, linePath: any, areaPath: any, linePaths: any, areaPaths: any, curve: any, isRangeStart: any}} opts */
43662
44022
  _createPaths({
43663
44023
  type,