@eva/plugin-renderer-text 2.1.0-beta.14 → 2.1.0-beta.15

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.
@@ -22,6 +22,18 @@ OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
22
22
  PERFORMANCE OF THIS SOFTWARE.
23
23
  ***************************************************************************** */
24
24
 
25
+ function __rest(s, e) {
26
+ var t = {};
27
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
28
+ t[p] = s[p];
29
+ if (s != null && typeof Object.getOwnPropertySymbols === "function")
30
+ for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
31
+ if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
32
+ t[p[i]] = s[p[i]];
33
+ }
34
+ return t;
35
+ }
36
+
25
37
  function __decorate(decorators, target, key, desc) {
26
38
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
27
39
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
@@ -608,166 +620,328 @@ let Text = class Text extends pluginRenderer.Renderer {
608
620
  constructor() {
609
621
  super(...arguments);
610
622
  this.name = 'Text';
623
+ this.presentationAddFlushEnabled = true;
611
624
  this.texts = {};
625
+ this.retired = false;
626
+ this.pendingHTMLTextComponents = {};
627
+ this.fontIdentityIds = new WeakMap();
628
+ this.nextFontIdentityId = 1;
612
629
  }
613
630
  init() {
614
631
  this.renderSystem = this.game.getSystem(pluginRenderer.RendererSystem);
615
632
  this.renderSystem.rendererManager.register(this);
616
633
  }
634
+ shouldFlushPresentationAddition(changed) {
635
+ return changed.componentName === 'Text' || changed.componentName === 'HTMLText' || changed.componentName === 'BitmapText';
636
+ }
617
637
  componentChanged(changed) {
638
+ var _a, _b, _c, _d, _e;
618
639
  return __awaiter(this, void 0, void 0, function* () {
640
+ if (this.retired)
641
+ return;
619
642
  const isText = changed.componentName === 'Text';
620
643
  const isHTMLText = changed.componentName === 'HTMLText';
621
644
  const isBitmapText = changed.componentName === 'BitmapText';
622
645
  if (!isText && !isHTMLText && !isBitmapText)
623
646
  return;
647
+ const gameObjectId = changed.gameObject.id;
624
648
  if (changed.type === eva_js.OBSERVER_TYPE.ADD) {
625
649
  if (isText) {
626
- yield this.addTextComponent(changed);
650
+ const loading = this.addTextComponent(changed);
651
+ if (loading)
652
+ yield loading;
627
653
  }
628
654
  else if (isHTMLText) {
629
- yield this.addHTMLTextComponent(changed);
655
+ const loading = this.addHTMLTextComponent(changed);
656
+ if (loading)
657
+ yield loading;
630
658
  }
631
659
  else {
632
- yield this.addBitmapTextComponent(changed);
660
+ const loading = this.addBitmapTextComponent(changed);
661
+ if (loading)
662
+ yield loading;
663
+ }
664
+ if (((_a = this.texts[gameObjectId]) === null || _a === void 0 ? void 0 : _a.component) === changed.component) {
665
+ this.setSize(changed);
633
666
  }
634
- this.setSize(changed);
635
667
  }
636
668
  else if (changed.type === eva_js.OBSERVER_TYPE.REMOVE) {
637
- this.containerManager.getContainer(changed.gameObject.id).removeChild(this.texts[changed.gameObject.id].text);
638
- this.texts[changed.gameObject.id].text.destroy({ children: true });
639
- delete this.texts[changed.gameObject.id];
669
+ if (((_b = this.texts[gameObjectId]) === null || _b === void 0 ? void 0 : _b.component) !== changed.component && this.pendingHTMLTextComponents[gameObjectId] !== changed.component)
670
+ return;
671
+ this.increaseAsyncId(gameObjectId);
672
+ if (this.pendingHTMLTextComponents[gameObjectId] === changed.component) {
673
+ delete this.pendingHTMLTextComponents[gameObjectId];
674
+ }
675
+ const current = this.texts[gameObjectId];
676
+ if (!current || current.component !== changed.component)
677
+ return;
678
+ this.containerManager.getContainer(gameObjectId).removeChild(current.text);
679
+ current.text.destroy({ children: true });
680
+ delete this.texts[gameObjectId];
640
681
  }
641
682
  else {
642
- this.change(changed);
643
683
  // Only a font-family replacement can introduce a new async font asset.
644
684
  const component = changed.component;
645
685
  const propPath = changed.prop.prop;
646
686
  const fontFamilyChanged = propPath[0] === 'style' && (propPath.length === 1 || propPath[1] === 'fontFamily');
647
- if (fontFamilyChanged && component.style && this.hasRegisteredFont(component.style.fontFamily)) {
648
- const { text } = this.texts[changed.gameObject.id];
687
+ const current = this.texts[gameObjectId];
688
+ // A registered-font HTMLText ADD is intentionally not mounted until the
689
+ // font is ready. While it is pending, ordinary updates are already held
690
+ // by the component; a font-family change restarts (or completes) ADD.
691
+ if (!current) {
692
+ if (isHTMLText &&
693
+ this.pendingHTMLTextComponents[gameObjectId] === component &&
694
+ fontFamilyChanged) {
695
+ yield this.addHTMLTextComponent(changed);
696
+ if (((_c = this.texts[gameObjectId]) === null || _c === void 0 ? void 0 : _c.component) === component) {
697
+ this.setSize(changed);
698
+ }
699
+ }
700
+ return;
701
+ }
702
+ if (current.component !== component)
703
+ return;
704
+ const waitsForFontResource = fontFamilyChanged && component.style && this.needsFontLoad(component.style.fontFamily);
705
+ if (fontFamilyChanged && !waitsForFontResource) {
706
+ // A system/CSS font switch is synchronous, but it must still cancel an
707
+ // older registered-font request for the same GameObject.
708
+ this.increaseAsyncId(gameObjectId);
709
+ }
710
+ this.change(changed);
711
+ if (waitsForFontResource) {
712
+ const { text } = current;
649
713
  yield this.waitForFontResource(text, changed, component.style.fontFamily);
650
714
  }
651
- this.setSize(changed);
715
+ if (((_d = this.texts[gameObjectId]) === null || _d === void 0 ? void 0 : _d.text) === current.text && ((_e = this.texts[gameObjectId]) === null || _e === void 0 ? void 0 : _e.component) === component) {
716
+ this.setSize(changed);
717
+ }
652
718
  }
653
719
  });
654
720
  }
655
721
  addTextComponent(changed) {
656
- return __awaiter(this, void 0, void 0, function* () {
657
- const component = changed.component;
658
- // Registered Eva font assets must load before the first glyph. Ordinary
659
- // CSS/system families are already valid Pixi styles and render directly.
660
- const styleWithoutFont = this.processStyle(component.style);
661
- const fontFamily = styleWithoutFont.fontFamily;
662
- const waitsForFontResource = this.hasRegisteredFont(fontFamily);
663
- if (waitsForFontResource)
664
- delete styleWithoutFont.fontFamily;
665
- const initialText = waitsForFontResource ? '' : component.text;
666
- const text = new rendererAdapter.Text(initialText, styleWithoutFont);
667
- this.containerManager.getContainer(changed.gameObject.id).addChildAt(text, 0);
668
- this.texts[changed.gameObject.id] = {
669
- text,
670
- component,
671
- };
672
- // 如果指定了字体资源,等待资源加载完成后设置 fontFamily
673
- if (waitsForFontResource) {
674
- yield this.waitForFontResource(text, changed, fontFamily);
675
- }
676
- });
722
+ const component = changed.component;
723
+ // Registered Eva font assets must load before the first glyph. Ordinary
724
+ // CSS/system families are already valid Pixi styles and render directly.
725
+ const styleWithoutFont = this.processStyle(component.style);
726
+ const fontFamily = styleWithoutFont.fontFamily;
727
+ const waitsForFontResource = this.needsFontLoad(fontFamily);
728
+ if (waitsForFontResource)
729
+ delete styleWithoutFont.fontFamily;
730
+ const initialText = waitsForFontResource ? '' : component.text;
731
+ const text = new rendererAdapter.Text(initialText, styleWithoutFont);
732
+ this.containerManager.getContainer(changed.gameObject.id).addChildAt(text, 0);
733
+ this.texts[changed.gameObject.id] = {
734
+ text,
735
+ component,
736
+ awaitingInitialFont: waitsForFontResource,
737
+ };
738
+ // 如果指定了字体资源,等待资源加载完成后设置 fontFamily
739
+ if (waitsForFontResource) {
740
+ return this.waitForFontResource(text, changed, fontFamily);
741
+ }
677
742
  }
678
743
  addHTMLTextComponent(changed) {
744
+ const component = changed.component;
745
+ const gameObjectId = changed.gameObject.id;
746
+ const initialStyle = this.processStyle(component.style);
747
+ const fontFamily = initialStyle.fontFamily;
748
+ const waitsForFontResource = this.needsFontLoad(fontFamily);
749
+ if (waitsForFontResource) {
750
+ this.pendingHTMLTextComponents[gameObjectId] = component;
751
+ return this.addHTMLTextAfterFont(changed, component, fontFamily, this.increaseAsyncId(gameObjectId));
752
+ }
753
+ else if (this.pendingHTMLTextComponents[gameObjectId] === component) {
754
+ delete this.pendingHTMLTextComponents[gameObjectId];
755
+ }
756
+ this.mountHTMLText(changed, component);
757
+ }
758
+ addHTMLTextAfterFont(changed, component, fontFamily, asyncId) {
759
+ var _a;
679
760
  return __awaiter(this, void 0, void 0, function* () {
680
- const component = changed.component;
681
- // 创建样式副本,先不设置 fontFamily
682
- const styleWithoutFont = this.processStyle(component.style);
683
- const fontFamily = styleWithoutFont.fontFamily;
684
- const waitsForFontResource = this.hasRegisteredFont(fontFamily);
685
- if (waitsForFontResource)
686
- delete styleWithoutFont.fontFamily;
687
- const initialText = waitsForFontResource ? '' : component.text;
688
- const htmlText = new rendererAdapter.HTMLText(Object.assign({ text: initialText, style: styleWithoutFont }, (component.textureStyle && { textureStyle: component.textureStyle })));
689
- this.containerManager.getContainer(changed.gameObject.id).addChildAt(htmlText, 0);
690
- this.texts[changed.gameObject.id] = {
691
- text: htmlText,
692
- component,
693
- };
694
- // 如果指定了字体资源,等待资源加载完成后设置 fontFamily
695
- if (waitsForFontResource) {
696
- yield this.waitForFontResource(htmlText, changed, fontFamily);
697
- }
761
+ const gameObjectId = changed.gameObject.id;
762
+ const fontName = this.getFontName(fontFamily);
763
+ const loaded = yield this.loadFontResource(fontFamily);
764
+ if (!loaded || this.retired || changed.gameObject.destroyed ||
765
+ !this.validateAsyncId(gameObjectId, asyncId) ||
766
+ this.pendingHTMLTextComponents[gameObjectId] !== component ||
767
+ this.getFontName((_a = component.style) === null || _a === void 0 ? void 0 : _a.fontFamily) !== fontName)
768
+ return;
769
+ this.mountHTMLText(changed, component);
698
770
  });
699
771
  }
700
- addBitmapTextComponent(changed) {
701
- return __awaiter(this, void 0, void 0, function* () {
702
- const component = changed.component;
703
- // 创建样式副本,先不设置 fontFamily
704
- const styleWithoutFont = this.processStyle(component.style);
705
- const fontFamily = styleWithoutFont.fontFamily;
706
- const waitsForFontResource = this.hasRegisteredFont(fontFamily);
707
- if (waitsForFontResource)
708
- delete styleWithoutFont.fontFamily;
709
- const initialText = waitsForFontResource ? '' : component.text;
710
- const bitmapText = new rendererAdapter.BitmapText(initialText, styleWithoutFont);
711
- this.containerManager.getContainer(changed.gameObject.id).addChildAt(bitmapText, 0);
712
- this.texts[changed.gameObject.id] = {
713
- text: bitmapText,
714
- component,
715
- };
716
- // 如果指定了字体资源,等待资源加载完成后设置 fontFamily
717
- if (waitsForFontResource) {
718
- yield this.waitForFontResource(bitmapText, changed, fontFamily);
772
+ mountHTMLText(changed, component) {
773
+ const gameObjectId = changed.gameObject.id;
774
+ const style = this.processStyle(component.style);
775
+ const _a = component.textureStyle || {}, sampling = __rest(_a, ["resolution"]);
776
+ const htmlText = new rendererAdapter.HTMLText({ text: component.text, style, textureStyle: Object.assign({}, sampling) });
777
+ htmlText.rasterCacheFontIdentity = (families) => this.getHTMLRasterFontIdentity(families);
778
+ this.applyHTMLResolution(htmlText, component, changed);
779
+ this.containerManager.getContainer(gameObjectId).addChildAt(htmlText, 0);
780
+ this.texts[gameObjectId] = {
781
+ text: htmlText,
782
+ component,
783
+ };
784
+ delete this.pendingHTMLTextComponents[gameObjectId];
785
+ }
786
+ getHTMLRasterFontIdentity(families) {
787
+ var _a;
788
+ const uniqueFamilies = [...new Set(families.filter((family) => typeof family === 'string')
789
+ .map(family => family.replace(/["']/g, '').trim()).filter(Boolean))];
790
+ if (!uniqueFamilies.length)
791
+ return 'external:<default>';
792
+ const describe = (face) => [
793
+ this.objectIdentity(face), face.family, face.style, face.weight, face.stretch,
794
+ face.unicodeRange, face.variant, face.featureSettings, face.display,
795
+ ].map(value => String(value !== null && value !== void 0 ? value : '')).join(':');
796
+ const identities = [];
797
+ for (const family of uniqueFamilies) {
798
+ const current = eva_js.resource.resourcesMap[family];
799
+ if (!current || current.type !== eva_js.RESOURCE_TYPE.FONT) {
800
+ identities.push(`external:${family}`);
801
+ continue;
719
802
  }
720
- });
803
+ if (current.complete !== true)
804
+ return null;
805
+ const rawFaces = (_a = current.data) === null || _a === void 0 ? void 0 : _a.font;
806
+ const faces = Array.isArray(rawFaces) ? rawFaces : [rawFaces];
807
+ if (!faces.length || faces.some(face => !face || typeof face !== 'object' || face.status !== 'loaded'))
808
+ return null;
809
+ identities.push(`${family}|resource:${this.objectIdentity(current)}|faces:${faces.map(describe).join(',')}`);
810
+ }
811
+ return identities.join(';');
812
+ }
813
+ objectIdentity(value) {
814
+ let id = this.fontIdentityIds.get(value);
815
+ if (!id) {
816
+ id = this.nextFontIdentityId++;
817
+ this.fontIdentityIds.set(value, id);
818
+ }
819
+ return id;
820
+ }
821
+ addBitmapTextComponent(changed) {
822
+ const component = changed.component;
823
+ // 创建样式副本,先不设置 fontFamily
824
+ const styleWithoutFont = this.processStyle(component.style);
825
+ const fontFamily = styleWithoutFont.fontFamily;
826
+ const waitsForFontResource = this.needsFontLoad(fontFamily);
827
+ if (waitsForFontResource)
828
+ delete styleWithoutFont.fontFamily;
829
+ const initialText = waitsForFontResource ? '' : component.text;
830
+ const bitmapText = new rendererAdapter.BitmapText(initialText, styleWithoutFont);
831
+ this.containerManager.getContainer(changed.gameObject.id).addChildAt(bitmapText, 0);
832
+ this.texts[changed.gameObject.id] = {
833
+ text: bitmapText,
834
+ component,
835
+ awaitingInitialFont: waitsForFontResource,
836
+ };
837
+ // 如果指定了字体资源,等待资源加载完成后设置 fontFamily
838
+ if (waitsForFontResource) {
839
+ return this.waitForFontResource(bitmapText, changed, fontFamily);
840
+ }
721
841
  }
722
842
  hasRegisteredFont(fontFamily) {
723
843
  var _a;
724
844
  if (!fontFamily)
725
845
  return false;
726
- const fontName = Array.isArray(fontFamily) ? fontFamily[0] : fontFamily;
846
+ const fontName = this.getFontName(fontFamily);
727
847
  return ((_a = eva_js.resource.resourcesMap[fontName]) === null || _a === void 0 ? void 0 : _a.type) === eva_js.RESOURCE_TYPE.FONT;
728
848
  }
849
+ needsFontLoad(fontFamily) {
850
+ if (!this.hasRegisteredFont(fontFamily))
851
+ return false;
852
+ // Consult the current resource generation, not a name-based font cache.
853
+ // A completed FONT already has its face installed; yielding here would
854
+ // present an empty placeholder even when the application preloaded it.
855
+ return eva_js.resource.resourcesMap[this.getFontName(fontFamily)].complete !== true;
856
+ }
857
+ getFontName(fontFamily) {
858
+ if (!fontFamily)
859
+ return undefined;
860
+ return Array.isArray(fontFamily) ? fontFamily[0] : fontFamily;
861
+ }
862
+ loadFontResource(fontFamily) {
863
+ return __awaiter(this, void 0, void 0, function* () {
864
+ const fontName = this.getFontName(fontFamily);
865
+ if (!fontName || !this.hasRegisteredFont(fontFamily))
866
+ return false;
867
+ const expectedResource = eva_js.resource.resourcesMap[fontName];
868
+ try {
869
+ const loaded = yield eva_js.resource.getResource(fontName);
870
+ // Resource resolves {} on failure or cancellation. Only the completed
871
+ // resource generation we requested can safely replace visible glyphs.
872
+ return loaded === expectedResource && loaded.complete === true &&
873
+ eva_js.resource.resourcesMap[fontName] === expectedResource;
874
+ }
875
+ catch (error) {
876
+ console.warn(`字体资源 ${fontFamily} 加载失败:`, error);
877
+ return false;
878
+ }
879
+ });
880
+ }
729
881
  /**
730
882
  * 等待字体资源加载完成并更新文本
731
883
  */
732
884
  waitForFontResource(text, changed, fontFamily) {
885
+ var _a;
733
886
  return __awaiter(this, void 0, void 0, function* () {
734
887
  if (!fontFamily) {
735
888
  return;
736
889
  }
737
- try {
738
- const fontName = Array.isArray(fontFamily) ? fontFamily[0] : fontFamily;
739
- if (!this.hasRegisteredFont(fontFamily))
740
- return;
741
- // 通过 resource 系统获取字体资源
742
- const asyncId = this.increaseAsyncId(changed.gameObject.id);
743
- yield eva_js.resource.getResource(fontName);
744
- // 验证异步操作是否仍然有效(防止组件已被移除)
745
- if (!this.validateAsyncId(changed.gameObject.id, asyncId))
746
- return;
747
- // 字体资源加载成功后,设置 fontFamily 并重新设置文本内容以触发重新渲染
748
- const component = this.texts[changed.gameObject.id].component;
749
- text.style.fontFamily = fontFamily;
750
- text.text = component.text;
751
- // 更新尺寸
752
- }
753
- catch (error) {
754
- console.warn(`字体资源 ${fontFamily} 加载失败:`, error);
890
+ const gameObjectId = changed.gameObject.id;
891
+ const fontName = this.getFontName(fontFamily);
892
+ if (!fontName || !this.hasRegisteredFont(fontFamily))
893
+ return;
894
+ const asyncId = this.increaseAsyncId(gameObjectId);
895
+ const loaded = yield this.loadFontResource(fontFamily);
896
+ const current = this.texts[gameObjectId];
897
+ if (!loaded || this.retired || changed.gameObject.destroyed ||
898
+ !this.validateAsyncId(gameObjectId, asyncId) ||
899
+ !current ||
900
+ current.text !== text ||
901
+ current.component !== changed.component) {
902
+ return;
755
903
  }
904
+ const currentFontFamily = (_a = current.component.style) === null || _a === void 0 ? void 0 : _a.fontFamily;
905
+ if (this.getFontName(currentFontFamily) !== fontName)
906
+ return;
907
+ // 字体资源加载成功后,设置 fontFamily 并重新设置文本内容以触发重新渲染
908
+ text.style.fontFamily = currentFontFamily;
909
+ current.awaitingInitialFont = false;
910
+ text.text = current.component.text;
756
911
  });
757
912
  }
758
913
  /**
759
914
  * 将 Eva.js 的样式格式转换为 PixiJS v8 格式
760
915
  */
761
916
  processStyle(style) {
762
- const processed = Object.assign({}, style);
763
- // stroke + strokeThickness -> stroke: { color, width }
764
- if (processed.strokeThickness) {
765
- const color = processed.stroke;
766
- processed.stroke = {
767
- color,
768
- width: processed.strokeThickness,
769
- };
770
- delete processed.strokeThickness;
917
+ const processed = this.cloneStyleValue(style);
918
+ // Preserve native v8 stroke options when merging v7 aliases. Zero is an
919
+ // intentional width update, not an absent value.
920
+ if (processed.strokeThickness != null || processed.lineJoin != null || processed.miterLimit != null) {
921
+ const stroke = processed.stroke;
922
+ const isOptions = stroke && typeof stroke === 'object' &&
923
+ ['color', 'width', 'fill', 'texture', 'join'].some(key => key in stroke);
924
+ const merged = isOptions ? Object.assign({}, stroke) : { color: stroke !== null && stroke !== void 0 ? stroke : 0x000000 };
925
+ if (processed.strokeThickness != null)
926
+ merged.width = processed.strokeThickness;
927
+ if (processed.lineJoin != null)
928
+ merged.join = processed.lineJoin;
929
+ if (processed.miterLimit != null)
930
+ merged.miterLimit = processed.miterLimit;
931
+ // Join-only defaults must not enable a previously disabled stroke.
932
+ if (stroke != null || processed.strokeThickness != null)
933
+ processed.stroke = merged;
934
+ }
935
+ delete processed.strokeThickness;
936
+ delete processed.lineJoin;
937
+ delete processed.miterLimit;
938
+ // Eva exposes an enabled switch; Pixi treats every shadow object as enabled.
939
+ // Normalize only the cloned style so toggling back preserves configuration.
940
+ if (processed.dropShadow && typeof processed.dropShadow === 'object' && !Array.isArray(processed.dropShadow)) {
941
+ if (processed.dropShadow.enabled === false)
942
+ processed.dropShadow = false;
943
+ else
944
+ delete processed.dropShadow.enabled;
771
945
  }
772
946
  // dropShadow*(v7 平铺 alias) -> dropShadow: { color, distance, angle, alpha, blur }
773
947
  // 关键:只有当存在任何 v7 平铺 alias 时才合并;否则保留 v8 嵌套对象(以及
@@ -803,60 +977,117 @@ let Text = class Text extends pluginRenderer.Renderer {
803
977
  delete processed.dropShadowAngle;
804
978
  delete processed.dropShadowAlpha;
805
979
  delete processed.dropShadowBlur;
806
- // fill 数组 -> 取第一个值 (deprecated)
980
+ // Legacy fill is the color array; fillGradientStops contains offsets.
981
+ // Local coordinates keep the direction valid for any font size or bounds.
807
982
  if (Array.isArray(processed.fill)) {
808
- processed.fill = processed.fill[0];
809
- }
810
- // fillGradientStops -> FillGradient 对象
811
- if (Array.isArray(processed.fillGradientStops)) {
812
- let fontSize;
813
- if (processed.fontSize == null) {
814
- fontSize = pixi_js.TextStyle.defaultTextStyle.fontSize;
815
- }
816
- else if (typeof processed.fontSize === 'string') {
817
- fontSize = parseInt(processed.fontSize, 10);
983
+ const colors = processed.fill;
984
+ if (colors.length > 1) {
985
+ const supplied = processed.fillGradientStops;
986
+ const stops = Array.isArray(supplied) && supplied.length === colors.length
987
+ ? supplied : colors.map((_, index) => index / (colors.length - 1));
988
+ const horizontal = processed.fillGradientType === 1;
989
+ const gradient = new pixi_js.FillGradient({
990
+ start: { x: 0, y: 0 },
991
+ end: horizontal ? { x: 1, y: 0 } : { x: 0, y: 1 },
992
+ textureSpace: 'local',
993
+ });
994
+ colors.forEach((color, index) => {
995
+ gradient.addColorStop(stops[index], pixi_js.Color.shared.setValue(color).toNumber());
996
+ });
997
+ processed.fill = { fill: gradient };
818
998
  }
819
999
  else {
820
- fontSize = processed.fontSize;
1000
+ processed.fill = colors[0];
821
1001
  }
822
- const gradientFill = new pixi_js.FillGradient(0, 0, 0, fontSize * 1.7);
823
- const fills = processed.fillGradientStops.map((color) => pixi_js.Color.shared.setValue(color).toNumber());
824
- fills.forEach((number, index) => {
825
- const ratio = index / (fills.length - 1);
826
- gradientFill.addColorStop(ratio, number);
827
- });
828
- processed.fill = { fill: gradientFill };
829
- delete processed.fillGradientStops;
830
1002
  }
1003
+ delete processed.fillGradientStops;
1004
+ delete processed.fillGradientType;
831
1005
  return processed;
832
1006
  }
833
1007
  change(changed) {
834
- const { text, component } = this.texts[changed.gameObject.id];
1008
+ var _a, _b, _c, _d, _e, _f, _g, _h;
1009
+ const current = this.texts[changed.gameObject.id];
1010
+ const { text, component } = current;
835
1011
  const isHTMLText = changed.componentName === 'HTMLText';
836
1012
  if (changed.prop.prop[0] === 'text') {
837
- text.text = component.text;
1013
+ // First glyphs must not leak through the temporary default font while
1014
+ // the registered font is loading. Already-visible text can still update.
1015
+ if (!current.awaitingInitialFont)
1016
+ text.text = component.text;
838
1017
  }
839
1018
  else if (changed.prop.prop[0] === 'style') {
840
1019
  const processedStyle = this.processStyle(component.style);
841
- if (this.hasRegisteredFont(processedStyle.fontFamily))
1020
+ if (this.needsFontLoad(processedStyle.fontFamily))
842
1021
  delete processedStyle.fontFamily;
843
1022
  Object.assign(text.style, processedStyle);
1023
+ if (current.awaitingInitialFont && !this.needsFontLoad((_a = component.style) === null || _a === void 0 ? void 0 : _a.fontFamily)) {
1024
+ current.awaitingInitialFont = false;
1025
+ text.text = component.text;
1026
+ }
844
1027
  }
845
1028
  else if (changed.prop.prop[0] === 'textureStyle' && isHTMLText) {
846
- // HTMLText 纹理样式变化需要重新创建
1029
+ // Sampling updates do not require destroying a visible HTMLText. Passing
1030
+ // its observed component.style back to Pixi also allowed legacy style
1031
+ // conversion to re-enter Eva's observer with a nested color object.
847
1032
  const htmlComponent = component;
848
- const container = this.containerManager.getContainer(changed.gameObject.id);
849
- const index = container.getChildIndex(text);
850
- container.removeChild(text);
851
- text.destroy({ children: true });
852
- const newText = new rendererAdapter.HTMLText({
853
- text: htmlComponent.text,
854
- style: htmlComponent.style,
855
- textureStyle: htmlComponent.textureStyle,
856
- });
857
- container.addChildAt(newText, index);
858
- this.texts[changed.gameObject.id].text = newText;
1033
+ const htmlText = text;
1034
+ const _j = htmlComponent.textureStyle || {}, sampling = __rest(_j, ["resolution"]);
1035
+ if (htmlText.textureStyle) {
1036
+ const options = Object.assign(Object.assign({}, ((pixi_js.TextureStyle === null || pixi_js.TextureStyle === void 0 ? void 0 : pixi_js.TextureStyle.defaultOptions) || { addressMode: 'clamp-to-edge', scaleMode: 'linear' })), sampling);
1037
+ htmlText.textureStyle.addressMode = options.addressMode;
1038
+ htmlText.textureStyle.scaleMode = options.scaleMode;
1039
+ Object.assign(htmlText.textureStyle, {
1040
+ addressModeU: (_b = options.addressModeU) !== null && _b !== void 0 ? _b : options.addressMode,
1041
+ addressModeV: (_c = options.addressModeV) !== null && _c !== void 0 ? _c : options.addressMode,
1042
+ addressModeW: (_d = options.addressModeW) !== null && _d !== void 0 ? _d : options.addressMode,
1043
+ magFilter: (_e = options.magFilter) !== null && _e !== void 0 ? _e : options.scaleMode,
1044
+ minFilter: (_f = options.minFilter) !== null && _f !== void 0 ? _f : options.scaleMode,
1045
+ mipmapFilter: (_g = options.mipmapFilter) !== null && _g !== void 0 ? _g : options.scaleMode,
1046
+ lodMinClamp: options.lodMinClamp, lodMaxClamp: options.lodMaxClamp,
1047
+ compare: options.compare, maxAnisotropy: (_h = options.maxAnisotropy) !== null && _h !== void 0 ? _h : 1,
1048
+ });
1049
+ htmlText.textureStyle.update();
1050
+ }
1051
+ else {
1052
+ htmlText.textureStyle = new pixi_js.TextureStyle(sampling);
1053
+ }
1054
+ this.applyHTMLResolution(htmlText, htmlComponent, changed);
1055
+ }
1056
+ }
1057
+ applyHTMLResolution(text, component, changed) {
1058
+ var _a, _b, _c, _d;
1059
+ // Render's explicit resolution remains authoritative, including when HTML
1060
+ // mounts after an asynchronous font load and misses Render's ADD callback.
1061
+ const render = (_b = (_a = changed.gameObject).getComponent) === null || _b === void 0 ? void 0 : _b.call(_a, 'Render');
1062
+ const resolution = (_c = render === null || render === void 0 ? void 0 : render.resolution) !== null && _c !== void 0 ? _c : (_d = component.textureStyle) === null || _d === void 0 ? void 0 : _d.resolution;
1063
+ if (typeof resolution === 'number' && Number.isFinite(resolution) && resolution > 0 && text.resolution !== resolution) {
1064
+ text.resolution = resolution;
1065
+ }
1066
+ else if (resolution == null) {
1067
+ text.resolution = null;
1068
+ }
1069
+ }
1070
+ onDestroy() {
1071
+ var _a, _b;
1072
+ this.retired = true;
1073
+ for (const id of Object.keys(this.asyncIdMap))
1074
+ this.increaseAsyncId(Number(id));
1075
+ this.pendingHTMLTextComponents = {};
1076
+ for (const { text } of Object.values(this.texts)) {
1077
+ (_a = text.parent) === null || _a === void 0 ? void 0 : _a.removeChild(text);
1078
+ if (!text.destroyed)
1079
+ (_b = text.destroy) === null || _b === void 0 ? void 0 : _b.call(text, { children: true });
1080
+ }
1081
+ this.texts = {};
1082
+ }
1083
+ cloneStyleValue(value) {
1084
+ if (Array.isArray(value))
1085
+ return value.map(item => this.cloneStyleValue(item));
1086
+ if (value && Object.getPrototypeOf(value) === Object.prototype) {
1087
+ return Object.fromEntries(Object.entries(value).map(([key, child]) => [key, this.cloneStyleValue(child)]));
859
1088
  }
1089
+ // Preserve class-based Pixi fills/gradients/textures; clone plain component data.
1090
+ return value;
860
1091
  }
861
1092
  setSize(changed) {
862
1093
  const { transform } = changed.gameObject;