@easyv/charts 1.9.18 → 1.9.20

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.
@@ -339,6 +339,11 @@ var _default = exports["default"] = /*#__PURE__*/(0, _react.memo)(/*#__PURE__*/(
339
339
  width = _useContext.width,
340
340
  height = _useContext.height,
341
341
  isIOS = _useContext.isIOS;
342
+ var cHeight = controlInfo.cHeight,
343
+ isC = controlInfo.isC,
344
+ cPercent = controlInfo.cPercent;
345
+ var x = orientation == "right" ? width : 0;
346
+ var y = orientation == "bottom" ? height - cHeight : 0;
342
347
  var LabelWidth = 1;
343
348
  if (label.labelNum == "Fixed") {
344
349
  LabelWidth = label.appearance.width;
@@ -347,53 +352,59 @@ var _default = exports["default"] = /*#__PURE__*/(0, _react.memo)(/*#__PURE__*/(
347
352
  LabelWidth = maxLabelFT(allTicks, label, formatter, label.font);
348
353
  }
349
354
  }
350
- var LabelNum = Math.floor(width * (1 - paddingOuter) / LabelWidth);
351
- var ticks = label.labelNum == "Fixed" ? tickss : getEvenlySpacedElements(allTicks, LabelNum < allTicks.length ? LabelNum > allTicks.length / 2 ? Math.ceil(allTicks.length) / 2 : LabelNum : allTicks.length, label.showLast);
355
+ var LabelNum = Math.floor(width / (isC ? cPercent : 1) * (1 - paddingOuter) / LabelWidth);
356
+ var ticks = label.labelNum == "Fixed" ? tickss : getEvenlySpacedElements(allTicks, LabelNum < allTicks.length ? LabelNum > allTicks.length / 2 ? Math.ceil(allTicks.length / 2) : LabelNum : allTicks.length, label.showLast);
352
357
  if (!(on && ticks.length > 0)) return null;
353
- var cHeight = controlInfo.cHeight,
354
- isC = controlInfo.isC,
355
- cPercent = controlInfo.cPercent;
356
- var x = orientation == "right" ? width : 0;
357
- var y = orientation == "bottom" ? height - cHeight : 0;
358
+
358
359
  //数据抽取逻辑
359
360
  function getEvenlySpacedElements(arr, expectCount) {
360
361
  var acc = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
361
362
  if (!arr.length || expectCount <= 0) return [];
362
363
  if (expectCount >= arr.length) return (0, _toConsumableArray2["default"])(arr);
363
-
364
- // 如果只需要取1个,直接返回第一个
365
- if (expectCount === 1) {
366
- return [arr[0]];
367
- }
364
+ if (expectCount === 1) return [arr[0]];
368
365
  if (acc) {
369
- //严格均分逻辑
370
- var result = [arr[0], arr[arr.length - 1]];
371
- if (expectCount === 2) {
372
- return result;
373
- }
374
- var needMiddleCount = expectCount - 2;
375
- var availableMiddleLength = arr.length - 2;
376
- if (needMiddleCount > 0 && availableMiddleLength >= needMiddleCount && (availableMiddleLength + 1) % (needMiddleCount + 1) === 0) {
377
- var step = (availableMiddleLength + 1) / (needMiddleCount + 1);
378
- result.length = 0;
379
- result.push(arr[0]);
380
- for (var i = 1; i <= needMiddleCount; i++) {
381
- var index = i * step;
382
- result.push(arr[index]);
366
+ var totalLength = arr.length;
367
+ var result = [];
368
+ var bestCount = 2;
369
+ for (var k = expectCount; k >= 2; k--) {
370
+ var denominator = k - 1;
371
+ var numerator = totalLength - 1;
372
+ if (denominator > 0 && numerator % denominator === 0) {
373
+ bestCount = k;
374
+ break;
383
375
  }
384
- result.push(arr[arr.length - 1]);
385
376
  }
377
+ var step = (totalLength - 1) / (bestCount - 1);
378
+ for (var i = 0; i < bestCount; i++) {
379
+ var rawIndex = i * step;
380
+ var index = Math.ceil(rawIndex);
381
+ var safeIndex = Math.max(0, Math.min(totalLength - 1, index));
382
+ result.push(arr[safeIndex]);
383
+ }
384
+ if (result.length > 0) result[0] = arr[0];
385
+ if (result.length >= 2) result[result.length - 1] = arr[totalLength - 1];
386
386
  return result;
387
387
  } else {
388
+ // 重构acc=false逻辑:优先均匀分布,不强制首尾
388
389
  var _result = [];
389
390
  var arrLen = arr.length;
390
- var _step = Math.max(1, Math.floor(arrLen / expectCount));
391
- for (var _i = 0; _i < arrLen; _i += _step) {
392
- _result.push(arr[_i]);
393
- if (_result.length >= expectCount) break;
391
+ if (expectCount === 2) {
392
+ _result.push(arr[0]);
393
+ _result.push(arr[arrLen - 1]);
394
+ return _result;
394
395
  }
395
- if (expectCount === 2 && _result.length >= 2) {
396
- _result[1] = arr[arr.length - 1];
396
+ var idealStep = (arrLen - 1) / (expectCount - 1);
397
+ var isIdealStepInteger = Math.abs(idealStep - Math.round(idealStep)) < 1e-10;
398
+ if (isIdealStepInteger) {
399
+ for (var _i = 0; _i < expectCount; _i++) {
400
+ var _index = _i * idealStep;
401
+ _result.push(arr[_index]);
402
+ }
403
+ } else {
404
+ var _step = Math.max(1, Math.floor(arrLen / expectCount));
405
+ for (var _i2 = 0; _i2 < arrLen && _result.length < expectCount; _i2 += _step) {
406
+ _result.push(arr[_i2]);
407
+ }
397
408
  }
398
409
  return _result;
399
410
  }
@@ -474,7 +485,8 @@ var _default = exports["default"] = /*#__PURE__*/(0, _react.memo)(/*#__PURE__*/(
474
485
  return draw(ticks, scaler[index]);
475
486
  }));
476
487
  } else if (isC && orientation == "bottom") {
477
- return /*#__PURE__*/_react["default"].createElement(_react["default"].Fragment, null, draw(rawTicks, scaler));
488
+ return /*#__PURE__*/_react["default"].createElement(_react["default"].Fragment, null, draw(ticks, scaler));
489
+ // return <>{draw(rawTicks, scaler)}</>;
478
490
  } else {
479
491
  return /*#__PURE__*/_react["default"].createElement(_react["default"].Fragment, null, draw(ticks, scaler));
480
492
  }
@@ -352,7 +352,7 @@ var Chart = /*#__PURE__*/(0, _react.memo)(function (_ref) {
352
352
  percent = isNaN(percent) ? 1 : percent;
353
353
  var translateX = -(controlEnd + start / cPercent - chartWidth) * percent;
354
354
  curControlPercent.current = percent;
355
- seriesEl.current.style.transform = "translate(".concat(translateX, "px,").concat(marginTop, "px)");
355
+ // seriesEl.current.style.transform = `translate(${translateX}px,${marginTop}px)`;
356
356
  axisElList.current[2].style.transform = "translate(".concat(translateX, "px,", 0, "px)");
357
357
  }
358
358
  }, [controlInfo]);
@@ -589,9 +589,20 @@ var Chart = /*#__PURE__*/(0, _react.memo)(function (_ref) {
589
589
  ref: seriesEl,
590
590
  style: {
591
591
  overflow: "visible",
592
+ // 保留原有样式,clipPath会接管裁剪
592
593
  position: "absolute",
593
594
  transform: isVertical ? "translate(".concat(marginRight, "px,").concat(isIOS ? marginTop : 0, "px)") : "translate(".concat(isIOS ? marginLeft : 0, "px,").concat(marginTop, "px)")
594
595
  }
596
+ }, /*#__PURE__*/_react["default"].createElement("defs", null, /*#__PURE__*/_react["default"].createElement("clipPath", {
597
+ id: "chart-clip-".concat(id)
598
+ }, /*#__PURE__*/_react["default"].createElement("rect", {
599
+ x: "0",
600
+ y: "0",
601
+ width: xLineRange,
602
+ height: yLineRange,
603
+ fill: "transparent"
604
+ }))), /*#__PURE__*/_react["default"].createElement("g", {
605
+ clipPath: "url(#chart-clip-".concat(id, ")")
595
606
  }, /*#__PURE__*/_react["default"].createElement("g", null, control && zIndex == "bottom" && ctlIndicatorList.map(function (item, index) {
596
607
  var x = axisX.scaler(item.tick);
597
608
  return /*#__PURE__*/_react["default"].createElement(_.Indicator, (0, _extends2["default"])({
@@ -662,7 +673,7 @@ var Chart = /*#__PURE__*/(0, _react.memo)(function (_ref) {
662
673
  ctlIndicatorList: ctlIndicatorList,
663
674
  manual: manual
664
675
  }));
665
- }))), showCtl && !!control && /*#__PURE__*/_react["default"].createElement(_.Tooltip, (0, _extends2["default"])({
676
+ })))), showCtl && !!control && /*#__PURE__*/_react["default"].createElement(_.Tooltip, (0, _extends2["default"])({
666
677
  isVertical: isVertical
667
678
  }, tooltip, {
668
679
  data: controlChartTooltipData,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@easyv/charts",
3
- "version": "1.9.18",
3
+ "version": "1.9.20",
4
4
  "description": "",
5
5
  "main": "lib/index.js",
6
6
  "scripts": {
@@ -392,6 +392,9 @@ export default memo(
392
392
  ref
393
393
  ) => {
394
394
  const { width, height, isIOS } = useContext(chartContext);
395
+ const { cHeight, isC, cPercent } = controlInfo;
396
+ const x = orientation == "right" ? width : 0;
397
+ const y = orientation == "bottom" ? height - cHeight : 0;
395
398
  let LabelWidth = 1;
396
399
  if (label.labelNum == "Fixed") {
397
400
  LabelWidth = label.appearance.width;
@@ -400,7 +403,9 @@ export default memo(
400
403
  LabelWidth = maxLabelFT(allTicks, label, formatter, label.font);
401
404
  }
402
405
  }
403
- const LabelNum = Math.floor((width * (1 - paddingOuter)) / LabelWidth);
406
+ const LabelNum = Math.floor(
407
+ ((width / (isC ? cPercent : 1)) * (1 - paddingOuter)) / LabelWidth
408
+ );
404
409
  const ticks: any =
405
410
  label.labelNum == "Fixed"
406
411
  ? tickss
@@ -408,16 +413,13 @@ export default memo(
408
413
  allTicks,
409
414
  LabelNum < allTicks.length
410
415
  ? LabelNum > allTicks.length / 2
411
- ? Math.ceil(allTicks.length) / 2
416
+ ? Math.ceil(allTicks.length / 2)
412
417
  : LabelNum
413
418
  : allTicks.length,
414
419
  label.showLast
415
420
  );
416
421
  if (!(on && ticks.length > 0)) return null;
417
422
 
418
- const { cHeight, isC, cPercent } = controlInfo;
419
- const x = orientation == "right" ? width : 0;
420
- const y = orientation == "bottom" ? height - cHeight : 0;
421
423
  //数据抽取逻辑
422
424
  function getEvenlySpacedElements(
423
425
  arr: any[],
@@ -426,51 +428,63 @@ export default memo(
426
428
  ): any[] {
427
429
  if (!arr.length || expectCount <= 0) return [];
428
430
  if (expectCount >= arr.length) return [...arr];
429
-
430
- // 如果只需要取1个,直接返回第一个
431
- if (expectCount === 1) {
432
- return [arr[0]];
433
- }
431
+ if (expectCount === 1) return [arr[0]];
434
432
 
435
433
  if (acc) {
436
- //严格均分逻辑
437
- const result: any[] = [arr[0], arr[arr.length - 1]];
438
- if (expectCount === 2) {
439
- return result;
440
- }
441
- const needMiddleCount = expectCount - 2;
442
- const availableMiddleLength = arr.length - 2;
434
+ const totalLength = arr.length;
435
+ const result: any[] = [];
443
436
 
444
- if (
445
- needMiddleCount > 0 &&
446
- availableMiddleLength >= needMiddleCount &&
447
- (availableMiddleLength + 1) % (needMiddleCount + 1) === 0
448
- ) {
449
- const step = (availableMiddleLength + 1) / (needMiddleCount + 1);
450
- result.length = 0;
451
- result.push(arr[0]);
452
- for (let i = 1; i <= needMiddleCount; i++) {
453
- const index = i * step;
454
- result.push(arr[index]);
437
+ let bestCount = 2;
438
+ for (let k = expectCount; k >= 2; k--) {
439
+ const denominator = k - 1;
440
+ const numerator = totalLength - 1;
441
+ if (denominator > 0 && numerator % denominator === 0) {
442
+ bestCount = k;
443
+ break;
455
444
  }
445
+ }
456
446
 
457
- result.push(arr[arr.length - 1]);
447
+ const step = (totalLength - 1) / (bestCount - 1);
448
+ for (let i = 0; i < bestCount; i++) {
449
+ const rawIndex = i * step;
450
+ const index = Math.ceil(rawIndex);
451
+ const safeIndex = Math.max(0, Math.min(totalLength - 1, index));
452
+ result.push(arr[safeIndex]);
458
453
  }
459
454
 
455
+ if (result.length > 0) result[0] = arr[0];
456
+ if (result.length >= 2)
457
+ result[result.length - 1] = arr[totalLength - 1];
458
+
460
459
  return result;
461
460
  } else {
461
+ // 重构acc=false逻辑:优先均匀分布,不强制首尾
462
462
  const result: any[] = [];
463
463
  const arrLen = arr.length;
464
- const step = Math.max(1, Math.floor(arrLen / expectCount));
465
-
466
- for (let i = 0; i < arrLen; i += step) {
467
- result.push(arr[i]);
468
- if (result.length >= expectCount) break;
469
- }
470
- if (expectCount === 2 && result.length >= 2) {
471
- result[1] = arr[arr.length - 1];
464
+ if (expectCount === 2) {
465
+ result.push(arr[0]);
466
+ result.push(arr[arrLen - 1]);
467
+ return result;
472
468
  }
469
+ const idealStep = (arrLen - 1) / (expectCount - 1);
470
+ const isIdealStepInteger =
471
+ Math.abs(idealStep - Math.round(idealStep)) < 1e-10;
473
472
 
473
+ if (isIdealStepInteger) {
474
+ for (let i = 0; i < expectCount; i++) {
475
+ const index = i * idealStep;
476
+ result.push(arr[index]);
477
+ }
478
+ } else {
479
+ const step = Math.max(1, Math.floor(arrLen / expectCount));
480
+ for (
481
+ let i = 0;
482
+ i < arrLen && result.length < expectCount;
483
+ i += step
484
+ ) {
485
+ result.push(arr[i]);
486
+ }
487
+ }
474
488
  return result;
475
489
  }
476
490
  }
@@ -588,7 +602,8 @@ export default memo(
588
602
  </>
589
603
  );
590
604
  } else if (isC && orientation == "bottom") {
591
- return <>{draw(rawTicks, scaler)}</>;
605
+ return <>{draw(ticks, scaler)}</>;
606
+ // return <>{draw(rawTicks, scaler)}</>;
592
607
  } else {
593
608
  return <>{draw(ticks, scaler)}</>;
594
609
  }
@@ -344,7 +344,7 @@ const Chart = memo(
344
344
  const translateX =
345
345
  -(controlEnd + start / cPercent - chartWidth) * percent;
346
346
  curControlPercent.current = percent;
347
- seriesEl.current.style.transform = `translate(${translateX}px,${marginTop}px)`;
347
+ // seriesEl.current.style.transform = `translate(${translateX}px,${marginTop}px)`;
348
348
  axisElList.current[2].style.transform = `translate(${translateX}px,${0}px)`;
349
349
  }
350
350
  }, [controlInfo]);
@@ -583,112 +583,129 @@ const Chart = memo(
583
583
  : `translateY(${-marginTop}px)`,
584
584
  }}
585
585
  >
586
+ {/* 关键修改1:添加clipPath定义,限定裁剪区域 */}
586
587
  <svg
587
588
  width="100%"
588
589
  height="100%"
589
590
  ref={seriesEl}
590
591
  style={{
591
- overflow: "visible",
592
+ overflow: "visible", // 保留原有样式,clipPath会接管裁剪
592
593
  position: "absolute",
593
594
  transform: isVertical
594
595
  ? `translate(${marginRight}px,${isIOS ? marginTop : 0}px)`
595
596
  : `translate(${isIOS ? marginLeft : 0}px,${marginTop}px)`,
596
597
  }}
597
598
  >
598
- {/* 控制图指示器部分 */}
599
- <g>
600
- {control &&
601
- zIndex == "bottom" &&
602
- ctlIndicatorList.map((item, index) => {
603
- const x = axisX.scaler(item.tick);
604
- return (
605
- <Indicator
599
+ {/* 定义裁剪路径:只显示指定宽高的区域 */}
600
+ <defs>
601
+ <clipPath id={`chart-clip-${id}`}>
602
+ <rect
603
+ x="0"
604
+ y="0"
605
+ width={xLineRange}
606
+ height={yLineRange}
607
+ fill="transparent"
608
+ />
609
+ </clipPath>
610
+ </defs>
611
+
612
+ {/* 关键修改2:将所有图表内容包裹在g标签中,并应用clipPath */}
613
+ <g clipPath={`url(#chart-clip-${id})`}>
614
+ {/* 控制图指示器部分 */}
615
+ <g>
616
+ {control &&
617
+ zIndex == "bottom" &&
618
+ ctlIndicatorList.map((item, index) => {
619
+ const x = axisX.scaler(item.tick);
620
+ return (
621
+ <Indicator
622
+ key={index}
623
+ {...indicator}
624
+ {...{
625
+ height: yLineRange,
626
+ width: indicatorWidth,
627
+ x: x - indicatorWidth / 2,
628
+ }}
629
+ isControlChart={!!control}
630
+ xName={item.tick}
631
+ setCtlTip={setCtlTip}
632
+ ctlIndicatorList={ctlIndicatorList}
633
+ manual={true}
634
+ />
635
+ );
636
+ })}
637
+ </g>
638
+ {/**绘制图表主体 */}
639
+ {series.map(({ Component, yOrZ, ...config }, index) => {
640
+ const yAxis = axes.get(yOrZ);
641
+ const isXRepeat = hasDuplicateX(series); //x项有重复项判断
642
+ return (
643
+ yAxis &&
644
+ Component && (
645
+ <Component
606
646
  key={index}
607
- {...indicator}
608
- {...{
609
- height: yLineRange,
610
- width: indicatorWidth,
611
- x: x - indicatorWidth / 2,
612
- }}
647
+ {...config}
648
+ bandLength={bandLength}
649
+ curXLabel={curXLabel}
650
+ selectStyle={selectStyle}
651
+ xAxis={axisX}
652
+ yAxis={yAxis}
653
+ auto={auto}
654
+ // 控制图部分,主要是为了,当鼠标悬浮在指示器上时,显示对应的tooltip
613
655
  isControlChart={!!control}
614
- xName={item.tick}
656
+ indicatorWidth={indicatorWidth}
657
+ triggerEvents={onInteraction}
615
658
  setCtlTip={setCtlTip}
616
- ctlIndicatorList={ctlIndicatorList}
617
- manual={true}
659
+ isXRepeat={isXRepeat}
660
+ maxWidth={bar ? bar.maxWidth : 0}
618
661
  />
619
- );
620
- })}
621
- </g>
622
- {/**绘制图表主体 */}
623
- {series.map(({ Component, yOrZ, ...config }, index) => {
624
- const yAxis = axes.get(yOrZ);
625
- const isXRepeat = hasDuplicateX(series); //x项有重复项判断
626
- return (
627
- yAxis &&
628
- Component && (
629
- <Component
630
- key={index}
631
- {...config}
632
- bandLength={bandLength}
633
- curXLabel={curXLabel}
634
- selectStyle={selectStyle}
635
- xAxis={axisX}
636
- yAxis={yAxis}
637
- auto={auto}
638
- // 控制图部分,主要是为了,当鼠标悬浮在指示器上时,显示对应的tooltip
639
- isControlChart={!!control}
640
- indicatorWidth={indicatorWidth}
641
- triggerEvents={onInteraction}
642
- setCtlTip={setCtlTip}
643
- isXRepeat={isXRepeat}
644
- maxWidth={bar ? bar.maxWidth : 0}
645
- />
646
- )
647
- );
648
- })}
649
- {/* 图表数值标签 */}
650
- {series.map(({ Component, yOrZ, ...config }, index) => {
651
- const yAxis = axes.get(yOrZ);
652
- const isXRepeat = hasDuplicateX(series); //x项有重复项判断
653
- return (
654
- yAxis && (
655
- <Label
656
- isXRepeat={isXRepeat}
657
- key={index}
658
- {...config}
659
- curXLabel={curXLabel}
660
- selectStyle={selectStyle}
661
- bandLength={bandLength}
662
- xAxis={axisX}
663
- yAxis={yAxis}
664
- triggerEvents={onInteraction}
665
- isControlChart={!!control}
666
- />
667
- )
668
- );
669
- })}
670
- <g>
671
- {control &&
672
- zIndex != "bottom" &&
673
- ctlIndicatorList.map((item, index) => {
674
- const x = axisX.scaler(item.tick);
675
- return (
676
- <Indicator
662
+ )
663
+ );
664
+ })}
665
+ {/* 图表数值标签 */}
666
+ {series.map(({ Component, yOrZ, ...config }, index) => {
667
+ const yAxis = axes.get(yOrZ);
668
+ const isXRepeat = hasDuplicateX(series); //x项有重复项判断
669
+ return (
670
+ yAxis && (
671
+ <Label
672
+ isXRepeat={isXRepeat}
677
673
  key={index}
678
- {...indicator}
679
- {...{
680
- height: yLineRange,
681
- width: indicatorWidth,
682
- x: x - indicatorWidth / 2,
683
- }}
674
+ {...config}
675
+ curXLabel={curXLabel}
676
+ selectStyle={selectStyle}
677
+ bandLength={bandLength}
678
+ xAxis={axisX}
679
+ yAxis={yAxis}
680
+ triggerEvents={onInteraction}
684
681
  isControlChart={!!control}
685
- xName={item.tick}
686
- setCtlTip={setCtlTip}
687
- ctlIndicatorList={ctlIndicatorList}
688
- manual={manual}
689
682
  />
690
- );
691
- })}
683
+ )
684
+ );
685
+ })}
686
+ <g>
687
+ {control &&
688
+ zIndex != "bottom" &&
689
+ ctlIndicatorList.map((item, index) => {
690
+ const x = axisX.scaler(item.tick);
691
+ return (
692
+ <Indicator
693
+ key={index}
694
+ {...indicator}
695
+ {...{
696
+ height: yLineRange,
697
+ width: indicatorWidth,
698
+ x: x - indicatorWidth / 2,
699
+ }}
700
+ isControlChart={!!control}
701
+ xName={item.tick}
702
+ setCtlTip={setCtlTip}
703
+ ctlIndicatorList={ctlIndicatorList}
704
+ manual={manual}
705
+ />
706
+ );
707
+ })}
708
+ </g>
692
709
  </g>
693
710
  </svg>
694
711
  {/* 控制图下的提示框 */}