@galacean/effects-plugin-rich-text 2.8.7 → 2.8.9

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.
package/dist/index.mjs CHANGED
@@ -3,7 +3,7 @@
3
3
  * Description: Galacean Effects player rich text plugin
4
4
  * Author: Ant Group CO., Ltd.
5
5
  * Contributors: 云垣
6
- * Version: v2.8.7
6
+ * Version: v2.8.9
7
7
  */
8
8
 
9
9
  import * as EFFECTS from '@galacean/effects';
@@ -403,7 +403,7 @@ var RichTextLayout = /*#__PURE__*/ function() {
403
403
  // TODO: width 和 height 新版富文本计算没有地方用到
404
404
  this.width = 0;
405
405
  this.height = 0;
406
- var size = options.size, _options_textOverflow = options.textOverflow, textOverflow = _options_textOverflow === void 0 ? spec.TextOverflow.clip : _options_textOverflow, _options_textVerticalAlign = options.textVerticalAlign, textVerticalAlign = _options_textVerticalAlign === void 0 ? spec.TextVerticalAlign.middle : _options_textVerticalAlign, _options_textAlign = options.textAlign, textAlign = _options_textAlign === void 0 ? spec.TextAlignment.left : _options_textAlign, _options_letterSpace = options.letterSpace, letterSpace = _options_letterSpace === void 0 ? 0 : _options_letterSpace, _options_lineHeight = options.lineHeight, lineHeight = _options_lineHeight === void 0 ? 0.571 : _options_lineHeight, _options_wrapEnabled = options.wrapEnabled, wrapEnabled = _options_wrapEnabled === void 0 ? false : _options_wrapEnabled, _options_maxTextWidth = options.maxTextWidth, maxTextWidth = _options_maxTextWidth === void 0 ? 350 : _options_maxTextWidth, _options_maxTextHeight = options.maxTextHeight, maxTextHeight = _options_maxTextHeight === void 0 ? 1000 : _options_maxTextHeight, _options_sizeMode = options.sizeMode, sizeMode = _options_sizeMode === void 0 ? spec.TextSizeMode.autoWidth : _options_sizeMode, _options_useLegacyRichText = options.// @ts-expect-error 兼容旧版
406
+ var size = options.size, _options_textOverflow = options.textOverflow, textOverflow = _options_textOverflow === void 0 ? spec.TextOverflow.clip : _options_textOverflow, _options_textVerticalAlign = options.textVerticalAlign, textVerticalAlign = _options_textVerticalAlign === void 0 ? spec.TextVerticalAlign.middle : _options_textVerticalAlign, _options_textAlign = options.textAlign, textAlign = _options_textAlign === void 0 ? spec.TextAlignment.left : _options_textAlign, _options_letterSpace = options.letterSpace, letterSpace = _options_letterSpace === void 0 ? 0 : _options_letterSpace, _options_lineHeight = options.lineHeight, lineHeight = _options_lineHeight === void 0 ? 0.571 : _options_lineHeight, _options_wrapEnabled = options.wrapEnabled, wrapEnabled = _options_wrapEnabled === void 0 ? false : _options_wrapEnabled, _options_maxTextWidth = options.maxTextWidth, maxTextWidth = _options_maxTextWidth === void 0 ? 350 : _options_maxTextWidth, _options_maxTextHeight = options.maxTextHeight, maxTextHeight = _options_maxTextHeight === void 0 ? 1000 : _options_maxTextHeight, _options_autoResize = options.autoResize, autoResize = _options_autoResize === void 0 ? spec.TextSizeMode.autoWidth : _options_autoResize, _options_useLegacyRichText = options.// @ts-expect-error 兼容旧版
407
407
  useLegacyRichText, useLegacyRichText = _options_useLegacyRichText === void 0 ? false : _options_useLegacyRichText;
408
408
  this.letterSpace = letterSpace;
409
409
  this.lineHeight = lineHeight;
@@ -416,7 +416,7 @@ var RichTextLayout = /*#__PURE__*/ function() {
416
416
  this.wrapEnabled = wrapEnabled;
417
417
  this.maxTextWidth = maxTextWidth;
418
418
  this.maxTextHeight = maxTextHeight;
419
- this.sizeMode = sizeMode;
419
+ this.autoResize = autoResize;
420
420
  }
421
421
  var _proto = RichTextLayout.prototype;
422
422
  _proto.getOffsetY = function getOffsetY(style, lineCount, lineHeight, fontSize, totalLineHeight) {
@@ -497,7 +497,7 @@ var RichTextLayout = /*#__PURE__*/ function() {
497
497
  */ _proto.getOffsetXRich = function getOffsetXRich(style, maxWidth, contentW) {
498
498
  switch(this.textAlign){
499
499
  case spec.TextAlignment.left:
500
- return style.outlineWidth * style.fontScale;
500
+ return style.outlineWidth;
501
501
  case spec.TextAlignment.middle:
502
502
  return (maxWidth - contentW) / 2;
503
503
  case spec.TextAlignment.right:
@@ -509,6 +509,7 @@ var RichTextLayout = /*#__PURE__*/ function() {
509
509
  /**
510
510
  * 设置文本框尺寸
511
511
  * - 不包含 fontScale 缩放
512
+ * @deprecated use maxTextWidth / maxTextHeight instead
512
513
  */ _proto.setSize = function setSize(width, height) {
513
514
  this.width = width;
514
515
  this.height = height;
@@ -595,11 +596,12 @@ var RichTextLayout = /*#__PURE__*/ function() {
595
596
  */ var RichWrapDisabledStrategy = /*#__PURE__*/ function() {
596
597
  function RichWrapDisabledStrategy() {}
597
598
  var _proto = RichWrapDisabledStrategy.prototype;
598
- _proto.computeLines = function computeLines(processedOptions, context, style, layout, singleLineHeight, fontScale, letterSpace, scaleFactor) {
599
+ _proto.computeLines = function computeLines(processedOptions, context, style, layout, letterSpace) {
599
600
  var _this = this;
600
601
  var lines = [];
601
602
  var baselines = [];
602
- var gapPx = (layout.lineHeight || 0) * fontScale;
603
+ var gapPx = layout.lineHeight || 0;
604
+ var scaleFactor = 1 / 10; // 1/10px, 后面 context.font 设置的字号为10px
603
605
  var currentLine = this.createNewLine();
604
606
  var maxLineWidth = 0;
605
607
  var totalHeight = 0;
@@ -607,8 +609,8 @@ var RichTextLayout = /*#__PURE__*/ function() {
607
609
  if (currentLine.chars.length === 0) {
608
610
  return;
609
611
  }
610
- // 第1行用真实首行高度,其余行用 gapPx
611
- currentLine.lineHeight = lines.length === 0 ? (currentLine.lineAscent || 0) + (currentLine.lineDescent || 0) : gapPx;
612
+ // 所有行都使用配置的行高(gapPx
613
+ currentLine.lineHeight = gapPx;
612
614
  // 记录本行基线
613
615
  var baseline = lines.length === 0 ? 0 : baselines[baselines.length - 1] + gapPx;
614
616
  baselines.push(baseline);
@@ -628,22 +630,20 @@ var RichTextLayout = /*#__PURE__*/ function() {
628
630
  context.font = fontStyle + " " + (options.fontWeight || style.textWeight) + " 10px " + (options.fontFamily || style.fontFamily);
629
631
  // 记录段起始 x
630
632
  currentLine.offsetX.push(currentLine.width);
631
- // 逐字宽度和高度测量(asc/desc 测量)
633
+ // 预计算缩放因子:measureText 基于 10px,乘 fontSize/10 得到逻辑像素
634
+ var glyphScale = fontSize * scaleFactor;
635
+ // 使用字体级别度量(不随具体字符变化),保证基线位置稳定
636
+ var refMetrics = context.measureText("x");
637
+ var fontAsc = refMetrics.fontBoundingBoxAscent * glyphScale;
638
+ var fontDesc = refMetrics.fontBoundingBoxDescent * glyphScale;
639
+ // 逐字宽度测量
632
640
  var segmentInnerX = 0;
633
641
  var charArr = [];
634
- var lineAscent = 0;
635
- var lineDescent = 0;
636
642
  for(var i = 0; i < text.length; i++){
637
643
  var ch = text[i];
638
644
  var m = context.measureText(ch);
639
645
  var w = m.width;
640
- var charWidth = (w <= 0 ? 0 : w) * fontSize * scaleFactor * fontScale;
641
- // 测量 asc/desc 并按目标字号缩放
642
- var scale = fontSize * fontScale * scaleFactor;
643
- var asc = m.actualBoundingBoxAscent * scale;
644
- var desc = m.actualBoundingBoxDescent * scale;
645
- lineAscent = Math.max(lineAscent, asc);
646
- lineDescent = Math.max(lineDescent, desc);
646
+ var charWidth = (w <= 0 ? 0 : w) * glyphScale;
647
647
  if (i > 0) {
648
648
  segmentInnerX += letterSpace; // 先加"前一个字符与当前字符之间"的间距
649
649
  }
@@ -656,13 +656,13 @@ var RichTextLayout = /*#__PURE__*/ function() {
656
656
  currentLine.chars.push(charArr);
657
657
  currentLine.width += segmentInnerX;
658
658
  currentLine.richOptions.push(options);
659
- // 累计行级 asc/desc
660
- currentLine.lineAscent = Math.max(currentLine.lineAscent || 0, lineAscent);
661
- currentLine.lineDescent = Math.max(currentLine.lineDescent || 0, lineDescent);
659
+ // 累计行级 asc/desc(字体级别,不因字符形状改变)
660
+ currentLine.lineAscent = Math.max(currentLine.lineAscent || 0, fontAsc);
661
+ currentLine.lineDescent = Math.max(currentLine.lineDescent || 0, fontDesc);
662
662
  });
663
663
  // 结束最后一行
664
664
  finishCurrentLine();
665
- // 计算 bbox
665
+ // 计算 bbox(包含行高带来的上下边距)
666
666
  var bboxTop = Infinity;
667
667
  var bboxBottom = -Infinity;
668
668
  if (lines.length === 0) {
@@ -676,8 +676,12 @@ var RichTextLayout = /*#__PURE__*/ function() {
676
676
  };
677
677
  }
678
678
  for(var i = 0; i < lines.length; i++){
679
- bboxTop = Math.min(bboxTop, baselines[i] - (lines[i].lineAscent || 0));
680
- bboxBottom = Math.max(bboxBottom, baselines[i] + (lines[i].lineDescent || 0));
679
+ var asc = lines[i].lineAscent || 0;
680
+ var desc = lines[i].lineDescent || 0;
681
+ var textHeight = asc + desc;
682
+ var margin = (gapPx - textHeight) / 2;
683
+ bboxTop = Math.min(bboxTop, baselines[i] - asc - margin);
684
+ bboxBottom = Math.max(bboxBottom, baselines[i] + desc + margin);
681
685
  }
682
686
  var bboxHeight = bboxBottom - bboxTop;
683
687
  return {
@@ -712,11 +716,14 @@ var RichTextLayout = /*#__PURE__*/ function() {
712
716
  */ var RichWrapEnabledStrategy = /*#__PURE__*/ function() {
713
717
  function RichWrapEnabledStrategy() {}
714
718
  var _proto = RichWrapEnabledStrategy.prototype;
715
- _proto.computeLines = function computeLines(processedOptions, context, style, layout, singleLineHeight, fontScale, letterSpace, scaleFactor) {
719
+ _proto.computeLines = function computeLines(processedOptions, context, style, layout, letterSpace) {
716
720
  var _this = this;
717
721
  var lines = [];
718
722
  var baselines = [];
719
- var gapPx = (layout.lineHeight || 0) * fontScale;
723
+ var gapPx = layout.lineHeight || 0;
724
+ var scaleFactor = 1 / 10; // 1/10px, 后面 context.font 设置的字号为10px
725
+ // 换行阈值,预计算避免每字符重复读取
726
+ var wrapWidth = layout.maxTextWidth || Infinity;
720
727
  var currentLine = this.createNewLine();
721
728
  var maxLineWidth = 0;
722
729
  var totalHeight = 0;
@@ -731,8 +738,8 @@ var RichTextLayout = /*#__PURE__*/ function() {
731
738
  if (currentLine.chars.length === 0) {
732
739
  return;
733
740
  }
734
- // 第1行用真实首行高度,其余行用 gapPx
735
- currentLine.lineHeight = lines.length === 0 ? (currentLine.lineAscent || 0) + (currentLine.lineDescent || 0) : gapPx;
741
+ // 所有行都使用配置的行高(gapPx
742
+ currentLine.lineHeight = gapPx;
736
743
  // 记录本行基线
737
744
  var baseline = lines.length === 0 ? 0 : baselines[baselines.length - 1] + gapPx;
738
745
  baselines.push(baseline);
@@ -774,22 +781,24 @@ var RichTextLayout = /*#__PURE__*/ function() {
774
781
  flushChunk();
775
782
  finishCurrentLine();
776
783
  }
784
+ // 预计算缩放因子:measureText 基于 10px,乘 fontSize/10 得到逻辑像素
785
+ var glyphScale = fontSize * scaleFactor;
786
+ // 使用字体级别度量(不随具体字符变化),保证基线位置稳定
787
+ var refMetrics = context.measureText("x");
788
+ var fontAsc = refMetrics.fontBoundingBoxAscent * glyphScale;
789
+ var fontDesc = refMetrics.fontBoundingBoxDescent * glyphScale;
777
790
  // 逐字符处理
778
791
  for(var i = 0; i < text.length; i++){
779
792
  var ch = text[i];
780
793
  // 获取基础宽度并计算实际宽度(动态缩放)
781
794
  var m = context.measureText(ch);
782
795
  var baseW = m.width;
783
- var charWidth = (baseW <= 0 ? 0 : baseW) * fontSize * scaleFactor * fontScale;
784
- // 测量 asc/desc 并按目标字号缩放
785
- var scale = fontSize * fontScale * scaleFactor;
786
- var asc = m.actualBoundingBoxAscent * scale;
787
- var desc = m.actualBoundingBoxDescent * scale;
796
+ var charWidth = (baseW <= 0 ? 0 : baseW) * glyphScale;
788
797
  // 计算预期宽度(包含字符间距)
789
798
  var spacing = chunkChars.length > 0 ? letterSpace : 0;
790
799
  var willWidth = currentLine.width + spacing + charWidth;
791
800
  // 自动换行判断
792
- if (willWidth > (layout.maxTextWidth * fontScale || Infinity)) {
801
+ if (willWidth > wrapWidth) {
793
802
  flushChunk();
794
803
  finishCurrentLine();
795
804
  }
@@ -808,9 +817,9 @@ var RichTextLayout = /*#__PURE__*/ function() {
808
817
  char: ch,
809
818
  x: segmentInnerX
810
819
  });
811
- // 累计行级 asc/desc
812
- currentLine.lineAscent = Math.max(currentLine.lineAscent || 0, asc);
813
- currentLine.lineDescent = Math.max(currentLine.lineDescent || 0, desc);
820
+ // 累计行级 asc/desc(字体级别,不因字符形状改变)
821
+ currentLine.lineAscent = Math.max(currentLine.lineAscent || 0, fontAsc);
822
+ currentLine.lineDescent = Math.max(currentLine.lineDescent || 0, fontDesc);
814
823
  // 更新位置和宽度
815
824
  segmentInnerX += charWidth;
816
825
  currentLine.width += charWidth;
@@ -820,12 +829,26 @@ var RichTextLayout = /*#__PURE__*/ function() {
820
829
  });
821
830
  // 结束最后一行
822
831
  finishCurrentLine();
823
- // 计算 bbox
832
+ // 计算 bbox(包含行高带来的上下边距)
824
833
  var bboxTop = Infinity;
825
834
  var bboxBottom = -Infinity;
835
+ if (lines.length === 0) {
836
+ return {
837
+ lines: lines,
838
+ maxLineWidth: maxLineWidth,
839
+ totalHeight: totalHeight,
840
+ bboxTop: 0,
841
+ bboxBottom: 0,
842
+ bboxHeight: 0
843
+ };
844
+ }
826
845
  for(var i = 0; i < lines.length; i++){
827
- bboxTop = Math.min(bboxTop, baselines[i] - (lines[i].lineAscent || 0));
828
- bboxBottom = Math.max(bboxBottom, baselines[i] + (lines[i].lineDescent || 0));
846
+ var asc = lines[i].lineAscent || 0;
847
+ var desc = lines[i].lineDescent || 0;
848
+ var textHeight = asc + desc;
849
+ var margin = (gapPx - textHeight) / 2;
850
+ bboxTop = Math.min(bboxTop, baselines[i] - asc - margin);
851
+ bboxBottom = Math.max(bboxBottom, baselines[i] + desc + margin);
829
852
  }
830
853
  var bboxHeight = bboxBottom - bboxTop;
831
854
  return {
@@ -854,117 +877,117 @@ var RichTextLayout = /*#__PURE__*/ function() {
854
877
  return RichWrapEnabledStrategy;
855
878
  }();
856
879
 
857
- /**
858
- * 富文本 Display 溢出策略
859
- * 在溢出策略中对所有几何量进行一次性缩放
860
- */ var RichDisplayOverflowStrategy = /*#__PURE__*/ function() {
861
- function RichDisplayOverflowStrategy() {}
862
- var _proto = RichDisplayOverflowStrategy.prototype;
863
- _proto.apply = function apply(lines, sizeResult, layout, style) {
864
- // 安全处理除零情况
865
- var safeDiv = function(a, b) {
866
- return Math.abs(b) > 1e-5 ? a / b : 1;
867
- };
868
- // 用像素空间计算
869
- var frameWpx = layout.maxTextWidth * style.fontScale;
870
- var frameHpx = layout.maxTextHeight * style.fontScale;
871
- var _sizeResult_contentWidth;
872
- var contentWpx = Math.max(1, (_sizeResult_contentWidth = sizeResult.contentWidth) != null ? _sizeResult_contentWidth : sizeResult.canvasWidth);
873
- var _sizeResult_bboxHeight;
874
- var contentHpx = Math.max(1, (_sizeResult_bboxHeight = sizeResult.bboxHeight) != null ? _sizeResult_bboxHeight : sizeResult.canvasHeight);
875
- // 只缩小不放大(基于像素空间计算)
876
- var s = Math.min(1, safeDiv(frameWpx, contentWpx), safeDiv(frameHpx, contentHpx));
877
- // 浮点精度处理
878
- if (s > 0.9999) {
879
- return {
880
- globalScale: 1
881
- };
882
- }
883
- // 统一缩放所有几何量
884
- for(var _iterator = _create_for_of_iterator_helper_loose(lines), _step; !(_step = _iterator()).done;){
885
- var line = _step.value;
886
- line.width *= s;
887
- line.lineHeight *= s; // 缩小行距(你的 gap-only 模式行高=gapPx)
888
- if (line.offsetX) {
889
- for(var i = 0; i < line.offsetX.length; i++){
890
- line.offsetX[i] *= s;
891
- }
892
- }
893
- for(var _iterator1 = _create_for_of_iterator_helper_loose(line.chars || []), _step1; !(_step1 = _iterator1()).done;){
894
- var seg = _step1.value;
895
- for(var _iterator2 = _create_for_of_iterator_helper_loose(seg), _step2; !(_step2 = _iterator2()).done;){
896
- var ch = _step2.value;
897
- ch.x *= s;
898
- }
899
- }
900
- for(var _iterator3 = _create_for_of_iterator_helper_loose(line.richOptions || []), _step3; !(_step3 = _iterator3()).done;){
901
- var opt = _step3.value;
902
- opt.fontSize *= s; // 缩小字形
903
- }
904
- // 同步缩放 asc/desc
905
- if (line.lineAscent != null) {
906
- line.lineAscent *= s;
907
- }
908
- if (line.lineDescent != null) {
909
- line.lineDescent *= s;
910
- }
911
- }
912
- return {
913
- globalScale: s
914
- };
915
- };
916
- return RichDisplayOverflowStrategy;
917
- }();
918
-
919
880
  /**
920
881
  * Clip 溢出策略
921
- * 超出画布部分自然裁切
882
+ * 画布尺寸固定为帧尺寸,超出部分自然裁切
883
+ * 不依赖对齐模式
922
884
  */ var RichClippedOverflowStrategy = /*#__PURE__*/ function() {
923
885
  function RichClippedOverflowStrategy() {}
924
886
  var _proto = RichClippedOverflowStrategy.prototype;
925
- _proto.apply = function apply(lines, sizeResult, layout, style) {
926
- // 不进行任何缩放,直接返回单位缩放系数
887
+ _proto.resolveCanvas = function resolveCanvas(lines, frameWidth, frameHeight, horizontalResult, verticalResult) {
927
888
  return {
928
- globalScale: 1
889
+ canvasWidth: Math.max(1, Math.ceil(frameWidth)),
890
+ canvasHeight: Math.max(1, Math.ceil(frameHeight)),
891
+ renderOffsetX: 0,
892
+ renderOffsetY: 0
929
893
  };
930
894
  };
931
895
  return RichClippedOverflowStrategy;
932
896
  }();
933
897
 
934
898
  /**
935
- * Visible 溢出策略
936
- * 允许内容超出画布可见
937
- */ var RichVisibleOverflowStrategy = /*#__PURE__*/ function() {
938
- function RichVisibleOverflowStrategy() {}
939
- var _proto = RichVisibleOverflowStrategy.prototype;
940
- _proto.apply = function apply(lines, sizeResult, layout, style) {
941
- // 不进行任何缩放或裁剪
899
+ * 扩展画布溢出策略(visible / display 模式共用)
900
+ *
901
+ * 根据对齐后的内容位置检测溢出,对称扩展画布以容纳所有内容。
902
+ * 不依赖对齐模式(textAlign / textVerticalAlign)的枚举值,
903
+ * 仅使用对齐策略输出的位置和行的度量数据。
904
+ *
905
+ * 对称扩展的原因:元素定位以中心点为锚点,
906
+ * 单侧扩展会导致视觉偏移。使用 max(溢出左, 溢出右) 作为双侧扩展量
907
+ * 可以保持中心点稳定并兼容所有对齐模式。
908
+ */ var RichExpandingOverflowStrategy = /*#__PURE__*/ function() {
909
+ function RichExpandingOverflowStrategy() {}
910
+ var _proto = RichExpandingOverflowStrategy.prototype;
911
+ _proto.resolveCanvas = function resolveCanvas(lines, frameWidth, frameHeight, horizontalResult, verticalResult) {
912
+ if (lines.length === 0) {
913
+ return {
914
+ canvasWidth: Math.max(1, frameWidth),
915
+ canvasHeight: Math.max(1, frameHeight),
916
+ renderOffsetX: 0,
917
+ renderOffsetY: 0
918
+ };
919
+ }
920
+ // ── 垂直溢出检测 ──
921
+ // 遍历每行基线,结合 ascent/descent 找出实际渲染范围
922
+ var contentTop = Infinity;
923
+ var contentBottom = -Infinity;
924
+ var currentY = verticalResult.baselineY;
925
+ for(var i = 0; i < lines.length; i++){
926
+ var _lines_i_lineAscent;
927
+ var asc = (_lines_i_lineAscent = lines[i].lineAscent) != null ? _lines_i_lineAscent : 0;
928
+ var _lines_i_lineDescent;
929
+ var desc = (_lines_i_lineDescent = lines[i].lineDescent) != null ? _lines_i_lineDescent : 0;
930
+ // 当 lineHeight >= textHeight 时,margin 为正,需要把行高边距纳入内容范围;
931
+ // 当 lineHeight < textHeight 时,margin 为负,此时字形超出行高框,
932
+ // 必须用原始字形边界(margin=0),否则负 margin 会把边界向内收缩,
933
+ // 导致检测不到溢出,最终首行顶部/末行底部被裁切。
934
+ var textHeight = asc + desc;
935
+ var margin = Math.max(0, (lines[i].lineHeight - textHeight) / 2);
936
+ contentTop = Math.min(contentTop, currentY - asc - margin);
937
+ contentBottom = Math.max(contentBottom, currentY + desc + margin);
938
+ if (i < lines.length - 1) {
939
+ currentY += lines[i + 1].lineHeight;
940
+ }
941
+ }
942
+ var overflowTop = Math.max(0, -contentTop);
943
+ var overflowBottom = Math.max(0, contentBottom - frameHeight);
944
+ // 对称扩展(保持中心点稳定)
945
+ var expandV = Math.max(overflowTop, overflowBottom);
946
+ // ── 水平溢出检测 ──
947
+ var contentLeft = Infinity;
948
+ var contentRight = -Infinity;
949
+ for(var i1 = 0; i1 < lines.length; i1++){
950
+ var _horizontalResult_lineOffsets_i;
951
+ var xOff = (_horizontalResult_lineOffsets_i = horizontalResult.lineOffsets[i1]) != null ? _horizontalResult_lineOffsets_i : 0;
952
+ var _lines_i_width;
953
+ var w = (_lines_i_width = lines[i1].width) != null ? _lines_i_width : 0;
954
+ contentLeft = Math.min(contentLeft, xOff);
955
+ contentRight = Math.max(contentRight, xOff + w);
956
+ }
957
+ if (!isFinite(contentLeft)) {
958
+ contentLeft = 0;
959
+ }
960
+ if (!isFinite(contentRight)) {
961
+ contentRight = 0;
962
+ }
963
+ var overflowLeft = Math.max(0, -contentLeft);
964
+ var overflowRight = Math.max(0, contentRight - frameWidth);
965
+ // 对称扩展
966
+ var expandH = Math.max(overflowLeft, overflowRight);
967
+ // ── 最终画布尺寸 ──
968
+ var canvasWidth = Math.ceil(frameWidth + expandH * 2);
969
+ var canvasHeight = Math.ceil(frameHeight + expandV * 2);
942
970
  return {
943
- globalScale: 1
971
+ canvasWidth: Math.max(1, canvasWidth),
972
+ canvasHeight: Math.max(1, canvasHeight),
973
+ renderOffsetX: expandH,
974
+ renderOffsetY: expandV
944
975
  };
945
976
  };
946
- return RichVisibleOverflowStrategy;
977
+ return RichExpandingOverflowStrategy;
947
978
  }();
948
979
 
949
980
  /**
950
981
  * 富文本水平对齐策略
951
- * 直接使用已缩放的行宽计算偏移量
982
+ * 在帧坐标系中计算每行的水平偏移,不依赖溢出模式
952
983
  */ var RichHorizontalAlignStrategyImpl = /*#__PURE__*/ function() {
953
984
  function RichHorizontalAlignStrategyImpl() {}
954
985
  var _proto = RichHorizontalAlignStrategyImpl.prototype;
955
- _proto.getHorizontalOffsets = function getHorizontalOffsets(lines, sizeResult, overflowResult, layout, style) {
956
- var _sizeResult_containerWidth;
957
- var containerWidthPx = (_sizeResult_containerWidth = sizeResult.containerWidth) != null ? _sizeResult_containerWidth : layout.maxTextWidth * style.fontScale;
958
- var _sizeResult_baselineCompensationX;
959
- var compX = (_sizeResult_baselineCompensationX = sizeResult.baselineCompensationX) != null ? _sizeResult_baselineCompensationX : 0;
960
- // 使用像素单位的容器宽度
961
- var baseOffsets = lines.map(function(line) {
962
- return layout.getOffsetXRich(style, containerWidthPx, line.width);
986
+ _proto.getHorizontalOffsets = function getHorizontalOffsets(lines, frameWidth, layout, style) {
987
+ // 在帧坐标系 [0, frameWidth] 中计算对齐偏移
988
+ var lineOffsets = lines.map(function(line) {
989
+ return layout.getOffsetXRich(style, frameWidth, line.width);
963
990
  });
964
- // visible 模式下使用 baseOffset + baselineCompensationX
965
- var lineOffsets = layout.overflow === spec.TextOverflow.visible ? baseOffsets.map(function(x) {
966
- return x + compX;
967
- }) : baseOffsets;
968
991
  return {
969
992
  lineOffsets: lineOffsets
970
993
  };
@@ -974,147 +997,63 @@ var RichTextLayout = /*#__PURE__*/ function() {
974
997
 
975
998
  /**
976
999
  * 富文本垂直对齐策略
977
- * 直接使用已缩放的行高
1000
+ * 在帧坐标系中计算基线位置,不依赖溢出模式
1001
+ *
1002
+ * 算法:
1003
+ * 1. 从行数据重建各行基线(baseline[0]=0, baseline[i]=baseline[i-1]+lineHeight[i])
1004
+ * 2. 计算 bbox(包含行高边距)
1005
+ * 3. 根据 textVerticalAlign 将内容定位到帧中
978
1006
  */ var RichVerticalAlignStrategyImpl = /*#__PURE__*/ function() {
979
1007
  function RichVerticalAlignStrategyImpl() {}
980
1008
  var _proto = RichVerticalAlignStrategyImpl.prototype;
981
- _proto.getVerticalOffsets = function getVerticalOffsets(lines, sizeResult, overflowResult, layout, style, singleLineHeight) {
982
- var _sizeResult_baselineCompensationY;
983
- var compY = (_sizeResult_baselineCompensationY = sizeResult.baselineCompensationY) != null ? _sizeResult_baselineCompensationY : 0;
984
- var baselineY = 0;
1009
+ _proto.getVerticalOffsets = function getVerticalOffsets(lines, frameHeight, layout) {
985
1010
  if (lines.length === 0) {
986
1011
  return {
987
1012
  baselineY: 0,
988
1013
  lineYOffsets: []
989
1014
  };
990
1015
  }
991
- switch(layout.overflow){
992
- case spec.TextOverflow.visible:
993
- {
994
- // frame-based 计算
995
- var frameHpx = layout.maxTextHeight * style.fontScale;
996
- var _sizeResult_bboxTop;
997
- var bboxTop = (_sizeResult_bboxTop = sizeResult.bboxTop) != null ? _sizeResult_bboxTop : 0;
998
- var _sizeResult_bboxHeight, _sizeResult_bboxBottom;
999
- var bboxBottom = (_sizeResult_bboxBottom = sizeResult.bboxBottom) != null ? _sizeResult_bboxBottom : bboxTop + ((_sizeResult_bboxHeight = sizeResult.bboxHeight) != null ? _sizeResult_bboxHeight : 0);
1000
- var _sizeResult_bboxHeight1;
1001
- var bboxHeight = (_sizeResult_bboxHeight1 = sizeResult.bboxHeight) != null ? _sizeResult_bboxHeight1 : bboxBottom - bboxTop;
1002
- // 计算 frame 基线
1003
- var baselineYFrame = 0;
1004
- switch(layout.textVerticalAlign){
1005
- case spec.TextVerticalAlign.top:
1006
- baselineYFrame = -bboxTop;
1007
- break;
1008
- case spec.TextVerticalAlign.middle:
1009
- baselineYFrame = (frameHpx - bboxHeight) / 2 - bboxTop;
1010
- break;
1011
- case spec.TextVerticalAlign.bottom:
1012
- baselineYFrame = frameHpx - bboxHeight - bboxTop;
1013
- break;
1014
- }
1015
- baselineY = baselineYFrame + compY; // 关键:叠加"向下移动 E"
1016
- break;
1017
- }
1018
- case spec.TextOverflow.clip:
1019
- {
1020
- // 让 clip 垂直对齐逻辑与 display 保持一致(基于 bbox 的几何模型)
1021
- var frameHpx1 = layout.maxTextHeight * style.fontScale;
1022
- // 用缩放后的行数据重建 baselines 和 bbox
1023
- var baselines = [
1024
- 0
1025
- ];
1026
- for(var i = 1; i < lines.length; i++){
1027
- baselines[i] = baselines[i - 1] + lines[i].lineHeight;
1028
- }
1029
- var bboxTop1 = Infinity;
1030
- var bboxBottom1 = -Infinity;
1031
- for(var i1 = 0; i1 < lines.length; i1++){
1032
- var _lines_i_lineAscent;
1033
- var asc = (_lines_i_lineAscent = lines[i1].lineAscent) != null ? _lines_i_lineAscent : 0;
1034
- var _lines_i_lineDescent;
1035
- var desc = (_lines_i_lineDescent = lines[i1].lineDescent) != null ? _lines_i_lineDescent : 0;
1036
- bboxTop1 = Math.min(bboxTop1, baselines[i1] - asc);
1037
- bboxBottom1 = Math.max(bboxBottom1, baselines[i1] + desc);
1038
- }
1039
- var bboxHeight1 = bboxBottom1 - bboxTop1;
1040
- var baselineClipY = 0;
1041
- switch(layout.textVerticalAlign){
1042
- case spec.TextVerticalAlign.top:
1043
- baselineClipY = -bboxTop1;
1044
- break;
1045
- case spec.TextVerticalAlign.middle:
1046
- baselineClipY = (frameHpx1 - bboxHeight1) / 2 - bboxTop1;
1047
- break;
1048
- case spec.TextVerticalAlign.bottom:
1049
- baselineClipY = frameHpx1 - bboxHeight1 - bboxTop1;
1050
- break;
1051
- }
1052
- baselineY = baselineClipY + compY;
1053
- break;
1054
- }
1055
- case spec.TextOverflow.display:
1056
- {
1057
- // display 垂直对齐改为基于 bbox,而不是 getOffsetYRich
1058
- var frameHpx2 = layout.maxTextHeight * style.fontScale;
1059
- // 用缩放后的行数据重建 baselines 和 bbox
1060
- var baselines1 = [
1061
- 0
1062
- ];
1063
- for(var i2 = 1; i2 < lines.length; i2++){
1064
- baselines1[i2] = baselines1[i2 - 1] + lines[i2].lineHeight;
1065
- }
1066
- // 计算 bboxTop / bboxBottom(使用行的测量值 lineAscent/lineDescent)
1067
- var bboxTop2 = Infinity;
1068
- var bboxBottom2 = -Infinity;
1069
- for(var i3 = 0; i3 < lines.length; i3++){
1070
- var _lines_i_lineAscent1;
1071
- var asc1 = (_lines_i_lineAscent1 = lines[i3].lineAscent) != null ? _lines_i_lineAscent1 : 0;
1072
- var _lines_i_lineDescent1;
1073
- var desc1 = (_lines_i_lineDescent1 = lines[i3].lineDescent) != null ? _lines_i_lineDescent1 : 0;
1074
- bboxTop2 = Math.min(bboxTop2, baselines1[i3] - asc1);
1075
- bboxBottom2 = Math.max(bboxBottom2, baselines1[i3] + desc1);
1076
- }
1077
- var bboxHeight2 = bboxBottom2 - bboxTop2;
1078
- // 计算 display 模式的基线
1079
- var baselineDisplayY = 0;
1080
- switch(layout.textVerticalAlign){
1081
- case spec.TextVerticalAlign.top:
1082
- baselineDisplayY = -bboxTop2;
1083
- break;
1084
- case spec.TextVerticalAlign.middle:
1085
- baselineDisplayY = (frameHpx2 - bboxHeight2) / 2 - bboxTop2;
1086
- break;
1087
- case spec.TextVerticalAlign.bottom:
1088
- baselineDisplayY = frameHpx2 - bboxHeight2 - bboxTop2;
1089
- break;
1090
- }
1091
- // 后续 lineYOffsets 保持按行高累计
1092
- var lineYOffsets = [];
1093
- var currentY = baselineDisplayY;
1094
- for(var i4 = 0; i4 < lines.length; i4++){
1095
- lineYOffsets.push(currentY);
1096
- if (i4 < lines.length - 1) {
1097
- currentY += lines[i4 + 1].lineHeight;
1098
- }
1099
- }
1100
- return {
1101
- baselineY: baselineDisplayY,
1102
- lineYOffsets: lineYOffsets
1103
- };
1104
- }
1016
+ // 1. 重建各行基线(相对于第一行基线=0)
1017
+ var baselines = [
1018
+ 0
1019
+ ];
1020
+ for(var i = 1; i < lines.length; i++){
1021
+ baselines[i] = baselines[i - 1] + lines[i].lineHeight;
1105
1022
  }
1106
- // 下面行偏移保持不变
1107
- var lineYOffsets1 = [];
1108
- var currentY1 = baselineY;
1109
- lines.forEach(function(line, index) {
1110
- lineYOffsets1.push(currentY1);
1111
- if (index < lines.length - 1) {
1112
- currentY1 += lines[index + 1].lineHeight;
1113
- }
1023
+ // 2. 计算内容 bbox(含行高边距)
1024
+ var bboxTop = Infinity;
1025
+ var bboxBottom = -Infinity;
1026
+ for(var i1 = 0; i1 < lines.length; i1++){
1027
+ var _lines_i_lineAscent;
1028
+ var asc = (_lines_i_lineAscent = lines[i1].lineAscent) != null ? _lines_i_lineAscent : 0;
1029
+ var _lines_i_lineDescent;
1030
+ var desc = (_lines_i_lineDescent = lines[i1].lineDescent) != null ? _lines_i_lineDescent : 0;
1031
+ var textHeight = asc + desc;
1032
+ var margin = (lines[i1].lineHeight - textHeight) / 2;
1033
+ bboxTop = Math.min(bboxTop, baselines[i1] - asc - margin);
1034
+ bboxBottom = Math.max(bboxBottom, baselines[i1] + desc + margin);
1035
+ }
1036
+ var bboxHeight = bboxBottom - bboxTop;
1037
+ // 3. 在帧坐标系 [0, frameHeight] 中定位第一行基线
1038
+ var baselineY = 0;
1039
+ switch(layout.textVerticalAlign){
1040
+ case spec.TextVerticalAlign.top:
1041
+ baselineY = -bboxTop;
1042
+ break;
1043
+ case spec.TextVerticalAlign.middle:
1044
+ baselineY = (frameHeight - bboxHeight) / 2 - bboxTop;
1045
+ break;
1046
+ case spec.TextVerticalAlign.bottom:
1047
+ baselineY = frameHeight - bboxHeight - bboxTop;
1048
+ break;
1049
+ }
1050
+ // 4. 各行 Y 坐标
1051
+ var lineYOffsets = baselines.map(function(b) {
1052
+ return baselineY + b;
1114
1053
  });
1115
1054
  return {
1116
1055
  baselineY: baselineY,
1117
- lineYOffsets: lineYOffsets1
1056
+ lineYOffsets: lineYOffsets
1118
1057
  };
1119
1058
  };
1120
1059
  return RichVerticalAlignStrategyImpl;
@@ -1123,11 +1062,12 @@ var RichTextLayout = /*#__PURE__*/ function() {
1123
1062
  /**
1124
1063
  * 富文本策略工厂
1125
1064
  * 负责创建各种策略实例
1065
+ *
1066
+ * 管线顺序:Wrap → scaleLinesToFit(display)→ Align → Overflow.resolveCanvas
1126
1067
  */ var RichTextStrategyFactory = /*#__PURE__*/ function() {
1127
1068
  function RichTextStrategyFactory() {}
1128
1069
  /**
1129
1070
  * 创建换行策略
1130
- * 根据wrapEnabled参数决定使用哪种策略
1131
1071
  */ RichTextStrategyFactory.createWrapStrategy = function createWrapStrategy(wrapEnabled) {
1132
1072
  if (wrapEnabled) {
1133
1073
  return new RichWrapEnabledStrategy();
@@ -1136,17 +1076,17 @@ var RichTextLayout = /*#__PURE__*/ function() {
1136
1076
  }
1137
1077
  };
1138
1078
  /**
1139
- * 创建溢出策略
1140
- * 支持三种模式:clip(裁切)、display(行级缩放)、visible(不缩放不裁剪)
1079
+ * 创建溢出策略(画布解析)
1080
+ * clip:画布=帧,超出裁切
1081
+ * display/visible:检测溢出并对称扩展画布
1141
1082
  */ RichTextStrategyFactory.createOverflowStrategy = function createOverflowStrategy(mode) {
1142
1083
  switch(mode){
1143
1084
  case spec.TextOverflow.clip:
1144
1085
  return new RichClippedOverflowStrategy();
1145
1086
  case spec.TextOverflow.display:
1146
- return new RichDisplayOverflowStrategy();
1147
1087
  case spec.TextOverflow.visible:
1148
1088
  default:
1149
- return new RichVisibleOverflowStrategy();
1089
+ return new RichExpandingOverflowStrategy();
1150
1090
  }
1151
1091
  };
1152
1092
  /**
@@ -1162,6 +1102,66 @@ var RichTextLayout = /*#__PURE__*/ function() {
1162
1102
  return RichTextStrategyFactory;
1163
1103
  }();
1164
1104
 
1105
+ function _extends() {
1106
+ _extends = Object.assign || function assign(target) {
1107
+ for(var i = 1; i < arguments.length; i++){
1108
+ var source = arguments[i];
1109
+ for(var key in source)if (Object.prototype.hasOwnProperty.call(source, key)) target[key] = source[key];
1110
+ }
1111
+ return target;
1112
+ };
1113
+ return _extends.apply(this, arguments);
1114
+ }
1115
+
1116
+ /**
1117
+ * 将行数据等比缩小以适配帧尺寸(display 模式使用)
1118
+ * 只缩小不放大,就地修改行数据
1119
+ */ function scaleLinesToFit(lines, contentWidth, contentHeight, frameWidth, frameHeight) {
1120
+ var safeDiv = function(a, b) {
1121
+ return Math.abs(b) > 1e-5 ? a / b : 1;
1122
+ };
1123
+ var safeContentW = Math.max(1, contentWidth);
1124
+ var safeContentH = Math.max(1, contentHeight);
1125
+ // 只缩小不放大
1126
+ var s = Math.min(1, safeDiv(frameWidth, safeContentW), safeDiv(frameHeight, safeContentH));
1127
+ // 浮点精度处理
1128
+ if (s > 0.9999) {
1129
+ return;
1130
+ }
1131
+ for(var _iterator = _create_for_of_iterator_helper_loose(lines), _step; !(_step = _iterator()).done;){
1132
+ var line = _step.value;
1133
+ line.width *= s;
1134
+ line.lineHeight *= s;
1135
+ if (line.offsetX) {
1136
+ for(var i = 0; i < line.offsetX.length; i++){
1137
+ line.offsetX[i] *= s;
1138
+ }
1139
+ }
1140
+ for(var _iterator1 = _create_for_of_iterator_helper_loose(line.chars || []), _step1; !(_step1 = _iterator1()).done;){
1141
+ var seg = _step1.value;
1142
+ for(var _iterator2 = _create_for_of_iterator_helper_loose(seg), _step2; !(_step2 = _iterator2()).done;){
1143
+ var ch = _step2.value;
1144
+ ch.x *= s;
1145
+ }
1146
+ }
1147
+ // 创建新的 richOptions 数组,避免修改原始对象
1148
+ var originalOptions = line.richOptions || [];
1149
+ var newOptions = [];
1150
+ for(var i1 = 0; i1 < originalOptions.length; i1++){
1151
+ newOptions.push(_extends({}, originalOptions[i1], {
1152
+ fontSize: originalOptions[i1].fontSize * s
1153
+ }));
1154
+ }
1155
+ line.richOptions = newOptions;
1156
+ if (line.lineAscent != null) {
1157
+ line.lineAscent *= s;
1158
+ }
1159
+ if (line.lineDescent != null) {
1160
+ line.lineDescent *= s;
1161
+ }
1162
+ }
1163
+ }
1164
+
1165
1165
  var seed = 0;
1166
1166
  var RichTextComponent = /*#__PURE__*/ function(MaskableGraphic) {
1167
1167
  _inherits(RichTextComponent, MaskableGraphic);
@@ -1171,10 +1171,10 @@ var RichTextComponent = /*#__PURE__*/ function(MaskableGraphic) {
1171
1171
  _this.isDirty = true;
1172
1172
  _this.text = "";
1173
1173
  _this.processedTextOptions = [];
1174
- _this.singleLineHeight = 1.571;
1175
- _this.size = null;
1176
- _this.initialized = false;
1177
- _this.canvasSize = null;
1174
+ /** @deprecated Use for legacy mode*/ _this.singleLineHeight = 1.571;
1175
+ /** @deprecated Use for legacy mode*/ _this.size = null;
1176
+ /** @deprecated Use for legacy mode*/ _this.initialized = false;
1177
+ /** @deprecated Use for legacy mode*/ _this.canvasSize = null;
1178
1178
  _this.SCALE_FACTOR = 0.11092565;
1179
1179
  _this.ALPHA_FIX_VALUE = 1 / 255;
1180
1180
  _this.name = "MRichText" + seed++;
@@ -1206,12 +1206,12 @@ var RichTextComponent = /*#__PURE__*/ function(MaskableGraphic) {
1206
1206
  this.textLayout.textVerticalAlign = spec.TextVerticalAlign.middle;
1207
1207
  }
1208
1208
  this.updateStrategies();
1209
- this.renderText(options);
1209
+ this.updateTexture();
1210
1210
  // 设置默认颜色(math.Color)
1211
1211
  this.material.setColor("_Color", new math.Color(1, 1, 1, 1));
1212
1212
  };
1213
1213
  /**
1214
- * 使用策略结果绘制文本
1214
+ * 根据布局配置更新策略实例
1215
1215
  */ _proto.updateStrategies = function updateStrategies() {
1216
1216
  var layout = this.textLayout;
1217
1217
  if (layout) {
@@ -1266,13 +1266,6 @@ var RichTextComponent = /*#__PURE__*/ function(MaskableGraphic) {
1266
1266
  this.updateStrategies();
1267
1267
  this.isDirty = true;
1268
1268
  };
1269
- _proto.renderText = function renderText(options) {
1270
- var size = options.size;
1271
- if (size) {
1272
- this.canvasSize = new math.Vector2(size[0], size[1]);
1273
- }
1274
- this.updateTexture();
1275
- };
1276
1269
  /**
1277
1270
  * 更新文本
1278
1271
  * @returns
@@ -1418,6 +1411,14 @@ var RichTextComponent = /*#__PURE__*/ function(MaskableGraphic) {
1418
1411
  };
1419
1412
  /**
1420
1413
  * 使用策略管线路径的渲染方法
1414
+ *
1415
+ * 管线顺序:
1416
+ * 1. Wrap → 换行与度量
1417
+ * 2. SizeMode → 根据 autoResize 回写帧尺寸
1418
+ * 3. ContentScale → 内容缩放(仅 display 模式)
1419
+ * 4. Alignment → 在帧坐标系中对齐(不依赖 overflow)
1420
+ * 5. Overflow → 画布解析:确定最终画布尺寸与渲染偏移(不依赖对齐模式)
1421
+ * 6. Render → 绘制
1421
1422
  */ _proto.updateTextureWithStrategies = function updateTextureWithStrategies(flipY) {
1422
1423
  var _this = this;
1423
1424
  if (!this.isDirty || !this.context || !this.canvas) {
@@ -1431,227 +1432,84 @@ var RichTextComponent = /*#__PURE__*/ function(MaskableGraphic) {
1431
1432
  if (!context) {
1432
1433
  return;
1433
1434
  }
1434
- // 步骤1: 换行策略计算行信息
1435
- var wrapResult = this.richWrapStrategy.computeLines(this.processedTextOptions, context, this.textStyle, layout, this.singleLineHeight, this.textStyle.fontScale, letterSpace, this.SCALE_FACTOR);
1436
- if (wrapResult.lines.length === 0 || wrapResult.maxLineWidth === 0 || wrapResult.totalHeight === 0) {
1437
- this.isDirty = false;
1438
- return;
1435
+ var fontScale = this.textStyle.fontScale;
1436
+ // autoWidth 模式下先去掉宽度约束,避免内容被提前换行
1437
+ if (layout.autoResize === spec.TextSizeMode.autoWidth) {
1438
+ layout.maxTextWidth = Number.MAX_SAFE_INTEGER;
1439
1439
  }
1440
- // 步骤2: 尺寸处理
1441
- var sizeResult = this.resolveCanvasSize(wrapResult, layout, this.textStyle, this.singleLineHeight);
1442
- // 首次渲染时初始化 canvas 尺寸和组件变换
1443
- this.setCanvasSize(sizeResult);
1444
- // 步骤3: 溢出策略处理
1445
- var overflowResult = this.richOverflowStrategy.apply(wrapResult.lines, sizeResult, layout, this.textStyle);
1446
- // 步骤4: 水平对齐策略
1447
- var horizontalAlignResult = this.richHorizontalAlignStrategy.getHorizontalOffsets(wrapResult.lines, sizeResult, overflowResult, layout, this.textStyle);
1448
- // 步骤5: 垂直对齐策略
1449
- var TextVerticalAlignResult = this.richVerticalAlignStrategy.getVerticalOffsets(wrapResult.lines, sizeResult, overflowResult, layout, this.textStyle, this.singleLineHeight);
1450
- // 使用 this.canvasSize 统一设置画布尺寸
1451
- assertExist(this.canvasSize);
1452
- var _this_canvasSize = this.canvasSize, canvasWidth = _this_canvasSize.x, canvasHeight = _this_canvasSize.y;
1453
- // 确保canvas宽高至少为1
1454
- var safeW = Math.max(1, Math.ceil(canvasWidth));
1455
- var safeH = Math.max(1, Math.ceil(canvasHeight));
1456
- layout.width = safeW / this.textStyle.fontScale;
1457
- layout.height = safeH / this.textStyle.fontScale;
1458
- this.renderToTexture(safeW, safeH, flipY, function(context) {
1459
- // 步骤6: 绘制文本
1460
- _this.drawTextWithStrategies(context, wrapResult.lines, horizontalAlignResult, TextVerticalAlignResult, overflowResult, _this.textStyle);
1440
+ // ── 步骤 1: 换行策略(逻辑坐标系,fontScale 不参与排版)──
1441
+ var wrapResult = this.richWrapStrategy.computeLines(this.processedTextOptions, context, this.textStyle, layout, letterSpace);
1442
+ // ── 步骤 2: SizeMode → 帧尺寸(逻辑单位)──
1443
+ // fontScale 不参与排版,全部在逻辑坐标系中完成。
1444
+ var frameW;
1445
+ var frameH;
1446
+ switch(layout.autoResize){
1447
+ case spec.TextSizeMode.autoWidth:
1448
+ frameW = Math.max(1, wrapResult.maxLineWidth || 0);
1449
+ frameH = Math.max(1, wrapResult.totalHeight || 0);
1450
+ break;
1451
+ case spec.TextSizeMode.autoHeight:
1452
+ frameW = layout.maxTextWidth;
1453
+ frameH = Math.max(1, wrapResult.totalHeight || 0);
1454
+ break;
1455
+ case spec.TextSizeMode.fixed:
1456
+ default:
1457
+ frameW = layout.maxTextWidth;
1458
+ frameH = layout.maxTextHeight;
1459
+ break;
1460
+ }
1461
+ // ── 步骤 3: 内容缩放(display 模式缩小以适配帧,其他模式跳过)──
1462
+ if (layout.overflow === spec.TextOverflow.display) {
1463
+ var contentW = Math.max(1, wrapResult.maxLineWidth || 0);
1464
+ var contentH = Math.max(1, wrapResult.totalHeight || 0);
1465
+ scaleLinesToFit(wrapResult.lines, contentW, contentH, frameW, frameH);
1466
+ }
1467
+ // ── 步骤 4: 对齐(在帧坐标系中,不依赖 overflow 模式)──
1468
+ var horizontalAlignResult = this.richHorizontalAlignStrategy.getHorizontalOffsets(wrapResult.lines, frameW, layout, this.textStyle);
1469
+ var verticalAlignResult = this.richVerticalAlignStrategy.getVerticalOffsets(wrapResult.lines, frameH, layout);
1470
+ // ── 步骤 5: 溢出 / 画布解析(不依赖对齐模式枚举)──
1471
+ var overflowResult = this.richOverflowStrategy.resolveCanvas(wrapResult.lines, frameW, frameH, horizontalAlignResult, verticalAlignResult);
1472
+ // 排版结果(逻辑单位)→ 物理像素画布
1473
+ var physicalW = Math.max(1, Math.ceil(overflowResult.canvasWidth * fontScale));
1474
+ var physicalH = Math.max(1, Math.ceil(overflowResult.canvasHeight * fontScale));
1475
+ // 渲染尺寸不随 fontScale 改变
1476
+ this.item.transform.size.set(overflowResult.canvasWidth * this.SCALE_FACTOR * this.SCALE_FACTOR, overflowResult.canvasHeight * this.SCALE_FACTOR * this.SCALE_FACTOR);
1477
+ // 统一回写布局属性(逻辑单位)
1478
+ layout.maxTextWidth = frameW;
1479
+ layout.maxTextHeight = frameH;
1480
+ layout.width = overflowResult.canvasWidth;
1481
+ layout.height = overflowResult.canvasHeight;
1482
+ this.renderToTexture(physicalW, physicalH, flipY, function(context) {
1483
+ // fontScale 仅作为渲染分辨率倍率,排版坐标全部为逻辑单位
1484
+ context.scale(fontScale, fontScale);
1485
+ // ── 步骤 6: 绘制 ──
1486
+ _this.drawTextWithStrategies(context, wrapResult.lines, horizontalAlignResult, verticalAlignResult, overflowResult, _this.textStyle);
1461
1487
  }, {
1462
1488
  disposeOld: false
1463
1489
  });
1464
1490
  this.isDirty = false;
1465
1491
  };
1466
- _proto.setCanvasSize = function setCanvasSize(sizeResult) {
1467
- var _this = this;
1468
- if (this.size === undefined || this.size === null) {
1469
- this.size = this.item.transform.size.clone();
1470
- }
1471
- var _this_size = this.size, _this_size_x = _this_size.x, x = _this_size_x === void 0 ? 1 : _this_size_x, _this_size_y = _this_size.y, y = _this_size_y === void 0 ? 1 : _this_size_y;
1472
- var layout = this.textLayout;
1473
- var fontScale = this.textStyle.fontScale || 1;
1474
- // 防止 frameW / frameH 为 0
1475
- var frameW = Math.max(1, layout.maxTextWidth || 0);
1476
- var frameH = Math.max(1, layout.maxTextHeight || 0);
1477
- switch(layout.overflow){
1478
- case spec.TextOverflow.visible:
1479
- {
1480
- var frameWpx = frameW * fontScale;
1481
- var frameHpx = frameH * fontScale;
1482
- var _sizeResult_bboxTop;
1483
- var bboxTop = (_sizeResult_bboxTop = sizeResult.bboxTop) != null ? _sizeResult_bboxTop : 0;
1484
- var _sizeResult_bboxHeight, _sizeResult_bboxBottom;
1485
- var bboxBottom = (_sizeResult_bboxBottom = sizeResult.bboxBottom) != null ? _sizeResult_bboxBottom : bboxTop + ((_sizeResult_bboxHeight = sizeResult.bboxHeight) != null ? _sizeResult_bboxHeight : 0);
1486
- var _sizeResult_bboxHeight1;
1487
- var bboxHeight = (_sizeResult_bboxHeight1 = sizeResult.bboxHeight) != null ? _sizeResult_bboxHeight1 : bboxBottom - bboxTop;
1488
- // 计算 frame 基线
1489
- var baselineYFrame = 0;
1490
- switch(layout.textVerticalAlign){
1491
- case spec.TextVerticalAlign.top:
1492
- baselineYFrame = -bboxTop;
1493
- break;
1494
- case spec.TextVerticalAlign.middle:
1495
- baselineYFrame = (frameHpx - bboxHeight) / 2 - bboxTop;
1496
- break;
1497
- case spec.TextVerticalAlign.bottom:
1498
- baselineYFrame = frameHpx - bboxHeight - bboxTop;
1499
- break;
1500
- }
1501
- // 上下溢出检测
1502
- var contentTopInFrame = baselineYFrame + bboxTop;
1503
- var contentBottomInFrame = baselineYFrame + bboxBottom;
1504
- var overflowTop = Math.max(0, -contentTopInFrame);
1505
- var overflowBottom = Math.max(0, contentBottomInFrame - frameHpx);
1506
- // 垂直扩张
1507
- var expandTop = overflowTop;
1508
- var expandBottom = overflowBottom;
1509
- switch(layout.textVerticalAlign){
1510
- case spec.TextVerticalAlign.top:
1511
- {
1512
- var E = overflowBottom;
1513
- expandTop = E;
1514
- expandBottom = E;
1515
- break;
1516
- }
1517
- case spec.TextVerticalAlign.bottom:
1518
- {
1519
- var E1 = overflowTop;
1520
- expandTop = E1;
1521
- expandBottom = E1;
1522
- break;
1523
- }
1524
- case spec.TextVerticalAlign.middle:
1525
- {
1526
- // 保持非对称:上扩 overflowTop,下扩 overflowBottom
1527
- expandTop = overflowTop;
1528
- expandBottom = overflowBottom;
1529
- break;
1530
- }
1531
- }
1532
- // 位移补偿:始终使用 expandTop
1533
- var compY = expandTop;
1534
- // 水平扩张
1535
- var lines = sizeResult.lines || [];
1536
- // 1. 先按 frameWpx 计算每行的对齐起点(逻辑对齐)
1537
- var xOffsetsFrame = lines.map(function(line) {
1538
- return layout.getOffsetXRich(_this.textStyle, frameWpx, line.width);
1539
- });
1540
- // 2. 用对齐后的偏移 + 行宽算出内容的左右边界
1541
- var contentMinX = Infinity;
1542
- var contentMaxX = -Infinity;
1543
- for(var i = 0; i < lines.length; i++){
1544
- var _xOffsetsFrame_i;
1545
- var off = (_xOffsetsFrame_i = xOffsetsFrame[i]) != null ? _xOffsetsFrame_i : 0;
1546
- var _lines_i_width;
1547
- var w = (_lines_i_width = lines[i].width) != null ? _lines_i_width : 0; // 像素宽
1548
- contentMinX = Math.min(contentMinX, off);
1549
- contentMaxX = Math.max(contentMaxX, off + w);
1550
- }
1551
- if (!isFinite(contentMinX)) {
1552
- contentMinX = 0;
1553
- }
1554
- if (!isFinite(contentMaxX)) {
1555
- contentMaxX = 0;
1556
- }
1557
- // 3. 计算内容相对于 frame 的越界量
1558
- var overflowLeft = Math.max(0, -contentMinX); // 内容左边 < 0
1559
- var overflowRight = Math.max(0, contentMaxX - frameWpx); // 内容右边 > frameWpx?
1560
- // 4. 让 canvas 左右都扩张到能容下整个内容 bbox
1561
- var expandLeft = overflowLeft;
1562
- var expandRight = overflowRight;
1563
- // 5. 最终 canvas 宽高
1564
- var finalWpx = Math.ceil(frameWpx + expandLeft + expandRight);
1565
- var finalHpx = Math.ceil(frameHpx + expandTop + expandBottom);
1566
- // 记录补偿,供垂直对齐策略叠加
1567
- sizeResult.baselineCompensationX = expandLeft;
1568
- sizeResult.baselineCompensationY = compY;
1569
- // containerWidth 用 frameWpx,而不是 finalWpx
1570
- sizeResult.containerWidth = frameWpx;
1571
- sizeResult.canvasWidth = finalWpx;
1572
- sizeResult.canvasHeight = finalHpx;
1573
- this.canvasSize = new math.Vector2(finalWpx, finalHpx);
1574
- var _this_size1;
1575
- var _ref = (_this_size1 = this.size) != null ? _this_size1 : this.item.transform.size, _ref_x = _ref.x, x1 = _ref_x === void 0 ? 1 : _ref_x, _ref_y = _ref.y, y1 = _ref_y === void 0 ? 1 : _ref_y;
1576
- // 实际元素渲染尺寸不随着 fontScale 改变
1577
- this.item.transform.size.set(x1 * finalWpx / fontScale * this.SCALE_FACTOR * this.SCALE_FACTOR, y1 * finalHpx / fontScale * this.SCALE_FACTOR * this.SCALE_FACTOR);
1578
- this.initialized = true;
1579
- break;
1580
- }
1581
- case spec.TextOverflow.clip:
1582
- {
1583
- var frameWpx1 = frameW * fontScale;
1584
- var frameHpx1 = frameH * fontScale;
1585
- // 直接使用 frame 尺寸作为画布尺寸
1586
- sizeResult.canvasWidth = frameWpx1;
1587
- sizeResult.canvasHeight = frameHpx1;
1588
- // clip 模式不需要任何补偿
1589
- sizeResult.baselineCompensationX = 0;
1590
- sizeResult.baselineCompensationY = 0;
1591
- // 设置 canvas 和节点变换
1592
- this.canvasSize = new math.Vector2(frameWpx1, frameHpx1);
1593
- // 把 layout 的尺寸更新为 frame 尺寸
1594
- layout.width = frameW;
1595
- layout.height = frameH;
1596
- var _this_size2;
1597
- var _ref1 = (_this_size2 = this.size) != null ? _this_size2 : this.item.transform.size, _ref_x1 = _ref1.x, x2 = _ref_x1 === void 0 ? 1 : _ref_x1, _ref_y1 = _ref1.y, y2 = _ref_y1 === void 0 ? 1 : _ref_y1;
1598
- this.item.transform.size.set(x2 * frameWpx1 / fontScale * this.SCALE_FACTOR * this.SCALE_FACTOR, y2 * frameHpx1 / fontScale * this.SCALE_FACTOR * this.SCALE_FACTOR);
1599
- this.initialized = true;
1600
- break;
1601
- }
1602
- case spec.TextOverflow.display:
1603
- {
1604
- if (!this.initialized) {
1605
- var frameWpx2 = frameW * fontScale;
1606
- var frameHpx2 = frameH * fontScale;
1607
- this.canvasSize = new math.Vector2(frameWpx2, frameHpx2);
1608
- this.item.transform.size.set(x * frameWpx2 / fontScale * this.SCALE_FACTOR * this.SCALE_FACTOR, y * frameHpx2 / fontScale * this.SCALE_FACTOR * this.SCALE_FACTOR);
1609
- this.initialized = true;
1610
- }
1611
- break;
1612
- }
1613
- }
1614
- };
1615
- /**
1616
- * 尺寸处理
1617
- */ _proto.resolveCanvasSize = function resolveCanvasSize(wrapResult, layout, style, singleLineHeight) {
1618
- var canvasWidth = Math.max(1, wrapResult.maxLineWidth || 0);
1619
- var canvasHeight = Math.max(1, wrapResult.totalHeight || 0); // stepTotalHeight
1620
- layout.width = canvasWidth / style.fontScale;
1621
- layout.height = canvasHeight / style.fontScale;
1622
- return {
1623
- canvasWidth: canvasWidth,
1624
- canvasHeight: canvasHeight,
1625
- contentWidth: wrapResult.maxLineWidth,
1626
- bboxTop: wrapResult.bboxTop,
1627
- bboxBottom: wrapResult.bboxBottom,
1628
- bboxHeight: wrapResult.bboxHeight,
1629
- // 为 visible 模式的画布尺寸计算保留行信息
1630
- lines: wrapResult.lines
1631
- };
1632
- };
1633
1492
  /**
1634
1493
  * 使用策略结果绘制文本
1635
- */ _proto.drawTextWithStrategies = function drawTextWithStrategies(context, lines, horizontalAlignResult, TextVerticalAlignResult, overflowResult, textStyle) {
1636
- var currentBaselineY = TextVerticalAlignResult.baselineY;
1494
+ * 坐标 = 帧坐标(对齐结果) + 渲染偏移(溢出结果)
1495
+ */ _proto.drawTextWithStrategies = function drawTextWithStrategies(context, lines, horizontalAlignResult, verticalAlignResult, overflowResult, textStyle) {
1496
+ var renderOffsetX = overflowResult.renderOffsetX, renderOffsetY = overflowResult.renderOffsetY;
1497
+ var currentBaselineY = verticalAlignResult.baselineY + renderOffsetY;
1637
1498
  var lineOffsets = horizontalAlignResult.lineOffsets;
1638
1499
  lines.forEach(function(line, index) {
1639
1500
  var richOptions = line.richOptions, chars = line.chars;
1640
- var xOffset = lineOffsets[index];
1501
+ var xOffset = lineOffsets[index] + renderOffsetX;
1641
1502
  var yOffset = currentBaselineY;
1642
1503
  richOptions.forEach(function(options, segIndex) {
1643
- var fontScale = textStyle.fontScale, textColor = textStyle.textColor, textFamily = textStyle.fontFamily, textWeight = textStyle.textWeight, richStyle = textStyle.fontStyle;
1504
+ var textColor = textStyle.textColor, textFamily = textStyle.fontFamily, textWeight = textStyle.textWeight, richStyle = textStyle.fontStyle;
1644
1505
  var fontSize = options.fontSize, _options_fontColor = options.fontColor, fontColor = _options_fontColor === void 0 ? textColor : _options_fontColor, _options_fontFamily = options.fontFamily, fontFamily = _options_fontFamily === void 0 ? textFamily : _options_fontFamily, _options_fontWeight = options.fontWeight, fontWeight = _options_fontWeight === void 0 ? textWeight : _options_fontWeight, _options_fontStyle = options.fontStyle, fontStyle = _options_fontStyle === void 0 ? richStyle : _options_fontStyle;
1645
- // 直接使用原始字体大小(已在行数据中预缩放)
1646
1506
  var textSize = fontSize;
1647
- context.font = fontStyle + " " + fontWeight + " " + textSize * fontScale + "px " + fontFamily;
1507
+ context.font = fontStyle + " " + fontWeight + " " + textSize + "px " + fontFamily;
1648
1508
  var r = fontColor[0], g = fontColor[1], b = fontColor[2], a = fontColor[3];
1649
1509
  context.fillStyle = "rgba(" + r + ", " + g + ", " + b + ", " + a + ")";
1650
- // 逐字绘制
1651
1510
  var segStartX = line.offsetX && line.offsetX[segIndex] ? line.offsetX[segIndex] : 0;
1652
1511
  var charArr = chars[segIndex];
1653
1512
  charArr.forEach(function(charDetail) {
1654
- // 直接使用缩放后的字符位置
1655
1513
  context.fillText(charDetail.char, xOffset + segStartX + charDetail.x, yOffset);
1656
1514
  });
1657
1515
  });
@@ -1724,7 +1582,7 @@ applyMixins(RichTextComponent, [
1724
1582
 
1725
1583
  /**
1726
1584
  * 插件版本号
1727
- */ var version = "2.8.7";
1585
+ */ var version = "2.8.9";
1728
1586
  registerPlugin("rich-text", RichTextLoader);
1729
1587
  logger.info("Plugin rich text version: " + version + ".");
1730
1588
  if (version !== EFFECTS.version) {