PixiJS 文本处理 #
文本渲染概述 #
PixiJS 提供两种文本渲染方式:Text 和 BitmapText。
text
┌─────────────────────────────────────────────────────────────┐
│ 文本渲染方式 │
├─────────────────────────────────────────────────────────────┤
│ │
│ Text(矢量文本) │
│ ├── 使用系统字体或 Web 字体 │
│ ├── 支持丰富的样式 │
│ ├── 动态生成纹理 │
│ └── 适合文本内容变化的场景 │
│ │
│ BitmapText(位图文本) │
│ ├── 使用预渲染的位图字体 │
│ ├── 性能更高 │
│ ├── 需要提前生成字体文件 │
│ └── 适合大量文本或频繁更新的场景 │
│ │
└─────────────────────────────────────────────────────────────┘
Text(矢量文本) #
创建文本 #
javascript
import { Text } from 'pixi.js';
// 简单创建
const text = new Text({ text: 'Hello PixiJS!' });
app.stage.addChild(text);
// 带样式创建
const styledText = new Text({
text: 'Hello PixiJS!',
style: {
fontFamily: 'Arial',
fontSize: 36,
fill: 0xffffff
}
});
app.stage.addChild(styledText);
文本样式 #
javascript
const text = new Text({
text: 'Styled Text',
style: {
// 字体
fontFamily: 'Arial',
fontSize: 48,
fontWeight: 'bold',
fontStyle: 'italic',
// 颜色
fill: 0xff9900,
// 描边
stroke: {
color: 0x000000,
width: 2
},
// 阴影
dropShadow: {
color: 0x000000,
alpha: 0.5,
blur: 4,
distance: 4,
angle: Math.PI / 4
},
// 对齐
align: 'center',
// 行高和字间距
lineHeight: 60,
letterSpacing: 2,
// 换行
wordWrap: true,
wordWrapWidth: 300,
breakWords: false,
// 省略
textBaseline: 'middle',
trim: false,
whiteSpace: 'pre-wrap'
}
});
动态更新 #
javascript
const text = new Text({
text: 'Score: 0',
style: {
fontFamily: 'Arial',
fontSize: 36,
fill: 0xffffff
}
});
// 更新文本内容
text.text = 'Score: 100';
// 更新样式
text.style.fill = 0xff0000;
text.style.fontSize = 48;
// 强制更新纹理
text._textureID = -1;
文本属性 #
javascript
const text = new Text({ text: 'Hello' });
// 位置
text.x = 100;
text.y = 100;
text.anchor.set(0.5);
// 尺寸
console.log(text.width);
console.log(text.height);
// 分辨率
text.resolution = 2;
// 自动分辨率
text.autoResolution = true;
多行文本 #
javascript
const text = new Text({
text: 'This is a long text that will wrap to multiple lines when word wrap is enabled.',
style: {
fontFamily: 'Arial',
fontSize: 24,
fill: 0xffffff,
wordWrap: true,
wordWrapWidth: 300,
align: 'center',
lineHeight: 36
}
});
渐变填充 #
javascript
const text = new Text({
text: 'Gradient Text',
style: {
fontFamily: 'Arial',
fontSize: 64,
fill: {
type: 'linear',
x1: 0, y1: 0,
x2: 0, y2: 1,
stops: [
{ offset: 0, color: 0xff0000 },
{ offset: 0.5, color: 0xffff00 },
{ offset: 1, color: 0x00ff00 }
]
}
}
});
使用 Web 字体 #
javascript
// 方法1:CSS 加载字体
// 在 HTML 中添加:
// <link href="https://fonts.googleapis.com/css2?family=Roboto&display=swap" rel="stylesheet">
// 方法2:使用 FontFace API
async function loadFont() {
const font = new FontFace('CustomFont', 'url(/fonts/custom.woff2)');
await font.load();
document.fonts.add(font);
const text = new Text({
text: 'Custom Font',
style: {
fontFamily: 'CustomFont',
fontSize: 48
}
});
}
await loadFont();
使用 Assets 加载字体 #
javascript
import { Assets } from 'pixi.js';
// 加载字体
await Assets.load({
src: 'https://fonts.googleapis.com/css2?family=Roboto&display=swap',
data: {
family: 'Roboto',
display: 'swap'
}
});
const text = new Text({
text: 'Roboto Font',
style: {
fontFamily: 'Roboto',
fontSize: 36
}
});
BitmapText(位图文本) #
什么是位图字体 #
位图字体是将字符预渲染为纹理,运行时直接使用这些纹理拼接文本,性能更高。
text
┌─────────────────────────────────────────────────────────────┐
│ 位图字体结构 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 字体文件 (.fnt 或 .xml) │
│ ├── 字符映射信息 │
│ ├── 字符位置和尺寸 │
│ └── 字符间距信息 │
│ │
│ 纹理文件 (.png) │
│ └── 所有字符的预渲染图像 │
│ │
└─────────────────────────────────────────────────────────────┘
加载位图字体 #
javascript
import { BitmapText, Assets } from 'pixi.js';
// 加载位图字体
await Assets.load('fonts/desycel.xml');
// 创建位图文本
const bitmapText = new BitmapText({
text: 'Hello Bitmap!',
style: {
fontFamily: 'Desycel',
fontSize: 48
}
});
app.stage.addChild(bitmapText);
BitmapText 样式 #
javascript
const bitmapText = new BitmapText({
text: 'Bitmap Text',
style: {
fontFamily: 'Desycel',
fontSize: 48,
// 对齐
align: 'center',
// 字母间距
letterSpacing: 2,
// 最大宽度
maxWidth: 300
}
});
动态更新 #
javascript
const bitmapText = new BitmapText({
text: 'Score: 0',
style: {
fontFamily: 'Desycel',
fontSize: 36
}
});
// 更新文本
bitmapText.text = 'Score: 100';
// 更新样式
bitmapText.style.fontSize = 48;
创建位图字体 #
可以使用工具生成位图字体:
| 工具 | 说明 |
|---|---|
| BMFont | Windows 经典工具 |
| Glyph Designer | macOS 工具 |
| Littera | 在线工具 |
| FontBuilder | 开源工具 |
生成步骤:
- 选择字体和大小
- 设置字符集(如 ASCII、中文等)
- 导出为 XML/JSON + PNG
Text vs BitmapText #
性能对比 #
text
┌─────────────────────────────────────────────────────────────┐
│ 性能对比 │
├─────────────────────────────────────────────────────────────┤
│ │
│ Text │
│ ├── 文本变化时需要重新生成纹理 │
│ ├── 适合少量动态文本 │
│ └── 内存占用较小 │
│ │
│ BitmapText │
│ ├── 无需生成纹理,直接拼接 │
│ ├── 适合大量文本或频繁更新 │
│ └── 需要预加载字体纹理 │
│ │
│ 性能排序: │
│ BitmapText > Text(静态) > Text(频繁更新) │
│ │
└─────────────────────────────────────────────────────────────┘
选择建议 #
| 场景 | 推荐方式 |
|---|---|
| UI 标签(静态) | Text |
| 分数/计时器(频繁更新) | BitmapText |
| 大量文本(如游戏对话) | BitmapText |
| 多语言支持 | Text |
| 特殊字体效果 | Text |
| 性能敏感场景 | BitmapText |
高级技巧 #
文本测量 #
javascript
const text = new Text({
text: 'Measure Me',
style: { fontFamily: 'Arial', fontSize: 36 }
});
// 获取文本尺寸
console.log('宽度:', text.width);
console.log('高度:', text.height);
// 获取精确边界
const bounds = text.getBounds();
console.log('边界:', bounds);
文本截断 #
javascript
function truncateText(text, maxWidth) {
const originalText = text.text;
if (text.width <= maxWidth) {
return originalText;
}
let truncated = originalText;
while (text.width > maxWidth && truncated.length > 0) {
truncated = truncated.slice(0, -1);
text.text = truncated + '...';
}
return text.text;
}
const text = new Text({
text: 'This is a very long text that needs to be truncated',
style: { fontFamily: 'Arial', fontSize: 24 }
});
truncateText(text, 200);
打字机效果 #
javascript
class TypeWriter {
constructor(text, textObject, speed = 50) {
this.fullText = text;
this.textObject = textObject;
this.speed = speed;
this.currentIndex = 0;
this.isTyping = false;
}
start() {
this.currentIndex = 0;
this.isTyping = true;
this.type();
}
type() {
if (!this.isTyping) return;
if (this.currentIndex < this.fullText.length) {
this.textObject.text = this.fullText.slice(0, this.currentIndex + 1);
this.currentIndex++;
setTimeout(() => this.type(), this.speed);
} else {
this.isTyping = false;
}
}
stop() {
this.isTyping = false;
}
complete() {
this.textObject.text = this.fullText;
this.isTyping = false;
}
}
const text = new Text({
text: '',
style: { fontFamily: 'Arial', fontSize: 24, fill: 0xffffff }
});
app.stage.addChild(text);
const typewriter = new TypeWriter(
'Hello, this is a typewriter effect!',
text,
50
);
typewriter.start();
文本闪烁效果 #
javascript
const text = new Text({
text: 'Blinking Text',
style: { fontFamily: 'Arial', fontSize: 36, fill: 0xffffff }
});
let alpha = 1;
let direction = -1;
app.ticker.add(() => {
alpha += direction * 0.02;
if (alpha <= 0.3) direction = 1;
if (alpha >= 1) direction = -1;
text.alpha = alpha;
});
文本阴影效果 #
javascript
const text = new Text({
text: 'Shadow Text',
style: {
fontFamily: 'Arial',
fontSize: 48,
fill: 0xffffff,
dropShadow: {
color: 0x000000,
alpha: 0.5,
blur: 4,
distance: 4,
angle: Math.PI / 4
}
}
});
文本发光效果 #
javascript
import { GlowFilter } from 'pixi-filters';
const text = new Text({
text: 'Glowing Text',
style: {
fontFamily: 'Arial',
fontSize: 48,
fill: 0xffffff
}
});
text.filters = [new GlowFilter({
distance: 15,
outerStrength: 2,
innerStrength: 0,
color: 0x00ff00,
quality: 0.5
})];
实战示例 #
分数显示 #
javascript
class ScoreDisplay extends Container {
constructor() {
super();
this.score = 0;
this.label = new Text({
text: 'SCORE',
style: {
fontFamily: 'Arial',
fontSize: 18,
fill: 0x888888
}
});
this.value = new BitmapText({
text: '0',
style: {
fontFamily: 'Desycel',
fontSize: 48
}
});
this.value.y = 25;
this.addChild(this.label, this.value);
}
setScore(score) {
this.score = score;
this.value.text = score.toString().padStart(6, '0');
}
addScore(points) {
this.setScore(this.score + points);
}
}
const scoreDisplay = new ScoreDisplay();
scoreDisplay.setScore(12345);
app.stage.addChild(scoreDisplay);
对话框系统 #
javascript
class DialogBox extends Container {
constructor(width, height) {
super();
this.boxWidth = width;
this.boxHeight = height;
this.background = new Graphics();
this.background.roundRect(0, 0, width, height, 10);
this.background.fill({ color: 0x000000, alpha: 0.8 });
this.background.stroke({ width: 2, color: 0xffffff });
this.text = new Text({
text: '',
style: {
fontFamily: 'Arial',
fontSize: 20,
fill: 0xffffff,
wordWrap: true,
wordWrapWidth: width - 40,
lineHeight: 28
}
});
this.text.x = 20;
this.text.y = 20;
this.speakerName = new Text({
text: '',
style: {
fontFamily: 'Arial',
fontSize: 16,
fill: 0xffff00,
fontWeight: 'bold'
}
});
this.speakerName.x = 20;
this.speakerName.y = -25;
this.addChild(this.background, this.speakerName, this.text);
this.typewriter = null;
}
show(speaker, message) {
this.speakerName.text = speaker;
this.text.text = '';
this.visible = true;
this.typewriter = new TypeWriter(message, this.text, 30);
this.typewriter.start();
}
hide() {
this.visible = false;
if (this.typewriter) {
this.typewriter.stop();
}
}
}
const dialog = new DialogBox(600, 150);
dialog.y = 400;
dialog.show('NPC', 'Welcome to the game! Press any key to continue.');
app.stage.addChild(dialog);
通知系统 #
javascript
class Notification extends Container {
constructor(message, duration = 3000) {
super();
this.text = new Text({
text: message,
style: {
fontFamily: 'Arial',
fontSize: 18,
fill: 0xffffff
}
});
this.background = new Graphics();
this.background.roundRect(
-10,
-5,
this.text.width + 20,
this.text.height + 10,
5
);
this.background.fill({ color: 0x333333, alpha: 0.9 });
this.addChild(this.background, this.text);
this.alpha = 0;
this.y = -50;
this.animateIn();
setTimeout(() => this.animateOut(), duration);
}
animateIn() {
const targetY = this.y;
this.y = targetY - 20;
const animate = () => {
this.alpha += 0.1;
this.y += 2;
if (this.alpha < 1) {
requestAnimationFrame(animate);
}
};
animate();
}
animateOut() {
const animate = () => {
this.alpha -= 0.1;
if (this.alpha > 0) {
requestAnimationFrame(animate);
} else {
this.destroy();
}
};
animate();
}
}
class NotificationManager {
constructor(container) {
this.container = container;
this.notifications = [];
this.spacing = 50;
}
show(message) {
const notification = new Notification(message);
notification.x = this.container.width / 2;
notification.y = 50 + this.notifications.length * this.spacing;
this.container.addChild(notification);
this.notifications.push(notification);
notification.on('destroyed', () => {
const index = this.notifications.indexOf(notification);
if (index > -1) {
this.notifications.splice(index, 1);
this.reposition();
}
});
}
reposition() {
this.notifications.forEach((n, i) => {
n.y = 50 + i * this.spacing;
});
}
}
性能优化 #
缓存文本 #
javascript
const text = new Text({
text: 'Static Text',
style: { fontFamily: 'Arial', fontSize: 36 }
});
// 对于不变化的文本,启用缓存
text.cacheAsBitmap = true;
批量更新 #
javascript
// 不推荐:多次更新
text.text = 'Score: ' + score;
text.style.fill = 0xff0000;
text.x = 100;
// 推荐:批量更新后一次性渲染
text.text = 'Score: ' + score;
text.style.fill = 0xff0000;
text.x = 100;
text.updateText();
使用 BitmapText #
javascript
// 对于频繁更新的文本,使用 BitmapText
const score = new BitmapText({
text: '0',
style: { fontFamily: 'Desycel', fontSize: 48 }
});
app.ticker.add(() => {
score.text = currentScore.toString();
});
下一步 #
掌握了文本处理后,接下来学习 交互事件,了解如何处理用户交互!
最后更新:2026-03-29