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