@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.
- package/dist/EVA.plugin.renderer.text.js +306 -121
- package/dist/EVA.plugin.renderer.text.min.js +1 -1
- package/dist/plugin-renderer-text.cjs.js +364 -133
- package/dist/plugin-renderer-text.cjs.prod.js +1 -1
- package/dist/plugin-renderer-text.d.ts +17 -0
- package/dist/plugin-renderer-text.esm.js +366 -135
- package/package.json +5 -5
|
@@ -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
|
-
|
|
650
|
+
const loading = this.addTextComponent(changed);
|
|
651
|
+
if (loading)
|
|
652
|
+
yield loading;
|
|
627
653
|
}
|
|
628
654
|
else if (isHTMLText) {
|
|
629
|
-
|
|
655
|
+
const loading = this.addHTMLTextComponent(changed);
|
|
656
|
+
if (loading)
|
|
657
|
+
yield loading;
|
|
630
658
|
}
|
|
631
659
|
else {
|
|
632
|
-
|
|
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
|
-
|
|
638
|
-
|
|
639
|
-
|
|
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
|
-
|
|
648
|
-
|
|
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.
|
|
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
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
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
|
|
681
|
-
|
|
682
|
-
const
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
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
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
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 =
|
|
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
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
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 =
|
|
763
|
-
// stroke
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
width
|
|
769
|
-
};
|
|
770
|
-
|
|
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
|
|
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
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
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
|
|
849
|
-
const
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
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;
|