@galacean/effects-plugin-rich-text 2.8.8 → 2.8.10

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.js 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.8
6
+ * Version: v2.8.10
7
7
  */
8
8
 
9
9
  'use strict';
@@ -426,7 +426,7 @@ var RichTextLayout = /*#__PURE__*/ function() {
426
426
  // TODO: width 和 height 新版富文本计算没有地方用到
427
427
  this.width = 0;
428
428
  this.height = 0;
429
- var size = options.size, _options_textOverflow = options.textOverflow, textOverflow = _options_textOverflow === void 0 ? EFFECTS.spec.TextOverflow.clip : _options_textOverflow, _options_textVerticalAlign = options.textVerticalAlign, textVerticalAlign = _options_textVerticalAlign === void 0 ? EFFECTS.spec.TextVerticalAlign.middle : _options_textVerticalAlign, _options_textAlign = options.textAlign, textAlign = _options_textAlign === void 0 ? EFFECTS.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 ? EFFECTS.spec.TextSizeMode.autoWidth : _options_sizeMode, _options_useLegacyRichText = options.// @ts-expect-error 兼容旧版
429
+ var size = options.size, _options_textOverflow = options.textOverflow, textOverflow = _options_textOverflow === void 0 ? EFFECTS.spec.TextOverflow.clip : _options_textOverflow, _options_textVerticalAlign = options.textVerticalAlign, textVerticalAlign = _options_textVerticalAlign === void 0 ? EFFECTS.spec.TextVerticalAlign.middle : _options_textVerticalAlign, _options_textAlign = options.textAlign, textAlign = _options_textAlign === void 0 ? EFFECTS.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 ? EFFECTS.spec.TextSizeMode.autoWidth : _options_autoResize, _options_useLegacyRichText = options.// @ts-expect-error 兼容旧版
430
430
  useLegacyRichText, useLegacyRichText = _options_useLegacyRichText === void 0 ? false : _options_useLegacyRichText;
431
431
  this.letterSpace = letterSpace;
432
432
  this.lineHeight = lineHeight;
@@ -439,7 +439,7 @@ var RichTextLayout = /*#__PURE__*/ function() {
439
439
  this.wrapEnabled = wrapEnabled;
440
440
  this.maxTextWidth = maxTextWidth;
441
441
  this.maxTextHeight = maxTextHeight;
442
- this.sizeMode = sizeMode;
442
+ this.autoResize = autoResize;
443
443
  }
444
444
  var _proto = RichTextLayout.prototype;
445
445
  _proto.getOffsetY = function getOffsetY(style, lineCount, lineHeight, fontSize, totalLineHeight) {
@@ -520,7 +520,7 @@ var RichTextLayout = /*#__PURE__*/ function() {
520
520
  */ _proto.getOffsetXRich = function getOffsetXRich(style, maxWidth, contentW) {
521
521
  switch(this.textAlign){
522
522
  case EFFECTS.spec.TextAlignment.left:
523
- return style.outlineWidth * style.fontScale;
523
+ return style.outlineWidth;
524
524
  case EFFECTS.spec.TextAlignment.middle:
525
525
  return (maxWidth - contentW) / 2;
526
526
  case EFFECTS.spec.TextAlignment.right:
@@ -619,11 +619,11 @@ var RichTextLayout = /*#__PURE__*/ function() {
619
619
  */ var RichWrapDisabledStrategy = /*#__PURE__*/ function() {
620
620
  function RichWrapDisabledStrategy() {}
621
621
  var _proto = RichWrapDisabledStrategy.prototype;
622
- _proto.computeLines = function computeLines(processedOptions, context, style, layout, singleLineHeight, fontScale, letterSpace) {
622
+ _proto.computeLines = function computeLines(processedOptions, context, style, layout, letterSpace) {
623
623
  var _this = this;
624
624
  var lines = [];
625
625
  var baselines = [];
626
- var gapPx = (layout.lineHeight || 0) * fontScale;
626
+ var gapPx = layout.lineHeight || 0;
627
627
  var scaleFactor = 1 / 10; // 1/10px, 后面 context.font 设置的字号为10px
628
628
  var currentLine = this.createNewLine();
629
629
  var maxLineWidth = 0;
@@ -632,8 +632,8 @@ var RichTextLayout = /*#__PURE__*/ function() {
632
632
  if (currentLine.chars.length === 0) {
633
633
  return;
634
634
  }
635
- // 第1行用真实首行高度,其余行用 gapPx
636
- currentLine.lineHeight = lines.length === 0 ? (currentLine.lineAscent || 0) + (currentLine.lineDescent || 0) : gapPx;
635
+ // 所有行都使用配置的行高(gapPx
636
+ currentLine.lineHeight = gapPx;
637
637
  // 记录本行基线
638
638
  var baseline = lines.length === 0 ? 0 : baselines[baselines.length - 1] + gapPx;
639
639
  baselines.push(baseline);
@@ -653,22 +653,20 @@ var RichTextLayout = /*#__PURE__*/ function() {
653
653
  context.font = fontStyle + " " + (options.fontWeight || style.textWeight) + " 10px " + (options.fontFamily || style.fontFamily);
654
654
  // 记录段起始 x
655
655
  currentLine.offsetX.push(currentLine.width);
656
- // 逐字宽度和高度测量(asc/desc 测量)
656
+ // 预计算缩放因子:measureText 基于 10px,乘 fontSize/10 得到逻辑像素
657
+ var glyphScale = fontSize * scaleFactor;
658
+ // 使用字体级别度量(不随具体字符变化),保证基线位置稳定
659
+ var refMetrics = context.measureText("x");
660
+ var fontAsc = refMetrics.fontBoundingBoxAscent * glyphScale;
661
+ var fontDesc = refMetrics.fontBoundingBoxDescent * glyphScale;
662
+ // 逐字宽度测量
657
663
  var segmentInnerX = 0;
658
664
  var charArr = [];
659
- var lineAscent = 0;
660
- var lineDescent = 0;
661
665
  for(var i = 0; i < text.length; i++){
662
666
  var ch = text[i];
663
667
  var m = context.measureText(ch);
664
668
  var w = m.width;
665
- var charWidth = (w <= 0 ? 0 : w) * fontSize * scaleFactor * fontScale;
666
- // 测量 asc/desc 并按目标字号缩放
667
- var scale = fontSize * fontScale * scaleFactor;
668
- var asc = m.actualBoundingBoxAscent * scale;
669
- var desc = m.actualBoundingBoxDescent * scale;
670
- lineAscent = Math.max(lineAscent, asc);
671
- lineDescent = Math.max(lineDescent, desc);
669
+ var charWidth = (w <= 0 ? 0 : w) * glyphScale;
672
670
  if (i > 0) {
673
671
  segmentInnerX += letterSpace; // 先加"前一个字符与当前字符之间"的间距
674
672
  }
@@ -681,13 +679,13 @@ var RichTextLayout = /*#__PURE__*/ function() {
681
679
  currentLine.chars.push(charArr);
682
680
  currentLine.width += segmentInnerX;
683
681
  currentLine.richOptions.push(options);
684
- // 累计行级 asc/desc
685
- currentLine.lineAscent = Math.max(currentLine.lineAscent || 0, lineAscent);
686
- currentLine.lineDescent = Math.max(currentLine.lineDescent || 0, lineDescent);
682
+ // 累计行级 asc/desc(字体级别,不因字符形状改变)
683
+ currentLine.lineAscent = Math.max(currentLine.lineAscent || 0, fontAsc);
684
+ currentLine.lineDescent = Math.max(currentLine.lineDescent || 0, fontDesc);
687
685
  });
688
686
  // 结束最后一行
689
687
  finishCurrentLine();
690
- // 计算 bbox
688
+ // 计算 bbox(包含行高带来的上下边距)
691
689
  var bboxTop = Infinity;
692
690
  var bboxBottom = -Infinity;
693
691
  if (lines.length === 0) {
@@ -701,8 +699,12 @@ var RichTextLayout = /*#__PURE__*/ function() {
701
699
  };
702
700
  }
703
701
  for(var i = 0; i < lines.length; i++){
704
- bboxTop = Math.min(bboxTop, baselines[i] - (lines[i].lineAscent || 0));
705
- bboxBottom = Math.max(bboxBottom, baselines[i] + (lines[i].lineDescent || 0));
702
+ var asc = lines[i].lineAscent || 0;
703
+ var desc = lines[i].lineDescent || 0;
704
+ var textHeight = asc + desc;
705
+ var margin = (gapPx - textHeight) / 2;
706
+ bboxTop = Math.min(bboxTop, baselines[i] - asc - margin);
707
+ bboxBottom = Math.max(bboxBottom, baselines[i] + desc + margin);
706
708
  }
707
709
  var bboxHeight = bboxBottom - bboxTop;
708
710
  return {
@@ -737,12 +739,14 @@ var RichTextLayout = /*#__PURE__*/ function() {
737
739
  */ var RichWrapEnabledStrategy = /*#__PURE__*/ function() {
738
740
  function RichWrapEnabledStrategy() {}
739
741
  var _proto = RichWrapEnabledStrategy.prototype;
740
- _proto.computeLines = function computeLines(processedOptions, context, style, layout, singleLineHeight, fontScale, letterSpace) {
742
+ _proto.computeLines = function computeLines(processedOptions, context, style, layout, letterSpace) {
741
743
  var _this = this;
742
744
  var lines = [];
743
745
  var baselines = [];
744
- var gapPx = (layout.lineHeight || 0) * fontScale;
746
+ var gapPx = layout.lineHeight || 0;
745
747
  var scaleFactor = 1 / 10; // 1/10px, 后面 context.font 设置的字号为10px
748
+ // 换行阈值,预计算避免每字符重复读取
749
+ var wrapWidth = layout.maxTextWidth || Infinity;
746
750
  var currentLine = this.createNewLine();
747
751
  var maxLineWidth = 0;
748
752
  var totalHeight = 0;
@@ -757,8 +761,8 @@ var RichTextLayout = /*#__PURE__*/ function() {
757
761
  if (currentLine.chars.length === 0) {
758
762
  return;
759
763
  }
760
- // 第1行用真实首行高度,其余行用 gapPx
761
- currentLine.lineHeight = lines.length === 0 ? (currentLine.lineAscent || 0) + (currentLine.lineDescent || 0) : gapPx;
764
+ // 所有行都使用配置的行高(gapPx
765
+ currentLine.lineHeight = gapPx;
762
766
  // 记录本行基线
763
767
  var baseline = lines.length === 0 ? 0 : baselines[baselines.length - 1] + gapPx;
764
768
  baselines.push(baseline);
@@ -800,22 +804,24 @@ var RichTextLayout = /*#__PURE__*/ function() {
800
804
  flushChunk();
801
805
  finishCurrentLine();
802
806
  }
807
+ // 预计算缩放因子:measureText 基于 10px,乘 fontSize/10 得到逻辑像素
808
+ var glyphScale = fontSize * scaleFactor;
809
+ // 使用字体级别度量(不随具体字符变化),保证基线位置稳定
810
+ var refMetrics = context.measureText("x");
811
+ var fontAsc = refMetrics.fontBoundingBoxAscent * glyphScale;
812
+ var fontDesc = refMetrics.fontBoundingBoxDescent * glyphScale;
803
813
  // 逐字符处理
804
814
  for(var i = 0; i < text.length; i++){
805
815
  var ch = text[i];
806
816
  // 获取基础宽度并计算实际宽度(动态缩放)
807
817
  var m = context.measureText(ch);
808
818
  var baseW = m.width;
809
- var charWidth = (baseW <= 0 ? 0 : baseW) * fontSize * scaleFactor * fontScale;
810
- // 测量 asc/desc 并按目标字号缩放
811
- var scale = fontSize * fontScale * scaleFactor;
812
- var asc = m.actualBoundingBoxAscent * scale;
813
- var desc = m.actualBoundingBoxDescent * scale;
819
+ var charWidth = (baseW <= 0 ? 0 : baseW) * glyphScale;
814
820
  // 计算预期宽度(包含字符间距)
815
821
  var spacing = chunkChars.length > 0 ? letterSpace : 0;
816
822
  var willWidth = currentLine.width + spacing + charWidth;
817
823
  // 自动换行判断
818
- if (willWidth > (layout.maxTextWidth * fontScale || Infinity)) {
824
+ if (willWidth > wrapWidth) {
819
825
  flushChunk();
820
826
  finishCurrentLine();
821
827
  }
@@ -834,9 +840,9 @@ var RichTextLayout = /*#__PURE__*/ function() {
834
840
  char: ch,
835
841
  x: segmentInnerX
836
842
  });
837
- // 累计行级 asc/desc
838
- currentLine.lineAscent = Math.max(currentLine.lineAscent || 0, asc);
839
- currentLine.lineDescent = Math.max(currentLine.lineDescent || 0, desc);
843
+ // 累计行级 asc/desc(字体级别,不因字符形状改变)
844
+ currentLine.lineAscent = Math.max(currentLine.lineAscent || 0, fontAsc);
845
+ currentLine.lineDescent = Math.max(currentLine.lineDescent || 0, fontDesc);
840
846
  // 更新位置和宽度
841
847
  segmentInnerX += charWidth;
842
848
  currentLine.width += charWidth;
@@ -846,12 +852,26 @@ var RichTextLayout = /*#__PURE__*/ function() {
846
852
  });
847
853
  // 结束最后一行
848
854
  finishCurrentLine();
849
- // 计算 bbox
855
+ // 计算 bbox(包含行高带来的上下边距)
850
856
  var bboxTop = Infinity;
851
857
  var bboxBottom = -Infinity;
858
+ if (lines.length === 0) {
859
+ return {
860
+ lines: lines,
861
+ maxLineWidth: maxLineWidth,
862
+ totalHeight: totalHeight,
863
+ bboxTop: 0,
864
+ bboxBottom: 0,
865
+ bboxHeight: 0
866
+ };
867
+ }
852
868
  for(var i = 0; i < lines.length; i++){
853
- bboxTop = Math.min(bboxTop, baselines[i] - (lines[i].lineAscent || 0));
854
- bboxBottom = Math.max(bboxBottom, baselines[i] + (lines[i].lineDescent || 0));
869
+ var asc = lines[i].lineAscent || 0;
870
+ var desc = lines[i].lineDescent || 0;
871
+ var textHeight = asc + desc;
872
+ var margin = (gapPx - textHeight) / 2;
873
+ bboxTop = Math.min(bboxTop, baselines[i] - asc - margin);
874
+ bboxBottom = Math.max(bboxBottom, baselines[i] + desc + margin);
855
875
  }
856
876
  var bboxHeight = bboxBottom - bboxTop;
857
877
  return {
@@ -880,117 +900,117 @@ var RichTextLayout = /*#__PURE__*/ function() {
880
900
  return RichWrapEnabledStrategy;
881
901
  }();
882
902
 
883
- /**
884
- * 富文本 Display 溢出策略
885
- * 在溢出策略中对所有几何量进行一次性缩放
886
- */ var RichDisplayOverflowStrategy = /*#__PURE__*/ function() {
887
- function RichDisplayOverflowStrategy() {}
888
- var _proto = RichDisplayOverflowStrategy.prototype;
889
- _proto.apply = function apply(lines, sizeResult, layout, style) {
890
- // 安全处理除零情况
891
- var safeDiv = function(a, b) {
892
- return Math.abs(b) > 1e-5 ? a / b : 1;
893
- };
894
- // 用像素空间计算
895
- var frameWpx = layout.maxTextWidth * style.fontScale;
896
- var frameHpx = layout.maxTextHeight * style.fontScale;
897
- var _sizeResult_contentWidth;
898
- var contentWpx = Math.max(1, (_sizeResult_contentWidth = sizeResult.contentWidth) != null ? _sizeResult_contentWidth : sizeResult.canvasWidth);
899
- var _sizeResult_bboxHeight;
900
- var contentHpx = Math.max(1, (_sizeResult_bboxHeight = sizeResult.bboxHeight) != null ? _sizeResult_bboxHeight : sizeResult.canvasHeight);
901
- // 只缩小不放大(基于像素空间计算)
902
- var s = Math.min(1, safeDiv(frameWpx, contentWpx), safeDiv(frameHpx, contentHpx));
903
- // 浮点精度处理
904
- if (s > 0.9999) {
905
- return {
906
- globalScale: 1
907
- };
908
- }
909
- // 统一缩放所有几何量
910
- for(var _iterator = _create_for_of_iterator_helper_loose(lines), _step; !(_step = _iterator()).done;){
911
- var line = _step.value;
912
- line.width *= s;
913
- line.lineHeight *= s; // 缩小行距(你的 gap-only 模式行高=gapPx)
914
- if (line.offsetX) {
915
- for(var i = 0; i < line.offsetX.length; i++){
916
- line.offsetX[i] *= s;
917
- }
918
- }
919
- for(var _iterator1 = _create_for_of_iterator_helper_loose(line.chars || []), _step1; !(_step1 = _iterator1()).done;){
920
- var seg = _step1.value;
921
- for(var _iterator2 = _create_for_of_iterator_helper_loose(seg), _step2; !(_step2 = _iterator2()).done;){
922
- var ch = _step2.value;
923
- ch.x *= s;
924
- }
925
- }
926
- for(var _iterator3 = _create_for_of_iterator_helper_loose(line.richOptions || []), _step3; !(_step3 = _iterator3()).done;){
927
- var opt = _step3.value;
928
- opt.fontSize *= s; // 缩小字形
929
- }
930
- // 同步缩放 asc/desc
931
- if (line.lineAscent != null) {
932
- line.lineAscent *= s;
933
- }
934
- if (line.lineDescent != null) {
935
- line.lineDescent *= s;
936
- }
937
- }
938
- return {
939
- globalScale: s
940
- };
941
- };
942
- return RichDisplayOverflowStrategy;
943
- }();
944
-
945
903
  /**
946
904
  * Clip 溢出策略
947
- * 超出画布部分自然裁切
905
+ * 画布尺寸固定为帧尺寸,超出部分自然裁切
906
+ * 不依赖对齐模式
948
907
  */ var RichClippedOverflowStrategy = /*#__PURE__*/ function() {
949
908
  function RichClippedOverflowStrategy() {}
950
909
  var _proto = RichClippedOverflowStrategy.prototype;
951
- _proto.apply = function apply(lines, sizeResult, layout, style) {
952
- // 不进行任何缩放,直接返回单位缩放系数
910
+ _proto.resolveCanvas = function resolveCanvas(lines, frameWidth, frameHeight, horizontalResult, verticalResult) {
953
911
  return {
954
- globalScale: 1
912
+ canvasWidth: Math.max(1, Math.ceil(frameWidth)),
913
+ canvasHeight: Math.max(1, Math.ceil(frameHeight)),
914
+ renderOffsetX: 0,
915
+ renderOffsetY: 0
955
916
  };
956
917
  };
957
918
  return RichClippedOverflowStrategy;
958
919
  }();
959
920
 
960
921
  /**
961
- * Visible 溢出策略
962
- * 允许内容超出画布可见
963
- */ var RichVisibleOverflowStrategy = /*#__PURE__*/ function() {
964
- function RichVisibleOverflowStrategy() {}
965
- var _proto = RichVisibleOverflowStrategy.prototype;
966
- _proto.apply = function apply(lines, sizeResult, layout, style) {
967
- // 不进行任何缩放或裁剪
922
+ * 扩展画布溢出策略(visible / display 模式共用)
923
+ *
924
+ * 根据对齐后的内容位置检测溢出,对称扩展画布以容纳所有内容。
925
+ * 不依赖对齐模式(textAlign / textVerticalAlign)的枚举值,
926
+ * 仅使用对齐策略输出的位置和行的度量数据。
927
+ *
928
+ * 对称扩展的原因:元素定位以中心点为锚点,
929
+ * 单侧扩展会导致视觉偏移。使用 max(溢出左, 溢出右) 作为双侧扩展量
930
+ * 可以保持中心点稳定并兼容所有对齐模式。
931
+ */ var RichExpandingOverflowStrategy = /*#__PURE__*/ function() {
932
+ function RichExpandingOverflowStrategy() {}
933
+ var _proto = RichExpandingOverflowStrategy.prototype;
934
+ _proto.resolveCanvas = function resolveCanvas(lines, frameWidth, frameHeight, horizontalResult, verticalResult) {
935
+ if (lines.length === 0) {
936
+ return {
937
+ canvasWidth: Math.max(1, frameWidth),
938
+ canvasHeight: Math.max(1, frameHeight),
939
+ renderOffsetX: 0,
940
+ renderOffsetY: 0
941
+ };
942
+ }
943
+ // ── 垂直溢出检测 ──
944
+ // 遍历每行基线,结合 ascent/descent 找出实际渲染范围
945
+ var contentTop = Infinity;
946
+ var contentBottom = -Infinity;
947
+ var currentY = verticalResult.baselineY;
948
+ for(var i = 0; i < lines.length; i++){
949
+ var _lines_i_lineAscent;
950
+ var asc = (_lines_i_lineAscent = lines[i].lineAscent) != null ? _lines_i_lineAscent : 0;
951
+ var _lines_i_lineDescent;
952
+ var desc = (_lines_i_lineDescent = lines[i].lineDescent) != null ? _lines_i_lineDescent : 0;
953
+ // 当 lineHeight >= textHeight 时,margin 为正,需要把行高边距纳入内容范围;
954
+ // 当 lineHeight < textHeight 时,margin 为负,此时字形超出行高框,
955
+ // 必须用原始字形边界(margin=0),否则负 margin 会把边界向内收缩,
956
+ // 导致检测不到溢出,最终首行顶部/末行底部被裁切。
957
+ var textHeight = asc + desc;
958
+ var margin = Math.max(0, (lines[i].lineHeight - textHeight) / 2);
959
+ contentTop = Math.min(contentTop, currentY - asc - margin);
960
+ contentBottom = Math.max(contentBottom, currentY + desc + margin);
961
+ if (i < lines.length - 1) {
962
+ currentY += lines[i + 1].lineHeight;
963
+ }
964
+ }
965
+ var overflowTop = Math.max(0, -contentTop);
966
+ var overflowBottom = Math.max(0, contentBottom - frameHeight);
967
+ // 对称扩展(保持中心点稳定)
968
+ var expandV = Math.max(overflowTop, overflowBottom);
969
+ // ── 水平溢出检测 ──
970
+ var contentLeft = Infinity;
971
+ var contentRight = -Infinity;
972
+ for(var i1 = 0; i1 < lines.length; i1++){
973
+ var _horizontalResult_lineOffsets_i;
974
+ var xOff = (_horizontalResult_lineOffsets_i = horizontalResult.lineOffsets[i1]) != null ? _horizontalResult_lineOffsets_i : 0;
975
+ var _lines_i_width;
976
+ var w = (_lines_i_width = lines[i1].width) != null ? _lines_i_width : 0;
977
+ contentLeft = Math.min(contentLeft, xOff);
978
+ contentRight = Math.max(contentRight, xOff + w);
979
+ }
980
+ if (!isFinite(contentLeft)) {
981
+ contentLeft = 0;
982
+ }
983
+ if (!isFinite(contentRight)) {
984
+ contentRight = 0;
985
+ }
986
+ var overflowLeft = Math.max(0, -contentLeft);
987
+ var overflowRight = Math.max(0, contentRight - frameWidth);
988
+ // 对称扩展
989
+ var expandH = Math.max(overflowLeft, overflowRight);
990
+ // ── 最终画布尺寸 ──
991
+ var canvasWidth = Math.ceil(frameWidth + expandH * 2);
992
+ var canvasHeight = Math.ceil(frameHeight + expandV * 2);
968
993
  return {
969
- globalScale: 1
994
+ canvasWidth: Math.max(1, canvasWidth),
995
+ canvasHeight: Math.max(1, canvasHeight),
996
+ renderOffsetX: expandH,
997
+ renderOffsetY: expandV
970
998
  };
971
999
  };
972
- return RichVisibleOverflowStrategy;
1000
+ return RichExpandingOverflowStrategy;
973
1001
  }();
974
1002
 
975
1003
  /**
976
1004
  * 富文本水平对齐策略
977
- * 直接使用已缩放的行宽计算偏移量
1005
+ * 在帧坐标系中计算每行的水平偏移,不依赖溢出模式
978
1006
  */ var RichHorizontalAlignStrategyImpl = /*#__PURE__*/ function() {
979
1007
  function RichHorizontalAlignStrategyImpl() {}
980
1008
  var _proto = RichHorizontalAlignStrategyImpl.prototype;
981
- _proto.getHorizontalOffsets = function getHorizontalOffsets(lines, sizeResult, overflowResult, layout, style) {
982
- var _sizeResult_containerWidth;
983
- var containerWidthPx = (_sizeResult_containerWidth = sizeResult.containerWidth) != null ? _sizeResult_containerWidth : layout.maxTextWidth * style.fontScale;
984
- var _sizeResult_baselineCompensationX;
985
- var compX = (_sizeResult_baselineCompensationX = sizeResult.baselineCompensationX) != null ? _sizeResult_baselineCompensationX : 0;
986
- // 使用像素单位的容器宽度
987
- var baseOffsets = lines.map(function(line) {
988
- return layout.getOffsetXRich(style, containerWidthPx, line.width);
1009
+ _proto.getHorizontalOffsets = function getHorizontalOffsets(lines, frameWidth, layout, style) {
1010
+ // 在帧坐标系 [0, frameWidth] 中计算对齐偏移
1011
+ var lineOffsets = lines.map(function(line) {
1012
+ return layout.getOffsetXRich(style, frameWidth, line.width);
989
1013
  });
990
- // visible 模式下使用 baseOffset + baselineCompensationX
991
- var lineOffsets = layout.overflow === EFFECTS.spec.TextOverflow.visible ? baseOffsets.map(function(x) {
992
- return x + compX;
993
- }) : baseOffsets;
994
1014
  return {
995
1015
  lineOffsets: lineOffsets
996
1016
  };
@@ -1000,147 +1020,63 @@ var RichTextLayout = /*#__PURE__*/ function() {
1000
1020
 
1001
1021
  /**
1002
1022
  * 富文本垂直对齐策略
1003
- * 直接使用已缩放的行高
1023
+ * 在帧坐标系中计算基线位置,不依赖溢出模式
1024
+ *
1025
+ * 算法:
1026
+ * 1. 从行数据重建各行基线(baseline[0]=0, baseline[i]=baseline[i-1]+lineHeight[i])
1027
+ * 2. 计算 bbox(包含行高边距)
1028
+ * 3. 根据 textVerticalAlign 将内容定位到帧中
1004
1029
  */ var RichVerticalAlignStrategyImpl = /*#__PURE__*/ function() {
1005
1030
  function RichVerticalAlignStrategyImpl() {}
1006
1031
  var _proto = RichVerticalAlignStrategyImpl.prototype;
1007
- _proto.getVerticalOffsets = function getVerticalOffsets(lines, sizeResult, overflowResult, layout, style, singleLineHeight) {
1008
- var _sizeResult_baselineCompensationY;
1009
- var compY = (_sizeResult_baselineCompensationY = sizeResult.baselineCompensationY) != null ? _sizeResult_baselineCompensationY : 0;
1010
- var baselineY = 0;
1032
+ _proto.getVerticalOffsets = function getVerticalOffsets(lines, frameHeight, layout) {
1011
1033
  if (lines.length === 0) {
1012
1034
  return {
1013
1035
  baselineY: 0,
1014
1036
  lineYOffsets: []
1015
1037
  };
1016
1038
  }
1017
- switch(layout.overflow){
1018
- case EFFECTS.spec.TextOverflow.visible:
1019
- {
1020
- // frame-based 计算
1021
- var frameHpx = layout.maxTextHeight * style.fontScale;
1022
- var _sizeResult_bboxTop;
1023
- var bboxTop = (_sizeResult_bboxTop = sizeResult.bboxTop) != null ? _sizeResult_bboxTop : 0;
1024
- var _sizeResult_bboxHeight, _sizeResult_bboxBottom;
1025
- var bboxBottom = (_sizeResult_bboxBottom = sizeResult.bboxBottom) != null ? _sizeResult_bboxBottom : bboxTop + ((_sizeResult_bboxHeight = sizeResult.bboxHeight) != null ? _sizeResult_bboxHeight : 0);
1026
- var _sizeResult_bboxHeight1;
1027
- var bboxHeight = (_sizeResult_bboxHeight1 = sizeResult.bboxHeight) != null ? _sizeResult_bboxHeight1 : bboxBottom - bboxTop;
1028
- // 计算 frame 基线
1029
- var baselineYFrame = 0;
1030
- switch(layout.textVerticalAlign){
1031
- case EFFECTS.spec.TextVerticalAlign.top:
1032
- baselineYFrame = -bboxTop;
1033
- break;
1034
- case EFFECTS.spec.TextVerticalAlign.middle:
1035
- baselineYFrame = (frameHpx - bboxHeight) / 2 - bboxTop;
1036
- break;
1037
- case EFFECTS.spec.TextVerticalAlign.bottom:
1038
- baselineYFrame = frameHpx - bboxHeight - bboxTop;
1039
- break;
1040
- }
1041
- baselineY = baselineYFrame + compY; // 关键:叠加"向下移动 E"
1042
- break;
1043
- }
1044
- case EFFECTS.spec.TextOverflow.clip:
1045
- {
1046
- // 让 clip 垂直对齐逻辑与 display 保持一致(基于 bbox 的几何模型)
1047
- var frameHpx1 = layout.maxTextHeight * style.fontScale;
1048
- // 用缩放后的行数据重建 baselines 和 bbox
1049
- var baselines = [
1050
- 0
1051
- ];
1052
- for(var i = 1; i < lines.length; i++){
1053
- baselines[i] = baselines[i - 1] + lines[i].lineHeight;
1054
- }
1055
- var bboxTop1 = Infinity;
1056
- var bboxBottom1 = -Infinity;
1057
- for(var i1 = 0; i1 < lines.length; i1++){
1058
- var _lines_i_lineAscent;
1059
- var asc = (_lines_i_lineAscent = lines[i1].lineAscent) != null ? _lines_i_lineAscent : 0;
1060
- var _lines_i_lineDescent;
1061
- var desc = (_lines_i_lineDescent = lines[i1].lineDescent) != null ? _lines_i_lineDescent : 0;
1062
- bboxTop1 = Math.min(bboxTop1, baselines[i1] - asc);
1063
- bboxBottom1 = Math.max(bboxBottom1, baselines[i1] + desc);
1064
- }
1065
- var bboxHeight1 = bboxBottom1 - bboxTop1;
1066
- var baselineClipY = 0;
1067
- switch(layout.textVerticalAlign){
1068
- case EFFECTS.spec.TextVerticalAlign.top:
1069
- baselineClipY = -bboxTop1;
1070
- break;
1071
- case EFFECTS.spec.TextVerticalAlign.middle:
1072
- baselineClipY = (frameHpx1 - bboxHeight1) / 2 - bboxTop1;
1073
- break;
1074
- case EFFECTS.spec.TextVerticalAlign.bottom:
1075
- baselineClipY = frameHpx1 - bboxHeight1 - bboxTop1;
1076
- break;
1077
- }
1078
- baselineY = baselineClipY + compY;
1079
- break;
1080
- }
1081
- case EFFECTS.spec.TextOverflow.display:
1082
- {
1083
- // display 垂直对齐改为基于 bbox,而不是 getOffsetYRich
1084
- var frameHpx2 = layout.maxTextHeight * style.fontScale;
1085
- // 用缩放后的行数据重建 baselines 和 bbox
1086
- var baselines1 = [
1087
- 0
1088
- ];
1089
- for(var i2 = 1; i2 < lines.length; i2++){
1090
- baselines1[i2] = baselines1[i2 - 1] + lines[i2].lineHeight;
1091
- }
1092
- // 计算 bboxTop / bboxBottom(使用行的测量值 lineAscent/lineDescent)
1093
- var bboxTop2 = Infinity;
1094
- var bboxBottom2 = -Infinity;
1095
- for(var i3 = 0; i3 < lines.length; i3++){
1096
- var _lines_i_lineAscent1;
1097
- var asc1 = (_lines_i_lineAscent1 = lines[i3].lineAscent) != null ? _lines_i_lineAscent1 : 0;
1098
- var _lines_i_lineDescent1;
1099
- var desc1 = (_lines_i_lineDescent1 = lines[i3].lineDescent) != null ? _lines_i_lineDescent1 : 0;
1100
- bboxTop2 = Math.min(bboxTop2, baselines1[i3] - asc1);
1101
- bboxBottom2 = Math.max(bboxBottom2, baselines1[i3] + desc1);
1102
- }
1103
- var bboxHeight2 = bboxBottom2 - bboxTop2;
1104
- // 计算 display 模式的基线
1105
- var baselineDisplayY = 0;
1106
- switch(layout.textVerticalAlign){
1107
- case EFFECTS.spec.TextVerticalAlign.top:
1108
- baselineDisplayY = -bboxTop2;
1109
- break;
1110
- case EFFECTS.spec.TextVerticalAlign.middle:
1111
- baselineDisplayY = (frameHpx2 - bboxHeight2) / 2 - bboxTop2;
1112
- break;
1113
- case EFFECTS.spec.TextVerticalAlign.bottom:
1114
- baselineDisplayY = frameHpx2 - bboxHeight2 - bboxTop2;
1115
- break;
1116
- }
1117
- // 后续 lineYOffsets 保持按行高累计
1118
- var lineYOffsets = [];
1119
- var currentY = baselineDisplayY;
1120
- for(var i4 = 0; i4 < lines.length; i4++){
1121
- lineYOffsets.push(currentY);
1122
- if (i4 < lines.length - 1) {
1123
- currentY += lines[i4 + 1].lineHeight;
1124
- }
1125
- }
1126
- return {
1127
- baselineY: baselineDisplayY,
1128
- lineYOffsets: lineYOffsets
1129
- };
1130
- }
1039
+ // 1. 重建各行基线(相对于第一行基线=0)
1040
+ var baselines = [
1041
+ 0
1042
+ ];
1043
+ for(var i = 1; i < lines.length; i++){
1044
+ baselines[i] = baselines[i - 1] + lines[i].lineHeight;
1131
1045
  }
1132
- // 下面行偏移保持不变
1133
- var lineYOffsets1 = [];
1134
- var currentY1 = baselineY;
1135
- lines.forEach(function(line, index) {
1136
- lineYOffsets1.push(currentY1);
1137
- if (index < lines.length - 1) {
1138
- currentY1 += lines[index + 1].lineHeight;
1139
- }
1046
+ // 2. 计算内容 bbox(含行高边距)
1047
+ var bboxTop = Infinity;
1048
+ var bboxBottom = -Infinity;
1049
+ for(var i1 = 0; i1 < lines.length; i1++){
1050
+ var _lines_i_lineAscent;
1051
+ var asc = (_lines_i_lineAscent = lines[i1].lineAscent) != null ? _lines_i_lineAscent : 0;
1052
+ var _lines_i_lineDescent;
1053
+ var desc = (_lines_i_lineDescent = lines[i1].lineDescent) != null ? _lines_i_lineDescent : 0;
1054
+ var textHeight = asc + desc;
1055
+ var margin = (lines[i1].lineHeight - textHeight) / 2;
1056
+ bboxTop = Math.min(bboxTop, baselines[i1] - asc - margin);
1057
+ bboxBottom = Math.max(bboxBottom, baselines[i1] + desc + margin);
1058
+ }
1059
+ var bboxHeight = bboxBottom - bboxTop;
1060
+ // 3. 在帧坐标系 [0, frameHeight] 中定位第一行基线
1061
+ var baselineY = 0;
1062
+ switch(layout.textVerticalAlign){
1063
+ case EFFECTS.spec.TextVerticalAlign.top:
1064
+ baselineY = -bboxTop;
1065
+ break;
1066
+ case EFFECTS.spec.TextVerticalAlign.middle:
1067
+ baselineY = (frameHeight - bboxHeight) / 2 - bboxTop;
1068
+ break;
1069
+ case EFFECTS.spec.TextVerticalAlign.bottom:
1070
+ baselineY = frameHeight - bboxHeight - bboxTop;
1071
+ break;
1072
+ }
1073
+ // 4. 各行 Y 坐标
1074
+ var lineYOffsets = baselines.map(function(b) {
1075
+ return baselineY + b;
1140
1076
  });
1141
1077
  return {
1142
1078
  baselineY: baselineY,
1143
- lineYOffsets: lineYOffsets1
1079
+ lineYOffsets: lineYOffsets
1144
1080
  };
1145
1081
  };
1146
1082
  return RichVerticalAlignStrategyImpl;
@@ -1149,11 +1085,12 @@ var RichTextLayout = /*#__PURE__*/ function() {
1149
1085
  /**
1150
1086
  * 富文本策略工厂
1151
1087
  * 负责创建各种策略实例
1088
+ *
1089
+ * 管线顺序:Wrap → scaleLinesToFit(display)→ Align → Overflow.resolveCanvas
1152
1090
  */ var RichTextStrategyFactory = /*#__PURE__*/ function() {
1153
1091
  function RichTextStrategyFactory() {}
1154
1092
  /**
1155
1093
  * 创建换行策略
1156
- * 根据wrapEnabled参数决定使用哪种策略
1157
1094
  */ RichTextStrategyFactory.createWrapStrategy = function createWrapStrategy(wrapEnabled) {
1158
1095
  if (wrapEnabled) {
1159
1096
  return new RichWrapEnabledStrategy();
@@ -1162,17 +1099,17 @@ var RichTextLayout = /*#__PURE__*/ function() {
1162
1099
  }
1163
1100
  };
1164
1101
  /**
1165
- * 创建溢出策略
1166
- * 支持三种模式:clip(裁切)、display(行级缩放)、visible(不缩放不裁剪)
1102
+ * 创建溢出策略(画布解析)
1103
+ * clip:画布=帧,超出裁切
1104
+ * display/visible:检测溢出并对称扩展画布
1167
1105
  */ RichTextStrategyFactory.createOverflowStrategy = function createOverflowStrategy(mode) {
1168
1106
  switch(mode){
1169
1107
  case EFFECTS.spec.TextOverflow.clip:
1170
1108
  return new RichClippedOverflowStrategy();
1171
1109
  case EFFECTS.spec.TextOverflow.display:
1172
- return new RichDisplayOverflowStrategy();
1173
1110
  case EFFECTS.spec.TextOverflow.visible:
1174
1111
  default:
1175
- return new RichVisibleOverflowStrategy();
1112
+ return new RichExpandingOverflowStrategy();
1176
1113
  }
1177
1114
  };
1178
1115
  /**
@@ -1188,6 +1125,66 @@ var RichTextLayout = /*#__PURE__*/ function() {
1188
1125
  return RichTextStrategyFactory;
1189
1126
  }();
1190
1127
 
1128
+ function _extends() {
1129
+ _extends = Object.assign || function assign(target) {
1130
+ for(var i = 1; i < arguments.length; i++){
1131
+ var source = arguments[i];
1132
+ for(var key in source)if (Object.prototype.hasOwnProperty.call(source, key)) target[key] = source[key];
1133
+ }
1134
+ return target;
1135
+ };
1136
+ return _extends.apply(this, arguments);
1137
+ }
1138
+
1139
+ /**
1140
+ * 将行数据等比缩小以适配帧尺寸(display 模式使用)
1141
+ * 只缩小不放大,就地修改行数据
1142
+ */ function scaleLinesToFit(lines, contentWidth, contentHeight, frameWidth, frameHeight) {
1143
+ var safeDiv = function(a, b) {
1144
+ return Math.abs(b) > 1e-5 ? a / b : 1;
1145
+ };
1146
+ var safeContentW = Math.max(1, contentWidth);
1147
+ var safeContentH = Math.max(1, contentHeight);
1148
+ // 只缩小不放大
1149
+ var s = Math.min(1, safeDiv(frameWidth, safeContentW), safeDiv(frameHeight, safeContentH));
1150
+ // 浮点精度处理
1151
+ if (s > 0.9999) {
1152
+ return;
1153
+ }
1154
+ for(var _iterator = _create_for_of_iterator_helper_loose(lines), _step; !(_step = _iterator()).done;){
1155
+ var line = _step.value;
1156
+ line.width *= s;
1157
+ line.lineHeight *= s;
1158
+ if (line.offsetX) {
1159
+ for(var i = 0; i < line.offsetX.length; i++){
1160
+ line.offsetX[i] *= s;
1161
+ }
1162
+ }
1163
+ for(var _iterator1 = _create_for_of_iterator_helper_loose(line.chars || []), _step1; !(_step1 = _iterator1()).done;){
1164
+ var seg = _step1.value;
1165
+ for(var _iterator2 = _create_for_of_iterator_helper_loose(seg), _step2; !(_step2 = _iterator2()).done;){
1166
+ var ch = _step2.value;
1167
+ ch.x *= s;
1168
+ }
1169
+ }
1170
+ // 创建新的 richOptions 数组,避免修改原始对象
1171
+ var originalOptions = line.richOptions || [];
1172
+ var newOptions = [];
1173
+ for(var i1 = 0; i1 < originalOptions.length; i1++){
1174
+ newOptions.push(_extends({}, originalOptions[i1], {
1175
+ fontSize: originalOptions[i1].fontSize * s
1176
+ }));
1177
+ }
1178
+ line.richOptions = newOptions;
1179
+ if (line.lineAscent != null) {
1180
+ line.lineAscent *= s;
1181
+ }
1182
+ if (line.lineDescent != null) {
1183
+ line.lineDescent *= s;
1184
+ }
1185
+ }
1186
+ }
1187
+
1191
1188
  var seed = 0;
1192
1189
  exports.RichTextComponent = /*#__PURE__*/ function(MaskableGraphic) {
1193
1190
  _inherits(RichTextComponent, MaskableGraphic);
@@ -1197,10 +1194,10 @@ exports.RichTextComponent = /*#__PURE__*/ function(MaskableGraphic) {
1197
1194
  _this.isDirty = true;
1198
1195
  _this.text = "";
1199
1196
  _this.processedTextOptions = [];
1200
- _this.singleLineHeight = 1.571;
1201
- _this.size = null;
1202
- _this.initialized = false;
1203
- _this.canvasSize = null;
1197
+ /** @deprecated Use for legacy mode*/ _this.singleLineHeight = 1.571;
1198
+ /** @deprecated Use for legacy mode*/ _this.size = null;
1199
+ /** @deprecated Use for legacy mode*/ _this.initialized = false;
1200
+ /** @deprecated Use for legacy mode*/ _this.canvasSize = null;
1204
1201
  _this.SCALE_FACTOR = 0.11092565;
1205
1202
  _this.ALPHA_FIX_VALUE = 1 / 255;
1206
1203
  _this.name = "MRichText" + seed++;
@@ -1237,7 +1234,7 @@ exports.RichTextComponent = /*#__PURE__*/ function(MaskableGraphic) {
1237
1234
  this.material.setColor("_Color", new EFFECTS.math.Color(1, 1, 1, 1));
1238
1235
  };
1239
1236
  /**
1240
- * 使用策略结果绘制文本
1237
+ * 根据布局配置更新策略实例
1241
1238
  */ _proto.updateStrategies = function updateStrategies() {
1242
1239
  var layout = this.textLayout;
1243
1240
  if (layout) {
@@ -1437,11 +1434,20 @@ exports.RichTextComponent = /*#__PURE__*/ function(MaskableGraphic) {
1437
1434
  };
1438
1435
  /**
1439
1436
  * 使用策略管线路径的渲染方法
1437
+ *
1438
+ * 管线顺序:
1439
+ * 1. Wrap → 换行与度量
1440
+ * 2. SizeMode → 根据 autoResize 回写帧尺寸
1441
+ * 3. ContentScale → 内容缩放(仅 display 模式)
1442
+ * 4. Alignment → 在帧坐标系中对齐(不依赖 overflow)
1443
+ * 5. Overflow → 画布解析:确定最终画布尺寸与渲染偏移(不依赖对齐模式)
1444
+ * 6. Render → 绘制
1440
1445
  */ _proto.updateTextureWithStrategies = function updateTextureWithStrategies(flipY) {
1441
1446
  var _this = this;
1442
1447
  if (!this.isDirty || !this.context || !this.canvas) {
1443
1448
  return;
1444
1449
  }
1450
+ this.updateStrategies();
1445
1451
  // 解析富文本
1446
1452
  this.generateTextProgram(this.text);
1447
1453
  var layout = this.textLayout;
@@ -1450,227 +1456,84 @@ exports.RichTextComponent = /*#__PURE__*/ function(MaskableGraphic) {
1450
1456
  if (!context) {
1451
1457
  return;
1452
1458
  }
1453
- // 步骤1: 换行策略计算行信息
1454
- var wrapResult = this.richWrapStrategy.computeLines(this.processedTextOptions, context, this.textStyle, layout, this.singleLineHeight, this.textStyle.fontScale, letterSpace);
1455
- if (wrapResult.lines.length === 0 || wrapResult.maxLineWidth === 0 || wrapResult.totalHeight === 0) {
1456
- this.isDirty = false;
1457
- return;
1459
+ var fontScale = this.textStyle.fontScale;
1460
+ // autoWidth 模式下先去掉宽度约束,避免内容被提前换行
1461
+ if (layout.autoResize === EFFECTS.spec.TextSizeMode.autoWidth) {
1462
+ layout.maxTextWidth = Number.MAX_SAFE_INTEGER;
1458
1463
  }
1459
- // 步骤2: 尺寸处理
1460
- var sizeResult = this.resolveCanvasSize(wrapResult, layout, this.textStyle, this.singleLineHeight);
1461
- // 首次渲染时初始化 canvas 尺寸和组件变换
1462
- this.setCanvasSize(sizeResult);
1463
- // 步骤3: 溢出策略处理
1464
- var overflowResult = this.richOverflowStrategy.apply(wrapResult.lines, sizeResult, layout, this.textStyle);
1465
- // 步骤4: 水平对齐策略
1466
- var horizontalAlignResult = this.richHorizontalAlignStrategy.getHorizontalOffsets(wrapResult.lines, sizeResult, overflowResult, layout, this.textStyle);
1467
- // 步骤5: 垂直对齐策略
1468
- var TextVerticalAlignResult = this.richVerticalAlignStrategy.getVerticalOffsets(wrapResult.lines, sizeResult, overflowResult, layout, this.textStyle, this.singleLineHeight);
1469
- // 使用 this.canvasSize 统一设置画布尺寸
1470
- EFFECTS.assertExist(this.canvasSize);
1471
- var _this_canvasSize = this.canvasSize, canvasWidth = _this_canvasSize.x, canvasHeight = _this_canvasSize.y;
1472
- // 确保canvas宽高至少为1
1473
- var safeW = Math.max(1, Math.ceil(canvasWidth));
1474
- var safeH = Math.max(1, Math.ceil(canvasHeight));
1475
- layout.width = safeW / this.textStyle.fontScale;
1476
- layout.height = safeH / this.textStyle.fontScale;
1477
- this.renderToTexture(safeW, safeH, flipY, function(context) {
1478
- // 步骤6: 绘制文本
1479
- _this.drawTextWithStrategies(context, wrapResult.lines, horizontalAlignResult, TextVerticalAlignResult, overflowResult, _this.textStyle);
1464
+ // ── 步骤 1: 换行策略(逻辑坐标系,fontScale 不参与排版)──
1465
+ var wrapResult = this.richWrapStrategy.computeLines(this.processedTextOptions, context, this.textStyle, layout, letterSpace);
1466
+ // ── 步骤 2: SizeMode → 帧尺寸(逻辑单位)──
1467
+ // fontScale 不参与排版,全部在逻辑坐标系中完成。
1468
+ var frameW;
1469
+ var frameH;
1470
+ switch(layout.autoResize){
1471
+ case EFFECTS.spec.TextSizeMode.autoWidth:
1472
+ frameW = Math.max(1, wrapResult.maxLineWidth || 0);
1473
+ frameH = Math.max(1, wrapResult.totalHeight || 0);
1474
+ break;
1475
+ case EFFECTS.spec.TextSizeMode.autoHeight:
1476
+ frameW = layout.maxTextWidth;
1477
+ frameH = Math.max(1, wrapResult.totalHeight || 0);
1478
+ break;
1479
+ case EFFECTS.spec.TextSizeMode.fixed:
1480
+ default:
1481
+ frameW = layout.maxTextWidth;
1482
+ frameH = layout.maxTextHeight;
1483
+ break;
1484
+ }
1485
+ // ── 步骤 3: 内容缩放(display 模式缩小以适配帧,其他模式跳过)──
1486
+ if (layout.overflow === EFFECTS.spec.TextOverflow.display) {
1487
+ var contentW = Math.max(1, wrapResult.maxLineWidth || 0);
1488
+ var contentH = Math.max(1, wrapResult.totalHeight || 0);
1489
+ scaleLinesToFit(wrapResult.lines, contentW, contentH, frameW, frameH);
1490
+ }
1491
+ // ── 步骤 4: 对齐(在帧坐标系中,不依赖 overflow 模式)──
1492
+ var horizontalAlignResult = this.richHorizontalAlignStrategy.getHorizontalOffsets(wrapResult.lines, frameW, layout, this.textStyle);
1493
+ var verticalAlignResult = this.richVerticalAlignStrategy.getVerticalOffsets(wrapResult.lines, frameH, layout);
1494
+ // ── 步骤 5: 溢出 / 画布解析(不依赖对齐模式枚举)──
1495
+ var overflowResult = this.richOverflowStrategy.resolveCanvas(wrapResult.lines, frameW, frameH, horizontalAlignResult, verticalAlignResult);
1496
+ // 排版结果(逻辑单位)→ 物理像素画布
1497
+ var physicalW = Math.max(1, Math.ceil(overflowResult.canvasWidth * fontScale));
1498
+ var physicalH = Math.max(1, Math.ceil(overflowResult.canvasHeight * fontScale));
1499
+ // 渲染尺寸不随 fontScale 改变
1500
+ this.item.transform.size.set(overflowResult.canvasWidth * this.SCALE_FACTOR * this.SCALE_FACTOR, overflowResult.canvasHeight * this.SCALE_FACTOR * this.SCALE_FACTOR);
1501
+ // 统一回写布局属性(逻辑单位)
1502
+ layout.maxTextWidth = frameW;
1503
+ layout.maxTextHeight = frameH;
1504
+ layout.width = overflowResult.canvasWidth;
1505
+ layout.height = overflowResult.canvasHeight;
1506
+ this.renderToTexture(physicalW, physicalH, flipY, function(context) {
1507
+ // fontScale 仅作为渲染分辨率倍率,排版坐标全部为逻辑单位
1508
+ context.scale(fontScale, fontScale);
1509
+ // ── 步骤 6: 绘制 ──
1510
+ _this.drawTextWithStrategies(context, wrapResult.lines, horizontalAlignResult, verticalAlignResult, overflowResult, _this.textStyle);
1480
1511
  }, {
1481
1512
  disposeOld: false
1482
1513
  });
1483
1514
  this.isDirty = false;
1484
1515
  };
1485
- _proto.setCanvasSize = function setCanvasSize(sizeResult) {
1486
- var _this = this;
1487
- if (this.size === undefined || this.size === null) {
1488
- this.size = this.item.transform.size.clone();
1489
- }
1490
- 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;
1491
- var layout = this.textLayout;
1492
- var fontScale = this.textStyle.fontScale || 1;
1493
- // 防止 frameW / frameH 为 0
1494
- var frameW = Math.max(1, layout.maxTextWidth || 0);
1495
- var frameH = Math.max(1, layout.maxTextHeight || 0);
1496
- switch(layout.overflow){
1497
- case EFFECTS.spec.TextOverflow.visible:
1498
- {
1499
- var frameWpx = frameW * fontScale;
1500
- var frameHpx = frameH * fontScale;
1501
- var _sizeResult_bboxTop;
1502
- var bboxTop = (_sizeResult_bboxTop = sizeResult.bboxTop) != null ? _sizeResult_bboxTop : 0;
1503
- var _sizeResult_bboxHeight, _sizeResult_bboxBottom;
1504
- var bboxBottom = (_sizeResult_bboxBottom = sizeResult.bboxBottom) != null ? _sizeResult_bboxBottom : bboxTop + ((_sizeResult_bboxHeight = sizeResult.bboxHeight) != null ? _sizeResult_bboxHeight : 0);
1505
- var _sizeResult_bboxHeight1;
1506
- var bboxHeight = (_sizeResult_bboxHeight1 = sizeResult.bboxHeight) != null ? _sizeResult_bboxHeight1 : bboxBottom - bboxTop;
1507
- // 计算 frame 基线
1508
- var baselineYFrame = 0;
1509
- switch(layout.textVerticalAlign){
1510
- case EFFECTS.spec.TextVerticalAlign.top:
1511
- baselineYFrame = -bboxTop;
1512
- break;
1513
- case EFFECTS.spec.TextVerticalAlign.middle:
1514
- baselineYFrame = (frameHpx - bboxHeight) / 2 - bboxTop;
1515
- break;
1516
- case EFFECTS.spec.TextVerticalAlign.bottom:
1517
- baselineYFrame = frameHpx - bboxHeight - bboxTop;
1518
- break;
1519
- }
1520
- // 上下溢出检测
1521
- var contentTopInFrame = baselineYFrame + bboxTop;
1522
- var contentBottomInFrame = baselineYFrame + bboxBottom;
1523
- var overflowTop = Math.max(0, -contentTopInFrame);
1524
- var overflowBottom = Math.max(0, contentBottomInFrame - frameHpx);
1525
- // 垂直扩张
1526
- var expandTop = overflowTop;
1527
- var expandBottom = overflowBottom;
1528
- switch(layout.textVerticalAlign){
1529
- case EFFECTS.spec.TextVerticalAlign.top:
1530
- {
1531
- var E = overflowBottom;
1532
- expandTop = E;
1533
- expandBottom = E;
1534
- break;
1535
- }
1536
- case EFFECTS.spec.TextVerticalAlign.bottom:
1537
- {
1538
- var E1 = overflowTop;
1539
- expandTop = E1;
1540
- expandBottom = E1;
1541
- break;
1542
- }
1543
- case EFFECTS.spec.TextVerticalAlign.middle:
1544
- {
1545
- // 保持非对称:上扩 overflowTop,下扩 overflowBottom
1546
- expandTop = overflowTop;
1547
- expandBottom = overflowBottom;
1548
- break;
1549
- }
1550
- }
1551
- // 位移补偿:始终使用 expandTop
1552
- var compY = expandTop;
1553
- // 水平扩张
1554
- var lines = sizeResult.lines || [];
1555
- // 1. 先按 frameWpx 计算每行的对齐起点(逻辑对齐)
1556
- var xOffsetsFrame = lines.map(function(line) {
1557
- return layout.getOffsetXRich(_this.textStyle, frameWpx, line.width);
1558
- });
1559
- // 2. 用对齐后的偏移 + 行宽算出内容的左右边界
1560
- var contentMinX = Infinity;
1561
- var contentMaxX = -Infinity;
1562
- for(var i = 0; i < lines.length; i++){
1563
- var _xOffsetsFrame_i;
1564
- var off = (_xOffsetsFrame_i = xOffsetsFrame[i]) != null ? _xOffsetsFrame_i : 0;
1565
- var _lines_i_width;
1566
- var w = (_lines_i_width = lines[i].width) != null ? _lines_i_width : 0; // 像素宽
1567
- contentMinX = Math.min(contentMinX, off);
1568
- contentMaxX = Math.max(contentMaxX, off + w);
1569
- }
1570
- if (!isFinite(contentMinX)) {
1571
- contentMinX = 0;
1572
- }
1573
- if (!isFinite(contentMaxX)) {
1574
- contentMaxX = 0;
1575
- }
1576
- // 3. 计算内容相对于 frame 的越界量
1577
- var overflowLeft = Math.max(0, -contentMinX); // 内容左边 < 0
1578
- var overflowRight = Math.max(0, contentMaxX - frameWpx); // 内容右边 > frameWpx?
1579
- // 4. 让 canvas 左右都扩张到能容下整个内容 bbox
1580
- var expandLeft = overflowLeft;
1581
- var expandRight = overflowRight;
1582
- // 5. 最终 canvas 宽高
1583
- var finalWpx = Math.ceil(frameWpx + expandLeft + expandRight);
1584
- var finalHpx = Math.ceil(frameHpx + expandTop + expandBottom);
1585
- // 记录补偿,供垂直对齐策略叠加
1586
- sizeResult.baselineCompensationX = expandLeft;
1587
- sizeResult.baselineCompensationY = compY;
1588
- // containerWidth 用 frameWpx,而不是 finalWpx
1589
- sizeResult.containerWidth = frameWpx;
1590
- sizeResult.canvasWidth = finalWpx;
1591
- sizeResult.canvasHeight = finalHpx;
1592
- this.canvasSize = new EFFECTS.math.Vector2(finalWpx, finalHpx);
1593
- var _this_size1;
1594
- 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;
1595
- // 实际元素渲染尺寸不随着 fontScale 改变
1596
- this.item.transform.size.set(x1 * finalWpx / fontScale * this.SCALE_FACTOR * this.SCALE_FACTOR, y1 * finalHpx / fontScale * this.SCALE_FACTOR * this.SCALE_FACTOR);
1597
- this.initialized = true;
1598
- break;
1599
- }
1600
- case EFFECTS.spec.TextOverflow.clip:
1601
- {
1602
- var frameWpx1 = frameW * fontScale;
1603
- var frameHpx1 = frameH * fontScale;
1604
- // 直接使用 frame 尺寸作为画布尺寸
1605
- sizeResult.canvasWidth = frameWpx1;
1606
- sizeResult.canvasHeight = frameHpx1;
1607
- // clip 模式不需要任何补偿
1608
- sizeResult.baselineCompensationX = 0;
1609
- sizeResult.baselineCompensationY = 0;
1610
- // 设置 canvas 和节点变换
1611
- this.canvasSize = new EFFECTS.math.Vector2(frameWpx1, frameHpx1);
1612
- // 把 layout 的尺寸更新为 frame 尺寸
1613
- layout.width = frameW;
1614
- layout.height = frameH;
1615
- var _this_size2;
1616
- 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;
1617
- this.item.transform.size.set(x2 * frameWpx1 / fontScale * this.SCALE_FACTOR * this.SCALE_FACTOR, y2 * frameHpx1 / fontScale * this.SCALE_FACTOR * this.SCALE_FACTOR);
1618
- this.initialized = true;
1619
- break;
1620
- }
1621
- case EFFECTS.spec.TextOverflow.display:
1622
- {
1623
- if (!this.initialized) {
1624
- var frameWpx2 = frameW * fontScale;
1625
- var frameHpx2 = frameH * fontScale;
1626
- this.canvasSize = new EFFECTS.math.Vector2(frameWpx2, frameHpx2);
1627
- this.item.transform.size.set(x * frameWpx2 / fontScale * this.SCALE_FACTOR * this.SCALE_FACTOR, y * frameHpx2 / fontScale * this.SCALE_FACTOR * this.SCALE_FACTOR);
1628
- this.initialized = true;
1629
- }
1630
- break;
1631
- }
1632
- }
1633
- };
1634
- /**
1635
- * 尺寸处理
1636
- */ _proto.resolveCanvasSize = function resolveCanvasSize(wrapResult, layout, style, singleLineHeight) {
1637
- var canvasWidth = Math.max(1, wrapResult.maxLineWidth || 0);
1638
- var canvasHeight = Math.max(1, wrapResult.totalHeight || 0); // stepTotalHeight
1639
- layout.width = canvasWidth / style.fontScale;
1640
- layout.height = canvasHeight / style.fontScale;
1641
- return {
1642
- canvasWidth: canvasWidth,
1643
- canvasHeight: canvasHeight,
1644
- contentWidth: wrapResult.maxLineWidth,
1645
- bboxTop: wrapResult.bboxTop,
1646
- bboxBottom: wrapResult.bboxBottom,
1647
- bboxHeight: wrapResult.bboxHeight,
1648
- // 为 visible 模式的画布尺寸计算保留行信息
1649
- lines: wrapResult.lines
1650
- };
1651
- };
1652
1516
  /**
1653
1517
  * 使用策略结果绘制文本
1654
- */ _proto.drawTextWithStrategies = function drawTextWithStrategies(context, lines, horizontalAlignResult, TextVerticalAlignResult, overflowResult, textStyle) {
1655
- var currentBaselineY = TextVerticalAlignResult.baselineY;
1518
+ * 坐标 = 帧坐标(对齐结果) + 渲染偏移(溢出结果)
1519
+ */ _proto.drawTextWithStrategies = function drawTextWithStrategies(context, lines, horizontalAlignResult, verticalAlignResult, overflowResult, textStyle) {
1520
+ var renderOffsetX = overflowResult.renderOffsetX, renderOffsetY = overflowResult.renderOffsetY;
1521
+ var currentBaselineY = verticalAlignResult.baselineY + renderOffsetY;
1656
1522
  var lineOffsets = horizontalAlignResult.lineOffsets;
1657
1523
  lines.forEach(function(line, index) {
1658
1524
  var richOptions = line.richOptions, chars = line.chars;
1659
- var xOffset = lineOffsets[index];
1525
+ var xOffset = lineOffsets[index] + renderOffsetX;
1660
1526
  var yOffset = currentBaselineY;
1661
1527
  richOptions.forEach(function(options, segIndex) {
1662
- var fontScale = textStyle.fontScale, textColor = textStyle.textColor, textFamily = textStyle.fontFamily, textWeight = textStyle.textWeight, richStyle = textStyle.fontStyle;
1528
+ var textColor = textStyle.textColor, textFamily = textStyle.fontFamily, textWeight = textStyle.textWeight, richStyle = textStyle.fontStyle;
1663
1529
  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;
1664
- // 直接使用原始字体大小(已在行数据中预缩放)
1665
1530
  var textSize = fontSize;
1666
- context.font = fontStyle + " " + fontWeight + " " + textSize * fontScale + "px " + fontFamily;
1531
+ context.font = fontStyle + " " + fontWeight + " " + textSize + "px " + fontFamily;
1667
1532
  var r = fontColor[0], g = fontColor[1], b = fontColor[2], a = fontColor[3];
1668
1533
  context.fillStyle = "rgba(" + r + ", " + g + ", " + b + ", " + a + ")";
1669
- // 逐字绘制
1670
1534
  var segStartX = line.offsetX && line.offsetX[segIndex] ? line.offsetX[segIndex] : 0;
1671
1535
  var charArr = chars[segIndex];
1672
1536
  charArr.forEach(function(charDetail) {
1673
- // 直接使用缩放后的字符位置
1674
1537
  context.fillText(charDetail.char, xOffset + segStartX + charDetail.x, yOffset);
1675
1538
  });
1676
1539
  });
@@ -1743,7 +1606,7 @@ EFFECTS.applyMixins(exports.RichTextComponent, [
1743
1606
 
1744
1607
  /**
1745
1608
  * 插件版本号
1746
- */ var version = "2.8.8";
1609
+ */ var version = "2.8.10";
1747
1610
  EFFECTS.registerPlugin("rich-text", RichTextLoader);
1748
1611
  EFFECTS.logger.info("Plugin rich text version: " + version + ".");
1749
1612
  if (version !== EFFECTS__namespace.version) {