@eva/plugin-renderer-text 2.0.1-beta.3 → 2.0.1-beta.30
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 +129 -11
- package/dist/EVA.plugin.renderer.text.min.js +1 -1
- package/dist/plugin-renderer-text.cjs.js +249 -11
- package/dist/plugin-renderer-text.cjs.prod.js +2 -2
- package/dist/plugin-renderer-text.d.ts +179 -2
- package/dist/plugin-renderer-text.esm.js +251 -14
- package/package.json +4 -4
|
@@ -81,6 +81,30 @@ var _EVA_IIFE_text = function (exports, pixi_js, eva_js, pluginRenderer, rendere
|
|
|
81
81
|
}
|
|
82
82
|
Text$2.componentName = 'Text';
|
|
83
83
|
__decorate([type('string')], Text$2.prototype, "text", void 0);
|
|
84
|
+
class HTMLText extends eva_js.Component {
|
|
85
|
+
constructor() {
|
|
86
|
+
super(...arguments);
|
|
87
|
+
this.text = '';
|
|
88
|
+
this.style = {};
|
|
89
|
+
this.textureStyle = {};
|
|
90
|
+
}
|
|
91
|
+
init(obj) {
|
|
92
|
+
this.style = _extends({
|
|
93
|
+
fontSize: 24,
|
|
94
|
+
fill: '#000000',
|
|
95
|
+
fontFamily: 'Arial'
|
|
96
|
+
}, obj === null || obj === void 0 ? void 0 : obj.style);
|
|
97
|
+
this.textureStyle = _extends({
|
|
98
|
+
scaleMode: 'linear',
|
|
99
|
+
resolution: window.devicePixelRatio || 1
|
|
100
|
+
}, obj === null || obj === void 0 ? void 0 : obj.textureStyle);
|
|
101
|
+
if (obj) {
|
|
102
|
+
this.text = obj.text;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
HTMLText.componentName = 'HTMLText';
|
|
107
|
+
__decorate([type('string')], HTMLText.prototype, "text", void 0);
|
|
84
108
|
let Text = class Text extends pluginRenderer.Renderer {
|
|
85
109
|
constructor() {
|
|
86
110
|
super(...arguments);
|
|
@@ -93,15 +117,15 @@ var _EVA_IIFE_text = function (exports, pixi_js, eva_js, pluginRenderer, rendere
|
|
|
93
117
|
}
|
|
94
118
|
componentChanged(changed) {
|
|
95
119
|
return __awaiter(this, void 0, void 0, function* () {
|
|
96
|
-
|
|
120
|
+
const isText = changed.componentName === 'Text';
|
|
121
|
+
const isHTMLText = changed.componentName === 'HTMLText';
|
|
122
|
+
if (!isText && !isHTMLText) return;
|
|
97
123
|
if (changed.type === eva_js.OBSERVER_TYPE.ADD) {
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
component: changed.component
|
|
104
|
-
};
|
|
124
|
+
if (isText) {
|
|
125
|
+
yield this.addTextComponent(changed);
|
|
126
|
+
} else {
|
|
127
|
+
yield this.addHTMLTextComponent(changed);
|
|
128
|
+
}
|
|
105
129
|
this.setSize(changed);
|
|
106
130
|
} else if (changed.type === eva_js.OBSERVER_TYPE.REMOVE) {
|
|
107
131
|
this.containerManager.getContainer(changed.gameObject.id).removeChild(this.texts[changed.gameObject.id].text);
|
|
@@ -111,19 +135,101 @@ var _EVA_IIFE_text = function (exports, pixi_js, eva_js, pluginRenderer, rendere
|
|
|
111
135
|
delete this.texts[changed.gameObject.id];
|
|
112
136
|
} else {
|
|
113
137
|
this.change(changed);
|
|
138
|
+
const component = changed.component;
|
|
139
|
+
if (changed.prop.prop[0] === 'style' && component.style && component.style.fontFamily) {
|
|
140
|
+
const {
|
|
141
|
+
text
|
|
142
|
+
} = this.texts[changed.gameObject.id];
|
|
143
|
+
yield this.waitForFontResource(text, changed, component.style.fontFamily);
|
|
144
|
+
}
|
|
114
145
|
this.setSize(changed);
|
|
115
146
|
}
|
|
116
147
|
});
|
|
117
148
|
}
|
|
149
|
+
addTextComponent(changed) {
|
|
150
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
151
|
+
const component = changed.component;
|
|
152
|
+
const styleWithoutFont = _extends({}, component.style);
|
|
153
|
+
const fontFamily = styleWithoutFont.fontFamily;
|
|
154
|
+
delete styleWithoutFont.fontFamily;
|
|
155
|
+
const initialText = fontFamily ? '' : component.text;
|
|
156
|
+
const text = new rendererAdapter.Text(initialText, styleWithoutFont);
|
|
157
|
+
this.containerManager.getContainer(changed.gameObject.id).addChildAt(text, 0);
|
|
158
|
+
this.texts[changed.gameObject.id] = {
|
|
159
|
+
text,
|
|
160
|
+
component
|
|
161
|
+
};
|
|
162
|
+
if (fontFamily) {
|
|
163
|
+
yield this.waitForFontResource(text, changed, fontFamily);
|
|
164
|
+
}
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
addHTMLTextComponent(changed) {
|
|
168
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
169
|
+
const component = changed.component;
|
|
170
|
+
const styleWithoutFont = _extends({}, component.style);
|
|
171
|
+
const fontFamily = styleWithoutFont.fontFamily;
|
|
172
|
+
delete styleWithoutFont.fontFamily;
|
|
173
|
+
const initialText = fontFamily ? '' : component.text;
|
|
174
|
+
const htmlText = new rendererAdapter.HTMLText(_extends({
|
|
175
|
+
text: initialText,
|
|
176
|
+
style: styleWithoutFont
|
|
177
|
+
}, component.textureStyle && {
|
|
178
|
+
textureStyle: component.textureStyle
|
|
179
|
+
}));
|
|
180
|
+
this.containerManager.getContainer(changed.gameObject.id).addChildAt(htmlText, 0);
|
|
181
|
+
this.texts[changed.gameObject.id] = {
|
|
182
|
+
text: htmlText,
|
|
183
|
+
component
|
|
184
|
+
};
|
|
185
|
+
if (fontFamily) {
|
|
186
|
+
yield this.waitForFontResource(htmlText, changed, fontFamily);
|
|
187
|
+
}
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
waitForFontResource(text, changed, fontFamily) {
|
|
191
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
192
|
+
if (!fontFamily) {
|
|
193
|
+
return;
|
|
194
|
+
}
|
|
195
|
+
try {
|
|
196
|
+
const fontName = Array.isArray(fontFamily) ? fontFamily[0] : fontFamily;
|
|
197
|
+
const asyncId = this.increaseAsyncId(changed.gameObject.id);
|
|
198
|
+
yield eva_js.resource.getResource(fontName);
|
|
199
|
+
if (!this.validateAsyncId(changed.gameObject.id, asyncId)) return;
|
|
200
|
+
const component = this.texts[changed.gameObject.id].component;
|
|
201
|
+
text.style.fontFamily = fontFamily;
|
|
202
|
+
text.text = component.text;
|
|
203
|
+
} catch (error) {
|
|
204
|
+
console.warn(`字体资源 ${fontFamily} 加载失败:`, error);
|
|
205
|
+
}
|
|
206
|
+
});
|
|
207
|
+
}
|
|
118
208
|
change(changed) {
|
|
119
209
|
const {
|
|
120
210
|
text,
|
|
121
211
|
component
|
|
122
212
|
} = this.texts[changed.gameObject.id];
|
|
213
|
+
const isHTMLText = changed.componentName === 'HTMLText';
|
|
123
214
|
if (changed.prop.prop[0] === 'text') {
|
|
124
215
|
text.text = component.text;
|
|
125
216
|
} else if (changed.prop.prop[0] === 'style') {
|
|
126
|
-
_extends(text.style,
|
|
217
|
+
_extends(text.style, component.style);
|
|
218
|
+
} else if (changed.prop.prop[0] === 'textureStyle' && isHTMLText) {
|
|
219
|
+
const htmlComponent = component;
|
|
220
|
+
const container = this.containerManager.getContainer(changed.gameObject.id);
|
|
221
|
+
const index = container.getChildIndex(text);
|
|
222
|
+
container.removeChild(text);
|
|
223
|
+
text.destroy({
|
|
224
|
+
children: true
|
|
225
|
+
});
|
|
226
|
+
const newText = new rendererAdapter.HTMLText({
|
|
227
|
+
text: htmlComponent.text,
|
|
228
|
+
style: htmlComponent.style,
|
|
229
|
+
textureStyle: htmlComponent.textureStyle
|
|
230
|
+
});
|
|
231
|
+
container.addChildAt(newText, index);
|
|
232
|
+
this.texts[changed.gameObject.id].text = newText;
|
|
127
233
|
}
|
|
128
234
|
}
|
|
129
235
|
setSize(changed) {
|
|
@@ -131,8 +237,12 @@ var _EVA_IIFE_text = function (exports, pixi_js, eva_js, pluginRenderer, rendere
|
|
|
131
237
|
transform
|
|
132
238
|
} = changed.gameObject;
|
|
133
239
|
if (!transform) return;
|
|
134
|
-
|
|
135
|
-
|
|
240
|
+
const {
|
|
241
|
+
text
|
|
242
|
+
} = this.texts[changed.gameObject.id];
|
|
243
|
+
const size = text.getSize();
|
|
244
|
+
transform.size.width = size.width;
|
|
245
|
+
transform.size.height = size.height;
|
|
136
246
|
}
|
|
137
247
|
};
|
|
138
248
|
Text.systemName = 'Text';
|
|
@@ -140,9 +250,17 @@ var _EVA_IIFE_text = function (exports, pixi_js, eva_js, pluginRenderer, rendere
|
|
|
140
250
|
Text: ['text', {
|
|
141
251
|
prop: ['style'],
|
|
142
252
|
deep: true
|
|
253
|
+
}],
|
|
254
|
+
HTMLText: ['text', {
|
|
255
|
+
prop: ['style'],
|
|
256
|
+
deep: true
|
|
257
|
+
}, {
|
|
258
|
+
prop: ['textureStyle'],
|
|
259
|
+
deep: true
|
|
143
260
|
}]
|
|
144
261
|
})], Text);
|
|
145
262
|
var Text$1 = Text;
|
|
263
|
+
exports.HTMLText = HTMLText;
|
|
146
264
|
exports.Text = Text$2;
|
|
147
265
|
exports.TextSystem = Text$1;
|
|
148
266
|
Object.defineProperty(exports, '__esModule', {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
function _extends(){return _extends=Object.assign?Object.assign.bind():function(
|
|
1
|
+
function _extends(){return _extends=Object.assign?Object.assign.bind():function(t){for(var e=1;e<arguments.length;e++){var n=arguments[e];for(var i in n)({}).hasOwnProperty.call(n,i)&&(t[i]=n[i])}return t},_extends.apply(null,arguments)}globalThis.EVA=globalThis.EVA||{},globalThis.EVA.plugin=globalThis.EVA.plugin||{},globalThis.EVA.plugin.renderer=globalThis.EVA.plugin.renderer||{};var _EVA_IIFE_text=function(t,e,n,i,o){"use strict";function s(t,e,n,i){var o,s=arguments.length,r=s<3?e:null===i?i=Object.getOwnPropertyDescriptor(e,n):i;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)r=Reflect.decorate(t,e,n,i);else for(var c=t.length-1;c>=0;c--)(o=t[c])&&(r=(s<3?o(r):s>3?o(e,n,r):o(e,n))||r);return s>3&&r&&Object.defineProperty(e,n,r),r}function r(t,e,n,i){return new(n||(n=Promise))((function(o,s){function r(t){try{l(i.next(t))}catch(t){s(t)}}function c(t){try{l(i.throw(t))}catch(t){s(t)}}function l(t){var e;t.done?o(t.value):(e=t.value,e instanceof n?e:new n((function(t){t(e)}))).then(r,c)}l((i=i.apply(t,e||[])).next())}))}function c(t){return function(e,n){var i=function(t,e){return t.constructor.IDEProps||(t.constructor.IDEProps={}),t.constructor.IDEProps[e]||(t.constructor.IDEProps[e]={}),t.constructor.IDEProps[e]}(e,n);i.key=n,i.type=t}}class l extends n.Component{constructor(){super(...arguments),this.text="",this.style={}}init(t){const n=new e.TextStyle({fontSize:20}),i={};for(const t in n)0===t.indexOf("_")&&(i[t.substring(1)]=n[t]);delete i.styleKey,this.style=i,t&&(this.text=t.text,_extends(this.style,t.style))}}l.componentName="Text",s([c("string")],l.prototype,"text",void 0);class a extends n.Component{constructor(){super(...arguments),this.text="",this.style={},this.textureStyle={}}init(t){this.style=_extends({fontSize:24,fill:"#000000",fontFamily:"Arial"},null==t?void 0:t.style),this.textureStyle=_extends({scaleMode:"linear",resolution:window.devicePixelRatio||1},null==t?void 0:t.textureStyle),t&&(this.text=t.text)}}a.componentName="HTMLText",s([c("string")],a.prototype,"text",void 0);let d=class extends i.Renderer{constructor(){super(...arguments),this.name="Text",this.texts={}}init(){this.renderSystem=this.game.getSystem(i.RendererSystem),this.renderSystem.rendererManager.register(this)}componentChanged(t){return r(this,void 0,void 0,(function*(){const e="Text"===t.componentName,i="HTMLText"===t.componentName;if(e||i)if(t.type===n.OBSERVER_TYPE.ADD)e?yield this.addTextComponent(t):yield this.addHTMLTextComponent(t),this.setSize(t);else if(t.type===n.OBSERVER_TYPE.REMOVE)this.containerManager.getContainer(t.gameObject.id).removeChild(this.texts[t.gameObject.id].text),this.texts[t.gameObject.id].text.destroy({children:!0}),delete this.texts[t.gameObject.id];else{this.change(t);const e=t.component;if("style"===t.prop.prop[0]&&e.style&&e.style.fontFamily){const{text:n}=this.texts[t.gameObject.id];yield this.waitForFontResource(n,t,e.style.fontFamily)}this.setSize(t)}}))}addTextComponent(t){return r(this,void 0,void 0,(function*(){const e=t.component,n=_extends({},e.style),i=n.fontFamily;delete n.fontFamily;const s=i?"":e.text,r=new o.Text(s,n);this.containerManager.getContainer(t.gameObject.id).addChildAt(r,0),this.texts[t.gameObject.id]={text:r,component:e},i&&(yield this.waitForFontResource(r,t,i))}))}addHTMLTextComponent(t){return r(this,void 0,void 0,(function*(){const e=t.component,n=_extends({},e.style),i=n.fontFamily;delete n.fontFamily;const s=i?"":e.text,r=new o.HTMLText(_extends({text:s,style:n},e.textureStyle&&{textureStyle:e.textureStyle}));this.containerManager.getContainer(t.gameObject.id).addChildAt(r,0),this.texts[t.gameObject.id]={text:r,component:e},i&&(yield this.waitForFontResource(r,t,i))}))}waitForFontResource(t,e,i){return r(this,void 0,void 0,(function*(){if(i)try{const o=Array.isArray(i)?i[0]:i,s=this.increaseAsyncId(e.gameObject.id);if(yield n.resource.getResource(o),!this.validateAsyncId(e.gameObject.id,s))return;const r=this.texts[e.gameObject.id].component;t.style.fontFamily=i,t.text=r.text}catch(t){console.warn(`字体资源 ${i} 加载失败:`,t)}}))}change(t){const{text:e,component:n}=this.texts[t.gameObject.id],i="HTMLText"===t.componentName;if("text"===t.prop.prop[0])e.text=n.text;else if("style"===t.prop.prop[0])_extends(e.style,n.style);else if("textureStyle"===t.prop.prop[0]&&i){const i=n,s=this.containerManager.getContainer(t.gameObject.id),r=s.getChildIndex(e);s.removeChild(e),e.destroy({children:!0});const c=new o.HTMLText({text:i.text,style:i.style,textureStyle:i.textureStyle});s.addChildAt(c,r),this.texts[t.gameObject.id].text=c}}setSize(t){const{transform:e}=t.gameObject;if(!e)return;const{text:n}=this.texts[t.gameObject.id],i=n.getSize();e.size.width=i.width,e.size.height=i.height}};d.systemName="Text",d=s([n.decorators.componentObserver({Text:["text",{prop:["style"],deep:!0}],HTMLText:["text",{prop:["style"],deep:!0},{prop:["textureStyle"],deep:!0}]})],d);var x=d;return t.HTMLText=a,t.Text=l,t.TextSystem=x,Object.defineProperty(t,"__esModule",{value:!0}),t}({},PIXI,EVA,EVA.plugin.renderer,EVA.rendererAdapter);globalThis.EVA.plugin.renderer.text=globalThis.EVA.plugin.renderer.text||_EVA_IIFE_text;
|
|
@@ -39,12 +39,63 @@ function __awaiter(thisArg, _arguments, P, generator) {
|
|
|
39
39
|
});
|
|
40
40
|
}
|
|
41
41
|
|
|
42
|
+
/**
|
|
43
|
+
* 文本组件(基于 PixiJS Text)
|
|
44
|
+
*
|
|
45
|
+
* Text 组件用于渲染文本内容,支持丰富的文本样式配置。
|
|
46
|
+
* 它基于 PixiJS 的 Text 实现,支持字体、颜色、描边、阴影、对齐等多种样式。
|
|
47
|
+
*
|
|
48
|
+
* 主要特性:
|
|
49
|
+
* - 支持多种字体和字号
|
|
50
|
+
* - 支持文本颜色、渐变填充
|
|
51
|
+
* - 支持描边和投影效果
|
|
52
|
+
* - 支持文本对齐和换行
|
|
53
|
+
*
|
|
54
|
+
* @example
|
|
55
|
+
* ```typescript
|
|
56
|
+
* // 基础文本
|
|
57
|
+
* const label = new GameObject('label');
|
|
58
|
+
* label.addComponent(new Text({
|
|
59
|
+
* text: 'Hello EVA!',
|
|
60
|
+
* style: {
|
|
61
|
+
* fontSize: 32,
|
|
62
|
+
* fill: 0xffffff
|
|
63
|
+
* }
|
|
64
|
+
* }));
|
|
65
|
+
*
|
|
66
|
+
* // 带样式的文本
|
|
67
|
+
* label.addComponent(new Text({
|
|
68
|
+
* text: '得分: 9999',
|
|
69
|
+
* style: {
|
|
70
|
+
* fontFamily: 'Arial',
|
|
71
|
+
* fontSize: 48,
|
|
72
|
+
* fontWeight: 'bold',
|
|
73
|
+
* fill: ['#ff0000', '#ffff00'], // 渐变色
|
|
74
|
+
* stroke: '#000000',
|
|
75
|
+
* strokeThickness: 4,
|
|
76
|
+
* dropShadow: true,
|
|
77
|
+
* dropShadowDistance: 3
|
|
78
|
+
* }
|
|
79
|
+
* }));
|
|
80
|
+
* // 如需高清渲染,使用 Render 组件的 resolution 属性
|
|
81
|
+
* label.addComponent(new Render({ resolution: 2 }));
|
|
82
|
+
* ```
|
|
83
|
+
*/
|
|
42
84
|
class Text$2 extends eva_js.Component {
|
|
43
85
|
constructor() {
|
|
44
86
|
super(...arguments);
|
|
87
|
+
/** 文本内容 */
|
|
45
88
|
this.text = '';
|
|
89
|
+
/** 文本样式配置 */
|
|
90
|
+
// @decorators.IDEProp 复杂编辑后续添加
|
|
46
91
|
this.style = {};
|
|
47
92
|
}
|
|
93
|
+
/**
|
|
94
|
+
* 初始化组件
|
|
95
|
+
* @param obj - 初始化参数
|
|
96
|
+
* @param obj.text - 文本内容
|
|
97
|
+
* @param obj.style - 文本样式
|
|
98
|
+
*/
|
|
48
99
|
init(obj) {
|
|
49
100
|
const style = new pixi_js.TextStyle({
|
|
50
101
|
fontSize: 20,
|
|
@@ -63,11 +114,104 @@ class Text$2 extends eva_js.Component {
|
|
|
63
114
|
}
|
|
64
115
|
}
|
|
65
116
|
}
|
|
117
|
+
/** 组件名称 */
|
|
66
118
|
Text$2.componentName = 'Text';
|
|
67
119
|
__decorate([
|
|
68
120
|
inspectorDecorator.type('string')
|
|
69
121
|
], Text$2.prototype, "text", void 0);
|
|
70
122
|
|
|
123
|
+
/**
|
|
124
|
+
* HTML 富文本组件
|
|
125
|
+
*
|
|
126
|
+
* HTMLText 组件支持渲染带有 HTML 标签的富文本内容。
|
|
127
|
+
* 可以在文本中使用 HTML 标签(如 `<b>`, `<i>`, `<span>` 等)来实现丰富的文本样式,
|
|
128
|
+
* 适用于聊天对话、新闻内容、富文本显示等需要多样式文本的场景。
|
|
129
|
+
*
|
|
130
|
+
* 支持的 HTML 标签:
|
|
131
|
+
* - `<b>` - 粗体
|
|
132
|
+
* - `<i>` - 斜体
|
|
133
|
+
* - `<span style="color:#ff0000">` - 自定义样式
|
|
134
|
+
* - `<br>` - 换行
|
|
135
|
+
* 以及更多标准 HTML 文本标签
|
|
136
|
+
*
|
|
137
|
+
* @example
|
|
138
|
+
* ```typescript
|
|
139
|
+
* // 基础富文本
|
|
140
|
+
* const label = new GameObject('label');
|
|
141
|
+
* label.addComponent(new HTMLText({
|
|
142
|
+
* text: '这是<b>粗体</b>和<i>斜体</i>文本',
|
|
143
|
+
* style: {
|
|
144
|
+
* fontSize: 24,
|
|
145
|
+
* fill: '#000000',
|
|
146
|
+
* fontFamily: 'Arial'
|
|
147
|
+
* }
|
|
148
|
+
* }));
|
|
149
|
+
*
|
|
150
|
+
* // 带颜色的富文本
|
|
151
|
+
* label.addComponent(new HTMLText({
|
|
152
|
+
* text: '欢迎 <span style="color:#ff0000">玩家123</span> 加入游戏!',
|
|
153
|
+
* style: {
|
|
154
|
+
* fontSize: 20,
|
|
155
|
+
* wordWrap: true,
|
|
156
|
+
* wordWrapWidth: 300
|
|
157
|
+
* }
|
|
158
|
+
* }));
|
|
159
|
+
*
|
|
160
|
+
* // 自定义标签样式
|
|
161
|
+
* label.addComponent(new HTMLText({
|
|
162
|
+
* text: '获得 <gold>100</gold> 金币',
|
|
163
|
+
* style: {
|
|
164
|
+
* fontSize: 18,
|
|
165
|
+
* tagStyles: {
|
|
166
|
+
* gold: {
|
|
167
|
+
* fill: '#ffd700',
|
|
168
|
+
* fontWeight: 'bold'
|
|
169
|
+
* }
|
|
170
|
+
* }
|
|
171
|
+
* }
|
|
172
|
+
* }));
|
|
173
|
+
*
|
|
174
|
+
* // 高分辨率渲染(推荐使用 Render 组件的 resolution 属性)
|
|
175
|
+
* label.addComponent(new HTMLText({
|
|
176
|
+
* text: '高清文本',
|
|
177
|
+
* textureStyle: {
|
|
178
|
+
* scaleMode: 'linear'
|
|
179
|
+
* }
|
|
180
|
+
* }));
|
|
181
|
+
* label.addComponent(new Render({ resolution: 2 }));
|
|
182
|
+
* ```
|
|
183
|
+
*/
|
|
184
|
+
class HTMLText extends eva_js.Component {
|
|
185
|
+
constructor() {
|
|
186
|
+
super(...arguments);
|
|
187
|
+
/** 富文本内容(支持 HTML 标签) */
|
|
188
|
+
this.text = '';
|
|
189
|
+
/** 文本样式配置 */
|
|
190
|
+
this.style = {};
|
|
191
|
+
/** 纹理渲染配置 */
|
|
192
|
+
this.textureStyle = {};
|
|
193
|
+
}
|
|
194
|
+
/**
|
|
195
|
+
* 初始化组件
|
|
196
|
+
* @param obj - 初始化参数
|
|
197
|
+
* @param obj.text - 富文本内容
|
|
198
|
+
* @param obj.style - 文本样式
|
|
199
|
+
* @param obj.textureStyle - 纹理配置
|
|
200
|
+
*/
|
|
201
|
+
init(obj) {
|
|
202
|
+
this.style = Object.assign({ fontSize: 24, fill: '#000000', fontFamily: 'Arial' }, obj === null || obj === void 0 ? void 0 : obj.style);
|
|
203
|
+
this.textureStyle = Object.assign({ scaleMode: 'linear', resolution: window.devicePixelRatio || 1 }, obj === null || obj === void 0 ? void 0 : obj.textureStyle);
|
|
204
|
+
if (obj) {
|
|
205
|
+
this.text = obj.text;
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
/** 组件名称 */
|
|
210
|
+
HTMLText.componentName = 'HTMLText';
|
|
211
|
+
__decorate([
|
|
212
|
+
inspectorDecorator.type('string')
|
|
213
|
+
], HTMLText.prototype, "text", void 0);
|
|
214
|
+
|
|
71
215
|
let Text = class Text extends pluginRenderer.Renderer {
|
|
72
216
|
constructor() {
|
|
73
217
|
super(...arguments);
|
|
@@ -80,16 +224,17 @@ let Text = class Text extends pluginRenderer.Renderer {
|
|
|
80
224
|
}
|
|
81
225
|
componentChanged(changed) {
|
|
82
226
|
return __awaiter(this, void 0, void 0, function* () {
|
|
83
|
-
|
|
227
|
+
const isText = changed.componentName === 'Text';
|
|
228
|
+
const isHTMLText = changed.componentName === 'HTMLText';
|
|
229
|
+
if (!isText && !isHTMLText)
|
|
84
230
|
return;
|
|
85
231
|
if (changed.type === eva_js.OBSERVER_TYPE.ADD) {
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
};
|
|
232
|
+
if (isText) {
|
|
233
|
+
yield this.addTextComponent(changed);
|
|
234
|
+
}
|
|
235
|
+
else {
|
|
236
|
+
yield this.addHTMLTextComponent(changed);
|
|
237
|
+
}
|
|
93
238
|
this.setSize(changed);
|
|
94
239
|
}
|
|
95
240
|
else if (changed.type === eva_js.OBSERVER_TYPE.REMOVE) {
|
|
@@ -99,34 +244,127 @@ let Text = class Text extends pluginRenderer.Renderer {
|
|
|
99
244
|
}
|
|
100
245
|
else {
|
|
101
246
|
this.change(changed);
|
|
247
|
+
// 如果样式改变且涉及字体,也需要等待字体资源加载
|
|
248
|
+
const component = changed.component;
|
|
249
|
+
if (changed.prop.prop[0] === 'style' && component.style && component.style.fontFamily) {
|
|
250
|
+
const { text } = this.texts[changed.gameObject.id];
|
|
251
|
+
yield this.waitForFontResource(text, changed, component.style.fontFamily);
|
|
252
|
+
}
|
|
102
253
|
this.setSize(changed);
|
|
103
254
|
}
|
|
104
255
|
});
|
|
105
256
|
}
|
|
257
|
+
addTextComponent(changed) {
|
|
258
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
259
|
+
const component = changed.component;
|
|
260
|
+
// 创建文本样式副本,先不设置 fontFamily
|
|
261
|
+
const styleWithoutFont = Object.assign({}, component.style);
|
|
262
|
+
const fontFamily = styleWithoutFont.fontFamily;
|
|
263
|
+
delete styleWithoutFont.fontFamily;
|
|
264
|
+
const initialText = fontFamily ? '' : component.text;
|
|
265
|
+
const text = new rendererAdapter.Text(initialText, styleWithoutFont);
|
|
266
|
+
this.containerManager.getContainer(changed.gameObject.id).addChildAt(text, 0);
|
|
267
|
+
this.texts[changed.gameObject.id] = {
|
|
268
|
+
text,
|
|
269
|
+
component,
|
|
270
|
+
};
|
|
271
|
+
// 如果指定了字体资源,等待资源加载完成后设置 fontFamily
|
|
272
|
+
if (fontFamily) {
|
|
273
|
+
yield this.waitForFontResource(text, changed, fontFamily);
|
|
274
|
+
}
|
|
275
|
+
});
|
|
276
|
+
}
|
|
277
|
+
addHTMLTextComponent(changed) {
|
|
278
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
279
|
+
const component = changed.component;
|
|
280
|
+
// 创建样式副本,先不设置 fontFamily
|
|
281
|
+
const styleWithoutFont = Object.assign({}, component.style);
|
|
282
|
+
const fontFamily = styleWithoutFont.fontFamily;
|
|
283
|
+
delete styleWithoutFont.fontFamily;
|
|
284
|
+
const initialText = fontFamily ? '' : component.text;
|
|
285
|
+
const htmlText = new rendererAdapter.HTMLText(Object.assign({ text: initialText, style: styleWithoutFont }, (component.textureStyle && { textureStyle: component.textureStyle })));
|
|
286
|
+
this.containerManager.getContainer(changed.gameObject.id).addChildAt(htmlText, 0);
|
|
287
|
+
this.texts[changed.gameObject.id] = {
|
|
288
|
+
text: htmlText,
|
|
289
|
+
component,
|
|
290
|
+
};
|
|
291
|
+
// 如果指定了字体资源,等待资源加载完成后设置 fontFamily
|
|
292
|
+
if (fontFamily) {
|
|
293
|
+
yield this.waitForFontResource(htmlText, changed, fontFamily);
|
|
294
|
+
}
|
|
295
|
+
});
|
|
296
|
+
}
|
|
297
|
+
/**
|
|
298
|
+
* 等待字体资源加载完成并更新文本
|
|
299
|
+
*/
|
|
300
|
+
waitForFontResource(text, changed, fontFamily) {
|
|
301
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
302
|
+
if (!fontFamily) {
|
|
303
|
+
return;
|
|
304
|
+
}
|
|
305
|
+
try {
|
|
306
|
+
const fontName = Array.isArray(fontFamily) ? fontFamily[0] : fontFamily;
|
|
307
|
+
// 通过 resource 系统获取字体资源
|
|
308
|
+
const asyncId = this.increaseAsyncId(changed.gameObject.id);
|
|
309
|
+
yield eva_js.resource.getResource(fontName);
|
|
310
|
+
// 验证异步操作是否仍然有效(防止组件已被移除)
|
|
311
|
+
if (!this.validateAsyncId(changed.gameObject.id, asyncId))
|
|
312
|
+
return;
|
|
313
|
+
// 字体资源加载成功后,设置 fontFamily 并重新设置文本内容以触发重新渲染
|
|
314
|
+
const component = this.texts[changed.gameObject.id].component;
|
|
315
|
+
text.style.fontFamily = fontFamily;
|
|
316
|
+
text.text = component.text;
|
|
317
|
+
// 更新尺寸
|
|
318
|
+
}
|
|
319
|
+
catch (error) {
|
|
320
|
+
console.warn(`字体资源 ${fontFamily} 加载失败:`, error);
|
|
321
|
+
}
|
|
322
|
+
});
|
|
323
|
+
}
|
|
106
324
|
change(changed) {
|
|
107
325
|
const { text, component } = this.texts[changed.gameObject.id];
|
|
326
|
+
const isHTMLText = changed.componentName === 'HTMLText';
|
|
108
327
|
if (changed.prop.prop[0] === 'text') {
|
|
109
328
|
text.text = component.text;
|
|
110
329
|
}
|
|
111
330
|
else if (changed.prop.prop[0] === 'style') {
|
|
112
|
-
Object.assign(text.style,
|
|
331
|
+
Object.assign(text.style, component.style);
|
|
332
|
+
}
|
|
333
|
+
else if (changed.prop.prop[0] === 'textureStyle' && isHTMLText) {
|
|
334
|
+
// HTMLText 纹理样式变化需要重新创建
|
|
335
|
+
const htmlComponent = component;
|
|
336
|
+
const container = this.containerManager.getContainer(changed.gameObject.id);
|
|
337
|
+
const index = container.getChildIndex(text);
|
|
338
|
+
container.removeChild(text);
|
|
339
|
+
text.destroy({ children: true });
|
|
340
|
+
const newText = new rendererAdapter.HTMLText({
|
|
341
|
+
text: htmlComponent.text,
|
|
342
|
+
style: htmlComponent.style,
|
|
343
|
+
textureStyle: htmlComponent.textureStyle
|
|
344
|
+
});
|
|
345
|
+
container.addChildAt(newText, index);
|
|
346
|
+
this.texts[changed.gameObject.id].text = newText;
|
|
113
347
|
}
|
|
114
348
|
}
|
|
115
349
|
setSize(changed) {
|
|
116
350
|
const { transform } = changed.gameObject;
|
|
117
351
|
if (!transform)
|
|
118
352
|
return;
|
|
119
|
-
|
|
120
|
-
|
|
353
|
+
const { text } = this.texts[changed.gameObject.id];
|
|
354
|
+
const size = text.getSize();
|
|
355
|
+
transform.size.width = size.width;
|
|
356
|
+
transform.size.height = size.height;
|
|
121
357
|
}
|
|
122
358
|
};
|
|
123
359
|
Text.systemName = 'Text';
|
|
124
360
|
Text = __decorate([
|
|
125
361
|
eva_js.decorators.componentObserver({
|
|
126
362
|
Text: ['text', { prop: ['style'], deep: true }],
|
|
363
|
+
HTMLText: ['text', { prop: ['style'], deep: true }, { prop: ['textureStyle'], deep: true }],
|
|
127
364
|
})
|
|
128
365
|
], Text);
|
|
129
366
|
var Text$1 = Text;
|
|
130
367
|
|
|
368
|
+
exports.HTMLText = HTMLText;
|
|
131
369
|
exports.Text = Text$2;
|
|
132
370
|
exports.TextSystem = Text$1;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,"__esModule",{value:!0});var e=require("pixi.js"),t=require("@eva/eva.js"),n=require("@eva/inspector-decorator"),
|
|
1
|
+
"use strict";Object.defineProperty(exports,"__esModule",{value:!0});var e=require("pixi.js"),t=require("@eva/eva.js"),n=require("@eva/inspector-decorator"),i=require("@eva/plugin-renderer"),s=require("@eva/renderer-adapter");
|
|
2
2
|
/*! *****************************************************************************
|
|
3
3
|
Copyright (c) Microsoft Corporation. All rights reserved.
|
|
4
4
|
Licensed under the Apache License, Version 2.0 (the "License"); you may not use
|
|
@@ -13,4 +13,4 @@ MERCHANTABLITY OR NON-INFRINGEMENT.
|
|
|
13
13
|
See the Apache Version 2.0 License for specific language governing permissions
|
|
14
14
|
and limitations under the License.
|
|
15
15
|
***************************************************************************** */
|
|
16
|
-
function
|
|
16
|
+
function o(e,t,n,i){var s,o=arguments.length,r=o<3?t:null===i?i=Object.getOwnPropertyDescriptor(t,n):i;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)r=Reflect.decorate(e,t,n,i);else for(var c=e.length-1;c>=0;c--)(s=e[c])&&(r=(o<3?s(r):o>3?s(t,n,r):s(t,n))||r);return o>3&&r&&Object.defineProperty(t,n,r),r}function r(e,t,n,i){return new(n||(n=Promise))((function(s,o){function r(e){try{a(i.next(e))}catch(e){o(e)}}function c(e){try{a(i.throw(e))}catch(e){o(e)}}function a(e){e.done?s(e.value):new n((function(t){t(e.value)})).then(r,c)}a((i=i.apply(e,t||[])).next())}))}class c extends t.Component{constructor(){super(...arguments),this.text="",this.style={}}init(t){const n=new e.TextStyle({fontSize:20}),i={};for(const e in n)0===e.indexOf("_")&&(i[e.substring(1)]=n[e]);delete i.styleKey,this.style=i,t&&(this.text=t.text,Object.assign(this.style,t.style))}}c.componentName="Text",o([n.type("string")],c.prototype,"text",void 0);class a extends t.Component{constructor(){super(...arguments),this.text="",this.style={},this.textureStyle={}}init(e){this.style=Object.assign({fontSize:24,fill:"#000000",fontFamily:"Arial"},null==e?void 0:e.style),this.textureStyle=Object.assign({scaleMode:"linear",resolution:window.devicePixelRatio||1},null==e?void 0:e.textureStyle),e&&(this.text=e.text)}}a.componentName="HTMLText",o([n.type("string")],a.prototype,"text",void 0);let d=class extends i.Renderer{constructor(){super(...arguments),this.name="Text",this.texts={}}init(){this.renderSystem=this.game.getSystem(i.RendererSystem),this.renderSystem.rendererManager.register(this)}componentChanged(e){return r(this,void 0,void 0,(function*(){const n="Text"===e.componentName,i="HTMLText"===e.componentName;if(n||i)if(e.type===t.OBSERVER_TYPE.ADD)n?yield this.addTextComponent(e):yield this.addHTMLTextComponent(e),this.setSize(e);else if(e.type===t.OBSERVER_TYPE.REMOVE)this.containerManager.getContainer(e.gameObject.id).removeChild(this.texts[e.gameObject.id].text),this.texts[e.gameObject.id].text.destroy({children:!0}),delete this.texts[e.gameObject.id];else{this.change(e);const t=e.component;if("style"===e.prop.prop[0]&&t.style&&t.style.fontFamily){const{text:n}=this.texts[e.gameObject.id];yield this.waitForFontResource(n,e,t.style.fontFamily)}this.setSize(e)}}))}addTextComponent(e){return r(this,void 0,void 0,(function*(){const t=e.component,n=Object.assign({},t.style),i=n.fontFamily;delete n.fontFamily;const o=i?"":t.text,r=new s.Text(o,n);this.containerManager.getContainer(e.gameObject.id).addChildAt(r,0),this.texts[e.gameObject.id]={text:r,component:t},i&&(yield this.waitForFontResource(r,e,i))}))}addHTMLTextComponent(e){return r(this,void 0,void 0,(function*(){const t=e.component,n=Object.assign({},t.style),i=n.fontFamily;delete n.fontFamily;const o=i?"":t.text,r=new s.HTMLText(Object.assign({text:o,style:n},t.textureStyle&&{textureStyle:t.textureStyle}));this.containerManager.getContainer(e.gameObject.id).addChildAt(r,0),this.texts[e.gameObject.id]={text:r,component:t},i&&(yield this.waitForFontResource(r,e,i))}))}waitForFontResource(e,n,i){return r(this,void 0,void 0,(function*(){if(i)try{const s=Array.isArray(i)?i[0]:i,o=this.increaseAsyncId(n.gameObject.id);if(yield t.resource.getResource(s),!this.validateAsyncId(n.gameObject.id,o))return;const r=this.texts[n.gameObject.id].component;e.style.fontFamily=i,e.text=r.text}catch(e){console.warn(`字体资源 ${i} 加载失败:`,e)}}))}change(e){const{text:t,component:n}=this.texts[e.gameObject.id],i="HTMLText"===e.componentName;if("text"===e.prop.prop[0])t.text=n.text;else if("style"===e.prop.prop[0])Object.assign(t.style,n.style);else if("textureStyle"===e.prop.prop[0]&&i){const i=n,o=this.containerManager.getContainer(e.gameObject.id),r=o.getChildIndex(t);o.removeChild(t),t.destroy({children:!0});const c=new s.HTMLText({text:i.text,style:i.style,textureStyle:i.textureStyle});o.addChildAt(c,r),this.texts[e.gameObject.id].text=c}}setSize(e){const{transform:t}=e.gameObject;if(!t)return;const{text:n}=this.texts[e.gameObject.id],i=n.getSize();t.size.width=i.width,t.size.height=i.height}};d.systemName="Text",d=o([t.decorators.componentObserver({Text:["text",{prop:["style"],deep:!0}],HTMLText:["text",{prop:["style"],deep:!0},{prop:["textureStyle"],deep:!0}]})],d);var l=d;exports.HTMLText=a,exports.Text=c,exports.TextSystem=l;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Component } from '@eva/eva.js';
|
|
2
2
|
import { ComponentChanged } from '@eva/eva.js';
|
|
3
3
|
import { ContainerManager } from '@eva/plugin-renderer';
|
|
4
|
+
import { HTMLText as HTMLText_2 } from '@eva/renderer-adapter';
|
|
4
5
|
import { Renderer } from '@eva/plugin-renderer';
|
|
5
6
|
import { RendererManager } from '@eva/plugin-renderer';
|
|
6
7
|
import { RendererSystem } from '@eva/plugin-renderer';
|
|
@@ -12,10 +13,180 @@ import { TextStyleFontWeight } from 'pixi.js';
|
|
|
12
13
|
import { TextStyleTextBaseline } from 'pixi.js';
|
|
13
14
|
import { TextStyleWhiteSpace } from 'pixi.js';
|
|
14
15
|
|
|
16
|
+
/**
|
|
17
|
+
* HTML 富文本组件
|
|
18
|
+
*
|
|
19
|
+
* HTMLText 组件支持渲染带有 HTML 标签的富文本内容。
|
|
20
|
+
* 可以在文本中使用 HTML 标签(如 `<b>`, `<i>`, `<span>` 等)来实现丰富的文本样式,
|
|
21
|
+
* 适用于聊天对话、新闻内容、富文本显示等需要多样式文本的场景。
|
|
22
|
+
*
|
|
23
|
+
* 支持的 HTML 标签:
|
|
24
|
+
* - `<b>` - 粗体
|
|
25
|
+
* - `<i>` - 斜体
|
|
26
|
+
* - `<span style="color:#ff0000">` - 自定义样式
|
|
27
|
+
* - `<br>` - 换行
|
|
28
|
+
* 以及更多标准 HTML 文本标签
|
|
29
|
+
*
|
|
30
|
+
* @example
|
|
31
|
+
* ```typescript
|
|
32
|
+
* // 基础富文本
|
|
33
|
+
* const label = new GameObject('label');
|
|
34
|
+
* label.addComponent(new HTMLText({
|
|
35
|
+
* text: '这是<b>粗体</b>和<i>斜体</i>文本',
|
|
36
|
+
* style: {
|
|
37
|
+
* fontSize: 24,
|
|
38
|
+
* fill: '#000000',
|
|
39
|
+
* fontFamily: 'Arial'
|
|
40
|
+
* }
|
|
41
|
+
* }));
|
|
42
|
+
*
|
|
43
|
+
* // 带颜色的富文本
|
|
44
|
+
* label.addComponent(new HTMLText({
|
|
45
|
+
* text: '欢迎 <span style="color:#ff0000">玩家123</span> 加入游戏!',
|
|
46
|
+
* style: {
|
|
47
|
+
* fontSize: 20,
|
|
48
|
+
* wordWrap: true,
|
|
49
|
+
* wordWrapWidth: 300
|
|
50
|
+
* }
|
|
51
|
+
* }));
|
|
52
|
+
*
|
|
53
|
+
* // 自定义标签样式
|
|
54
|
+
* label.addComponent(new HTMLText({
|
|
55
|
+
* text: '获得 <gold>100</gold> 金币',
|
|
56
|
+
* style: {
|
|
57
|
+
* fontSize: 18,
|
|
58
|
+
* tagStyles: {
|
|
59
|
+
* gold: {
|
|
60
|
+
* fill: '#ffd700',
|
|
61
|
+
* fontWeight: 'bold'
|
|
62
|
+
* }
|
|
63
|
+
* }
|
|
64
|
+
* }
|
|
65
|
+
* }));
|
|
66
|
+
*
|
|
67
|
+
* // 高分辨率渲染(推荐使用 Render 组件的 resolution 属性)
|
|
68
|
+
* label.addComponent(new HTMLText({
|
|
69
|
+
* text: '高清文本',
|
|
70
|
+
* textureStyle: {
|
|
71
|
+
* scaleMode: 'linear'
|
|
72
|
+
* }
|
|
73
|
+
* }));
|
|
74
|
+
* label.addComponent(new Render({ resolution: 2 }));
|
|
75
|
+
* ```
|
|
76
|
+
*/
|
|
77
|
+
export declare class HTMLText extends Component<HTMLTextParams> {
|
|
78
|
+
/** 组件名称 */
|
|
79
|
+
static componentName: string;
|
|
80
|
+
/** 富文本内容(支持 HTML 标签) */
|
|
81
|
+
text: string;
|
|
82
|
+
/** 文本样式配置 */
|
|
83
|
+
style: HTMLTextParams['style'];
|
|
84
|
+
/** 纹理渲染配置 */
|
|
85
|
+
textureStyle: HTMLTextParams['textureStyle'];
|
|
86
|
+
/**
|
|
87
|
+
* 初始化组件
|
|
88
|
+
* @param obj - 初始化参数
|
|
89
|
+
* @param obj.text - 富文本内容
|
|
90
|
+
* @param obj.style - 文本样式
|
|
91
|
+
* @param obj.textureStyle - 纹理配置
|
|
92
|
+
*/
|
|
93
|
+
init(obj?: HTMLTextParams): void;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export declare interface HTMLTextParams {
|
|
97
|
+
text: string;
|
|
98
|
+
style?: HTMLTextStyleOptions & {
|
|
99
|
+
cssOverrides?: string[];
|
|
100
|
+
wordWrap?: boolean;
|
|
101
|
+
wordWrapWidth?: number;
|
|
102
|
+
tagStyles?: Record<string, HTMLTextStyleOptions>;
|
|
103
|
+
};
|
|
104
|
+
textureStyle?: {
|
|
105
|
+
scaleMode?: 'linear' | 'nearest';
|
|
106
|
+
resolution?: number;
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
declare interface HTMLTextStyleOptions {
|
|
111
|
+
fontFamily?: string | string[];
|
|
112
|
+
fontSize?: number | string;
|
|
113
|
+
fill?: string | number;
|
|
114
|
+
align?: 'left' | 'center' | 'right' | 'justify';
|
|
115
|
+
breakWords?: boolean;
|
|
116
|
+
letterSpacing?: number;
|
|
117
|
+
lineHeight?: number;
|
|
118
|
+
padding?: number;
|
|
119
|
+
stroke?: string | number;
|
|
120
|
+
strokeThickness?: number;
|
|
121
|
+
dropShadow?: boolean;
|
|
122
|
+
dropShadowAlpha?: number;
|
|
123
|
+
dropShadowAngle?: number;
|
|
124
|
+
dropShadowBlur?: number;
|
|
125
|
+
dropShadowColor?: string | number;
|
|
126
|
+
dropShadowDistance?: number;
|
|
127
|
+
trim?: boolean;
|
|
128
|
+
fontWeight?: string;
|
|
129
|
+
fontStyle?: string;
|
|
130
|
+
fontVariant?: string;
|
|
131
|
+
textBaseline?: string;
|
|
132
|
+
whiteSpace?: string;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* 文本组件(基于 PixiJS Text)
|
|
137
|
+
*
|
|
138
|
+
* Text 组件用于渲染文本内容,支持丰富的文本样式配置。
|
|
139
|
+
* 它基于 PixiJS 的 Text 实现,支持字体、颜色、描边、阴影、对齐等多种样式。
|
|
140
|
+
*
|
|
141
|
+
* 主要特性:
|
|
142
|
+
* - 支持多种字体和字号
|
|
143
|
+
* - 支持文本颜色、渐变填充
|
|
144
|
+
* - 支持描边和投影效果
|
|
145
|
+
* - 支持文本对齐和换行
|
|
146
|
+
*
|
|
147
|
+
* @example
|
|
148
|
+
* ```typescript
|
|
149
|
+
* // 基础文本
|
|
150
|
+
* const label = new GameObject('label');
|
|
151
|
+
* label.addComponent(new Text({
|
|
152
|
+
* text: 'Hello EVA!',
|
|
153
|
+
* style: {
|
|
154
|
+
* fontSize: 32,
|
|
155
|
+
* fill: 0xffffff
|
|
156
|
+
* }
|
|
157
|
+
* }));
|
|
158
|
+
*
|
|
159
|
+
* // 带样式的文本
|
|
160
|
+
* label.addComponent(new Text({
|
|
161
|
+
* text: '得分: 9999',
|
|
162
|
+
* style: {
|
|
163
|
+
* fontFamily: 'Arial',
|
|
164
|
+
* fontSize: 48,
|
|
165
|
+
* fontWeight: 'bold',
|
|
166
|
+
* fill: ['#ff0000', '#ffff00'], // 渐变色
|
|
167
|
+
* stroke: '#000000',
|
|
168
|
+
* strokeThickness: 4,
|
|
169
|
+
* dropShadow: true,
|
|
170
|
+
* dropShadowDistance: 3
|
|
171
|
+
* }
|
|
172
|
+
* }));
|
|
173
|
+
* // 如需高清渲染,使用 Render 组件的 resolution 属性
|
|
174
|
+
* label.addComponent(new Render({ resolution: 2 }));
|
|
175
|
+
* ```
|
|
176
|
+
*/
|
|
15
177
|
declare class Text_2 extends Component<TextParams> {
|
|
178
|
+
/** 组件名称 */
|
|
16
179
|
static componentName: string;
|
|
180
|
+
/** 文本内容 */
|
|
17
181
|
text: string;
|
|
182
|
+
/** 文本样式配置 */
|
|
18
183
|
style: TextParams['style'];
|
|
184
|
+
/**
|
|
185
|
+
* 初始化组件
|
|
186
|
+
* @param obj - 初始化参数
|
|
187
|
+
* @param obj.text - 文本内容
|
|
188
|
+
* @param obj.style - 文本样式
|
|
189
|
+
*/
|
|
19
190
|
init(obj?: TextParams): void;
|
|
20
191
|
}
|
|
21
192
|
export { Text_2 as Text }
|
|
@@ -60,8 +231,8 @@ export declare class TextSystem extends Renderer {
|
|
|
60
231
|
name: string;
|
|
61
232
|
texts: {
|
|
62
233
|
[propName: number]: {
|
|
63
|
-
text: Text_3;
|
|
64
|
-
component: Text_2;
|
|
234
|
+
text: Text_3 | HTMLText_2;
|
|
235
|
+
component: Text_2 | HTMLText;
|
|
65
236
|
};
|
|
66
237
|
};
|
|
67
238
|
renderSystem: RendererSystem;
|
|
@@ -69,6 +240,12 @@ export declare class TextSystem extends Renderer {
|
|
|
69
240
|
containerManager: ContainerManager;
|
|
70
241
|
init(): void;
|
|
71
242
|
componentChanged(changed: ComponentChanged): Promise<void>;
|
|
243
|
+
private addTextComponent;
|
|
244
|
+
private addHTMLTextComponent;
|
|
245
|
+
/**
|
|
246
|
+
* 等待字体资源加载完成并更新文本
|
|
247
|
+
*/
|
|
248
|
+
private waitForFontResource;
|
|
72
249
|
change(changed: ComponentChanged): void;
|
|
73
250
|
setSize(changed: ComponentChanged): void;
|
|
74
251
|
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { TextStyle } from 'pixi.js';
|
|
2
|
-
import { Component, decorators, OBSERVER_TYPE } from '@eva/eva.js';
|
|
2
|
+
import { Component, decorators, OBSERVER_TYPE, resource } from '@eva/eva.js';
|
|
3
3
|
import { type } from '@eva/inspector-decorator';
|
|
4
4
|
import { Renderer, RendererSystem } from '@eva/plugin-renderer';
|
|
5
|
-
import { Text as Text$3 } from '@eva/renderer-adapter';
|
|
5
|
+
import { HTMLText as HTMLText$1, Text as Text$3 } from '@eva/renderer-adapter';
|
|
6
6
|
|
|
7
7
|
/*! *****************************************************************************
|
|
8
8
|
Copyright (c) Microsoft Corporation. All rights reserved.
|
|
@@ -35,12 +35,63 @@ function __awaiter(thisArg, _arguments, P, generator) {
|
|
|
35
35
|
});
|
|
36
36
|
}
|
|
37
37
|
|
|
38
|
+
/**
|
|
39
|
+
* 文本组件(基于 PixiJS Text)
|
|
40
|
+
*
|
|
41
|
+
* Text 组件用于渲染文本内容,支持丰富的文本样式配置。
|
|
42
|
+
* 它基于 PixiJS 的 Text 实现,支持字体、颜色、描边、阴影、对齐等多种样式。
|
|
43
|
+
*
|
|
44
|
+
* 主要特性:
|
|
45
|
+
* - 支持多种字体和字号
|
|
46
|
+
* - 支持文本颜色、渐变填充
|
|
47
|
+
* - 支持描边和投影效果
|
|
48
|
+
* - 支持文本对齐和换行
|
|
49
|
+
*
|
|
50
|
+
* @example
|
|
51
|
+
* ```typescript
|
|
52
|
+
* // 基础文本
|
|
53
|
+
* const label = new GameObject('label');
|
|
54
|
+
* label.addComponent(new Text({
|
|
55
|
+
* text: 'Hello EVA!',
|
|
56
|
+
* style: {
|
|
57
|
+
* fontSize: 32,
|
|
58
|
+
* fill: 0xffffff
|
|
59
|
+
* }
|
|
60
|
+
* }));
|
|
61
|
+
*
|
|
62
|
+
* // 带样式的文本
|
|
63
|
+
* label.addComponent(new Text({
|
|
64
|
+
* text: '得分: 9999',
|
|
65
|
+
* style: {
|
|
66
|
+
* fontFamily: 'Arial',
|
|
67
|
+
* fontSize: 48,
|
|
68
|
+
* fontWeight: 'bold',
|
|
69
|
+
* fill: ['#ff0000', '#ffff00'], // 渐变色
|
|
70
|
+
* stroke: '#000000',
|
|
71
|
+
* strokeThickness: 4,
|
|
72
|
+
* dropShadow: true,
|
|
73
|
+
* dropShadowDistance: 3
|
|
74
|
+
* }
|
|
75
|
+
* }));
|
|
76
|
+
* // 如需高清渲染,使用 Render 组件的 resolution 属性
|
|
77
|
+
* label.addComponent(new Render({ resolution: 2 }));
|
|
78
|
+
* ```
|
|
79
|
+
*/
|
|
38
80
|
class Text$2 extends Component {
|
|
39
81
|
constructor() {
|
|
40
82
|
super(...arguments);
|
|
83
|
+
/** 文本内容 */
|
|
41
84
|
this.text = '';
|
|
85
|
+
/** 文本样式配置 */
|
|
86
|
+
// @decorators.IDEProp 复杂编辑后续添加
|
|
42
87
|
this.style = {};
|
|
43
88
|
}
|
|
89
|
+
/**
|
|
90
|
+
* 初始化组件
|
|
91
|
+
* @param obj - 初始化参数
|
|
92
|
+
* @param obj.text - 文本内容
|
|
93
|
+
* @param obj.style - 文本样式
|
|
94
|
+
*/
|
|
44
95
|
init(obj) {
|
|
45
96
|
const style = new TextStyle({
|
|
46
97
|
fontSize: 20,
|
|
@@ -59,11 +110,104 @@ class Text$2 extends Component {
|
|
|
59
110
|
}
|
|
60
111
|
}
|
|
61
112
|
}
|
|
113
|
+
/** 组件名称 */
|
|
62
114
|
Text$2.componentName = 'Text';
|
|
63
115
|
__decorate([
|
|
64
116
|
type('string')
|
|
65
117
|
], Text$2.prototype, "text", void 0);
|
|
66
118
|
|
|
119
|
+
/**
|
|
120
|
+
* HTML 富文本组件
|
|
121
|
+
*
|
|
122
|
+
* HTMLText 组件支持渲染带有 HTML 标签的富文本内容。
|
|
123
|
+
* 可以在文本中使用 HTML 标签(如 `<b>`, `<i>`, `<span>` 等)来实现丰富的文本样式,
|
|
124
|
+
* 适用于聊天对话、新闻内容、富文本显示等需要多样式文本的场景。
|
|
125
|
+
*
|
|
126
|
+
* 支持的 HTML 标签:
|
|
127
|
+
* - `<b>` - 粗体
|
|
128
|
+
* - `<i>` - 斜体
|
|
129
|
+
* - `<span style="color:#ff0000">` - 自定义样式
|
|
130
|
+
* - `<br>` - 换行
|
|
131
|
+
* 以及更多标准 HTML 文本标签
|
|
132
|
+
*
|
|
133
|
+
* @example
|
|
134
|
+
* ```typescript
|
|
135
|
+
* // 基础富文本
|
|
136
|
+
* const label = new GameObject('label');
|
|
137
|
+
* label.addComponent(new HTMLText({
|
|
138
|
+
* text: '这是<b>粗体</b>和<i>斜体</i>文本',
|
|
139
|
+
* style: {
|
|
140
|
+
* fontSize: 24,
|
|
141
|
+
* fill: '#000000',
|
|
142
|
+
* fontFamily: 'Arial'
|
|
143
|
+
* }
|
|
144
|
+
* }));
|
|
145
|
+
*
|
|
146
|
+
* // 带颜色的富文本
|
|
147
|
+
* label.addComponent(new HTMLText({
|
|
148
|
+
* text: '欢迎 <span style="color:#ff0000">玩家123</span> 加入游戏!',
|
|
149
|
+
* style: {
|
|
150
|
+
* fontSize: 20,
|
|
151
|
+
* wordWrap: true,
|
|
152
|
+
* wordWrapWidth: 300
|
|
153
|
+
* }
|
|
154
|
+
* }));
|
|
155
|
+
*
|
|
156
|
+
* // 自定义标签样式
|
|
157
|
+
* label.addComponent(new HTMLText({
|
|
158
|
+
* text: '获得 <gold>100</gold> 金币',
|
|
159
|
+
* style: {
|
|
160
|
+
* fontSize: 18,
|
|
161
|
+
* tagStyles: {
|
|
162
|
+
* gold: {
|
|
163
|
+
* fill: '#ffd700',
|
|
164
|
+
* fontWeight: 'bold'
|
|
165
|
+
* }
|
|
166
|
+
* }
|
|
167
|
+
* }
|
|
168
|
+
* }));
|
|
169
|
+
*
|
|
170
|
+
* // 高分辨率渲染(推荐使用 Render 组件的 resolution 属性)
|
|
171
|
+
* label.addComponent(new HTMLText({
|
|
172
|
+
* text: '高清文本',
|
|
173
|
+
* textureStyle: {
|
|
174
|
+
* scaleMode: 'linear'
|
|
175
|
+
* }
|
|
176
|
+
* }));
|
|
177
|
+
* label.addComponent(new Render({ resolution: 2 }));
|
|
178
|
+
* ```
|
|
179
|
+
*/
|
|
180
|
+
class HTMLText extends Component {
|
|
181
|
+
constructor() {
|
|
182
|
+
super(...arguments);
|
|
183
|
+
/** 富文本内容(支持 HTML 标签) */
|
|
184
|
+
this.text = '';
|
|
185
|
+
/** 文本样式配置 */
|
|
186
|
+
this.style = {};
|
|
187
|
+
/** 纹理渲染配置 */
|
|
188
|
+
this.textureStyle = {};
|
|
189
|
+
}
|
|
190
|
+
/**
|
|
191
|
+
* 初始化组件
|
|
192
|
+
* @param obj - 初始化参数
|
|
193
|
+
* @param obj.text - 富文本内容
|
|
194
|
+
* @param obj.style - 文本样式
|
|
195
|
+
* @param obj.textureStyle - 纹理配置
|
|
196
|
+
*/
|
|
197
|
+
init(obj) {
|
|
198
|
+
this.style = Object.assign({ fontSize: 24, fill: '#000000', fontFamily: 'Arial' }, obj === null || obj === void 0 ? void 0 : obj.style);
|
|
199
|
+
this.textureStyle = Object.assign({ scaleMode: 'linear', resolution: window.devicePixelRatio || 1 }, obj === null || obj === void 0 ? void 0 : obj.textureStyle);
|
|
200
|
+
if (obj) {
|
|
201
|
+
this.text = obj.text;
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
/** 组件名称 */
|
|
206
|
+
HTMLText.componentName = 'HTMLText';
|
|
207
|
+
__decorate([
|
|
208
|
+
type('string')
|
|
209
|
+
], HTMLText.prototype, "text", void 0);
|
|
210
|
+
|
|
67
211
|
let Text = class Text extends Renderer {
|
|
68
212
|
constructor() {
|
|
69
213
|
super(...arguments);
|
|
@@ -76,16 +220,17 @@ let Text = class Text extends Renderer {
|
|
|
76
220
|
}
|
|
77
221
|
componentChanged(changed) {
|
|
78
222
|
return __awaiter(this, void 0, void 0, function* () {
|
|
79
|
-
|
|
223
|
+
const isText = changed.componentName === 'Text';
|
|
224
|
+
const isHTMLText = changed.componentName === 'HTMLText';
|
|
225
|
+
if (!isText && !isHTMLText)
|
|
80
226
|
return;
|
|
81
227
|
if (changed.type === OBSERVER_TYPE.ADD) {
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
};
|
|
228
|
+
if (isText) {
|
|
229
|
+
yield this.addTextComponent(changed);
|
|
230
|
+
}
|
|
231
|
+
else {
|
|
232
|
+
yield this.addHTMLTextComponent(changed);
|
|
233
|
+
}
|
|
89
234
|
this.setSize(changed);
|
|
90
235
|
}
|
|
91
236
|
else if (changed.type === OBSERVER_TYPE.REMOVE) {
|
|
@@ -95,33 +240,125 @@ let Text = class Text extends Renderer {
|
|
|
95
240
|
}
|
|
96
241
|
else {
|
|
97
242
|
this.change(changed);
|
|
243
|
+
// 如果样式改变且涉及字体,也需要等待字体资源加载
|
|
244
|
+
const component = changed.component;
|
|
245
|
+
if (changed.prop.prop[0] === 'style' && component.style && component.style.fontFamily) {
|
|
246
|
+
const { text } = this.texts[changed.gameObject.id];
|
|
247
|
+
yield this.waitForFontResource(text, changed, component.style.fontFamily);
|
|
248
|
+
}
|
|
98
249
|
this.setSize(changed);
|
|
99
250
|
}
|
|
100
251
|
});
|
|
101
252
|
}
|
|
253
|
+
addTextComponent(changed) {
|
|
254
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
255
|
+
const component = changed.component;
|
|
256
|
+
// 创建文本样式副本,先不设置 fontFamily
|
|
257
|
+
const styleWithoutFont = Object.assign({}, component.style);
|
|
258
|
+
const fontFamily = styleWithoutFont.fontFamily;
|
|
259
|
+
delete styleWithoutFont.fontFamily;
|
|
260
|
+
const initialText = fontFamily ? '' : component.text;
|
|
261
|
+
const text = new Text$3(initialText, styleWithoutFont);
|
|
262
|
+
this.containerManager.getContainer(changed.gameObject.id).addChildAt(text, 0);
|
|
263
|
+
this.texts[changed.gameObject.id] = {
|
|
264
|
+
text,
|
|
265
|
+
component,
|
|
266
|
+
};
|
|
267
|
+
// 如果指定了字体资源,等待资源加载完成后设置 fontFamily
|
|
268
|
+
if (fontFamily) {
|
|
269
|
+
yield this.waitForFontResource(text, changed, fontFamily);
|
|
270
|
+
}
|
|
271
|
+
});
|
|
272
|
+
}
|
|
273
|
+
addHTMLTextComponent(changed) {
|
|
274
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
275
|
+
const component = changed.component;
|
|
276
|
+
// 创建样式副本,先不设置 fontFamily
|
|
277
|
+
const styleWithoutFont = Object.assign({}, component.style);
|
|
278
|
+
const fontFamily = styleWithoutFont.fontFamily;
|
|
279
|
+
delete styleWithoutFont.fontFamily;
|
|
280
|
+
const initialText = fontFamily ? '' : component.text;
|
|
281
|
+
const htmlText = new HTMLText$1(Object.assign({ text: initialText, style: styleWithoutFont }, (component.textureStyle && { textureStyle: component.textureStyle })));
|
|
282
|
+
this.containerManager.getContainer(changed.gameObject.id).addChildAt(htmlText, 0);
|
|
283
|
+
this.texts[changed.gameObject.id] = {
|
|
284
|
+
text: htmlText,
|
|
285
|
+
component,
|
|
286
|
+
};
|
|
287
|
+
// 如果指定了字体资源,等待资源加载完成后设置 fontFamily
|
|
288
|
+
if (fontFamily) {
|
|
289
|
+
yield this.waitForFontResource(htmlText, changed, fontFamily);
|
|
290
|
+
}
|
|
291
|
+
});
|
|
292
|
+
}
|
|
293
|
+
/**
|
|
294
|
+
* 等待字体资源加载完成并更新文本
|
|
295
|
+
*/
|
|
296
|
+
waitForFontResource(text, changed, fontFamily) {
|
|
297
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
298
|
+
if (!fontFamily) {
|
|
299
|
+
return;
|
|
300
|
+
}
|
|
301
|
+
try {
|
|
302
|
+
const fontName = Array.isArray(fontFamily) ? fontFamily[0] : fontFamily;
|
|
303
|
+
// 通过 resource 系统获取字体资源
|
|
304
|
+
const asyncId = this.increaseAsyncId(changed.gameObject.id);
|
|
305
|
+
yield resource.getResource(fontName);
|
|
306
|
+
// 验证异步操作是否仍然有效(防止组件已被移除)
|
|
307
|
+
if (!this.validateAsyncId(changed.gameObject.id, asyncId))
|
|
308
|
+
return;
|
|
309
|
+
// 字体资源加载成功后,设置 fontFamily 并重新设置文本内容以触发重新渲染
|
|
310
|
+
const component = this.texts[changed.gameObject.id].component;
|
|
311
|
+
text.style.fontFamily = fontFamily;
|
|
312
|
+
text.text = component.text;
|
|
313
|
+
// 更新尺寸
|
|
314
|
+
}
|
|
315
|
+
catch (error) {
|
|
316
|
+
console.warn(`字体资源 ${fontFamily} 加载失败:`, error);
|
|
317
|
+
}
|
|
318
|
+
});
|
|
319
|
+
}
|
|
102
320
|
change(changed) {
|
|
103
321
|
const { text, component } = this.texts[changed.gameObject.id];
|
|
322
|
+
const isHTMLText = changed.componentName === 'HTMLText';
|
|
104
323
|
if (changed.prop.prop[0] === 'text') {
|
|
105
324
|
text.text = component.text;
|
|
106
325
|
}
|
|
107
326
|
else if (changed.prop.prop[0] === 'style') {
|
|
108
|
-
Object.assign(text.style,
|
|
327
|
+
Object.assign(text.style, component.style);
|
|
328
|
+
}
|
|
329
|
+
else if (changed.prop.prop[0] === 'textureStyle' && isHTMLText) {
|
|
330
|
+
// HTMLText 纹理样式变化需要重新创建
|
|
331
|
+
const htmlComponent = component;
|
|
332
|
+
const container = this.containerManager.getContainer(changed.gameObject.id);
|
|
333
|
+
const index = container.getChildIndex(text);
|
|
334
|
+
container.removeChild(text);
|
|
335
|
+
text.destroy({ children: true });
|
|
336
|
+
const newText = new HTMLText$1({
|
|
337
|
+
text: htmlComponent.text,
|
|
338
|
+
style: htmlComponent.style,
|
|
339
|
+
textureStyle: htmlComponent.textureStyle
|
|
340
|
+
});
|
|
341
|
+
container.addChildAt(newText, index);
|
|
342
|
+
this.texts[changed.gameObject.id].text = newText;
|
|
109
343
|
}
|
|
110
344
|
}
|
|
111
345
|
setSize(changed) {
|
|
112
346
|
const { transform } = changed.gameObject;
|
|
113
347
|
if (!transform)
|
|
114
348
|
return;
|
|
115
|
-
|
|
116
|
-
|
|
349
|
+
const { text } = this.texts[changed.gameObject.id];
|
|
350
|
+
const size = text.getSize();
|
|
351
|
+
transform.size.width = size.width;
|
|
352
|
+
transform.size.height = size.height;
|
|
117
353
|
}
|
|
118
354
|
};
|
|
119
355
|
Text.systemName = 'Text';
|
|
120
356
|
Text = __decorate([
|
|
121
357
|
decorators.componentObserver({
|
|
122
358
|
Text: ['text', { prop: ['style'], deep: true }],
|
|
359
|
+
HTMLText: ['text', { prop: ['style'], deep: true }, { prop: ['textureStyle'], deep: true }],
|
|
123
360
|
})
|
|
124
361
|
], Text);
|
|
125
362
|
var Text$1 = Text;
|
|
126
363
|
|
|
127
|
-
export { Text$2 as Text, Text$1 as TextSystem };
|
|
364
|
+
export { HTMLText, Text$2 as Text, Text$1 as TextSystem };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@eva/plugin-renderer-text",
|
|
3
|
-
"version": "2.0.1-beta.
|
|
3
|
+
"version": "2.0.1-beta.30",
|
|
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": "^0.0.5",
|
|
22
|
-
"@eva/plugin-renderer": "2.0.1-beta.
|
|
23
|
-
"@eva/renderer-adapter": "2.0.1-beta.
|
|
24
|
-
"@eva/eva.js": "2.0.1-beta.
|
|
22
|
+
"@eva/plugin-renderer": "2.0.1-beta.30",
|
|
23
|
+
"@eva/renderer-adapter": "2.0.1-beta.30",
|
|
24
|
+
"@eva/eva.js": "2.0.1-beta.30",
|
|
25
25
|
"pixi.js": "^8.8.1"
|
|
26
26
|
}
|
|
27
27
|
}
|