v8 迁移指南
欢迎使用 PixiJS v8 迁移指南!此文档旨在帮助您顺利地将项目从 PixiJS v7 过渡到最新、最棒的 PixiJS v8。请遵循以下步骤以确保迁移成功。
目录
1. 简介
PixiJS v8 引入了数项激动人心的更改和改进,可显著提高渲染器的性能。尽管我们努力使迁移过程尽可能平滑,但一些重大更改不可避免。本指南将引导您完成将 PixiJS v7 项目迁移到 PixiJS v8 所需的步骤。
2. 重大更改
在深入了解迁移过程之前,让我们回顾一下 PixiJS v8 中引入的重大更改。请务必密切关注这些更改,因为它们可能会影响您现有的代码库。
我应该升级吗?
通常,答案是肯定的!但目前,可能有一些理由表明最好不要立即升级。问问自己以下问题
- 你的项目利用了尚未迁移到 v8 的现有 Pixi 库吗?我们正在努力将我们的关键库迁移到 v8,但希望这不要成为在使用纯 Pixi 的用户的阻碍。这意味着某些库现在还不会有 v8 对应项。如果这是你的情况,最好推迟迁移。
已迁移
- 滤镜
- 声音
- Gif
- Storybook
- UI
- Open Games
正在迁移
- React
- Spine (esoteric 版本)
待迁移
- Pixi 分层(与其迁移这个,我们可能会直接将它作为一项特性合并到 PixiJS v8 中)
新包结构
自版本 5 起,PixiJS 便开始利用单独的子包将其代码库组织成更小的单元。然而,这种方法导致了问题,例如不同 PixiJS 版本的安装冲突,这引发了内部缓存的问题。
在 v8 中,PixiJS 恢复了单包结构。虽然你仍然可以导入 PixiJS 的特定部分,但你只需要安装主包。
旧
import { Application } from '@pixi/app';
import { Sprite } from '@pixi/sprite';
新
import { Application, Sprite } from 'pixi.js';
自定义构建
PixiJS 使用“扩展”系统添加渲染器功能。默认情况下,PixiJS 包含许多扩展,以便获得全面的开箱即用体验。然而,为了充分控制功能和捆绑大小,你可以手动导入特定的 PixiJS 组件。
// imported by default
import 'pixi.js/accessibility';
import 'pixi.js/app';
import 'pixi.js/events';
import 'pixi.js/filters';
import 'pixi.js/sprite-tiling';
import 'pixi.js/text';
import 'pixi.js/text-bitmap';
import 'pixi.js/text-html';
import 'pixi.js/graphics';
import 'pixi.js/mesh';
import 'pixi.js/sprite-nine-slice';
// not added by default, everyone needs to import these manually
import 'pixi.js/advanced-blend-modes';
import 'pixi.js/unsafe-eval';
import 'pixi.js/prepare';
import 'pixi.js/math-extras';
import 'pixi.js/dds';
import 'pixi.js/ktx';
import 'pixi.js/ktx2';
import 'pixi.js/basis';
import { Application } from 'pixi.js';
const app = new Application();
await app.init({
manageImports: false, // disable importing the above extensions
});
在初始化应用程序时,你可以禁用自动导入特性,防止 PixiJS 自动导入任何扩展。与上面展示的一样,你需要手动导入它们。
还应注意,pixi.js/text-bitmap
也添加了 Assets
加载功能。因此,如果你希望在初始化渲染器之前加载位图字体,你需要导入此扩展。
import 'pixi.js/text-bitmap';
import { Assets, Application } from 'pixi.js';
await Assets.load('my-font.fnt'); // If 'pixi.js/text-bitmap' is not imported, this will not load
await new Application().init();
异步初始化
现在需要异步初始化 PixiJS。随着 WebGPU 渲染器的引入,现在需要在使用之前等待 PixiJS
旧
import { Application } from 'pixi.js';
const app = new Application();
// do pixi things
新
import { Application } from 'pixi.js'
const app = new Application();
(async () => {
await app.init({
// application options
});
// do pixi things
})()
此更改还意味着 ApplicationOptions
对象现在可以传递到 init
函数中,而不是构造函数。
纹理调整
纹理结构经过修改,以简化 v7 中逐渐混乱的幕后情况。纹理不再了解或管理资源的加载。这需要您或资产管理器预先完成。纹理只预载完全加载的资源。这样可以大幅简化管理,纹理验证在构建期间就可以完成,并且可以就此打住了!BaseTexture 已不存在。我们现在拥有多种 TextureSource。纹理源将纹理的设置与如何上传和使用该纹理结合起来。在 v8 中,有以下纹理源
TextureSource - 一种香草味纹理,您可以随意渲染或上传。(主要用于渲染纹理)ImageSource - 一种包含某种图像资源的纹理源(例如 ImageBitmap 或 html 图像)CanvasSource - 一种包含画布的画布源。主要用于渲染画布或渲染到画布(web GPU)VideoSource - 一种包含视频的纹理源。负责更新 GPU 上的纹理,以确保它们保持同步。BufferSource - 一种包含缓冲区的纹理源。您真的需要什么都可以!确保您的缓冲区类型和格式兼容!CompressedSource -一种处理压缩纹理的纹理源。由 GPU 压缩纹理格式使用。
尽管大多数时候Assets
都会返回纹理,但您可能想要制作自己的纹理!祝您马到功成!
创建纹理源的签名与 baseTexture 不同。示例
const image = new Image();
image.onload = function(){
// create a texture source
const source = new ImageSource({
resource: image,
});
// create a texture
const texture = new Texture({
source
});
}
image.src = 'myImage.png';
图形 API 改造
图形 API 有一些关键变化。事实上,这可能是 v8 变化最大的部分。我们已尽力添加弃用,但以下是对更改的概述
- v8 不会再开始填充或描边,然后再构建形状,而是让您构建形状,然后描边/填充。
Line
已更名为Stroke
旧
// red rect
const graphics = new Graphics()
.beginFill(0xFF0000)
.drawRect(50, 50, 100, 100)
.endFill();
// blur rect with stroke
const graphics2 = new Graphics()
.lineStyle(2, 'white')
.beginFill('blue')
.circle(530, 50, 140, 100)
.endFill();
新
// red rect
const graphics = new Graphics()
.rect(50, 50, 100, 100)
.fill(0xFF0000);
// blur rect with stroke
const graphics2 = new Graphics()
.rect(50, 50, 100, 100)
.fill('blue')
.stroke({width:2, color:'white'});
- 形状函数已重命名。每个绘图函数都已简化为较短的名称版本。但它们的参数是相同的
v7 API 调用 | v8 API 等效 |
---|---|
drawChamferRect | chamferRect |
drawCircle | circle |
drawEllipse | ellipse |
drawFilletRect | filletRect |
drawPolygon | poly |
drawRect | rect |
drawRegularPolygon | regularPoly |
drawRoundedPolygon | roundPoly |
drawRoundedRect | roundRect |
drawRoundedShape | roundShape |
drawStar | star |
- fills 函数需要
FillStyle
选项或颜色,而不是参数字符串。这也替换了beginTextureFill
旧
const rect = new Graphics()
.beginTextureFill({texture:Texture.WHITE, alpha:0.5, color:0xFF0000})
.drawRect(0, 0, 100, 100)
.endFill()
.beginFill(0xFFFF00, 0.5)
.drawRect(100, 0, 100, 100)
.endFill();
新
const rect = new Graphics()
.rect(0, 0, 100, 100)
.fill({texture:Texture.WHITE, alpha:0.5, color:0xFF0000})
.rect(100, 0, 100, 100)
.fill({color:0xFFFF00, alpha:0.5});
- strokes 函数需要
strokeStyle
选项或颜色,而不是参数字符串。这也替换了lineTextureStyle
旧:
const rect = new Graphics()
.lineTextureStyle({texture:Texture.WHITE, width:10, color:0xFF0000})
.drawRect(0, 0, 100, 100)
.endFill()
.lineStyle(2, 0xFEEB77);
.drawRect(100, 0, 100, 100)
.endFill();
新
const rect = new Graphics()
.rect(0, 0, 100, 100)
.stroke({texture:Texture.WHITE, width:10, color:0xFF0000})
.rect(100, 0, 100, 100)
.stroke({color:0xFEEB77, width:2});
- 现在孔会使用新的
cut
函数。与stroke
和fill
一样,cut
作用于先前的形状。旧:
const rectAndHole = new Graphics()
.beginFill(0x00FF00)
.drawRect(0, 0, 100, 100)
.beginHole()
.drawCircle(50, 50, 20)
.endHole()
.endFill();
新
const rectAndHole = new Graphics()
.rect(0, 0, 100, 100)
.fill(0x00FF00)
.circle(50, 50, 20)
.cut();
GraphicsGeometry
已替换为GraphicsContext
,这使共享Graphics
数据的效率更高。
旧
const rect = new Graphics()
.beginFill(0xFF0000)
.drawRect(50, 50, 100, 100)
.endFill();
const geometry = rect.geometry;
const secondRect = new Graphics(geometry);
新
const context = new GraphicsContext()
.rect(50, 50, 100, 100)
.fill(0xFF0000);
const rect = new Graphics(context);
const secondRect = new Graphics(context);
着色器更改
因为现在我们需要适应 WebGL 和 WebGPU 着色器,所以我们对这些着色器的构造方式进行了调整来考虑到这一点。你将注意到的主要区别(适用于所有着色器)是,纹理不再被视为制服(即不能包含在制服组中)。而代之以资源的概念。资源可以是以下几种内容中的若干内容
- TextureSource - 源纹理
myTexture.source
- TextureStyle - 纹理样式
myTexture.style
- UniformGroup - 基于数字的制服集合
myUniforms = new UniformGroup({})
- BufferResource - 作为制服组处理的缓冲区(高级)
现在,创建一个仅限 webgl 的着色器的外观如下所示
旧
const shader = PIXI.Shader.from(vertex, fragment, uniforms);
新
仅适用于 WebGL
const shader = Shader.from({
gl: { vertex, fragment },
resources, // resource used from above including uniform groups
});
适用于 WebGL 和 WebGPU
const shader = Shader.from({
gl: { vertex, fragment },
gpu: {
vertex: {
entryPoint: 'mainVert',
source,
},
fragment: {
entryPoint: 'mainFrag',
source,
},
},
resources, // resource used from above including uniform groups
});
制服的构造方式也略有不同。在创建它们时,你现在提供你希望它们是哪种类型的变量。
旧
const uniformGroup = new UniformGroup({
uTime:1,
});
uniformGroup.uniforms.uTime = 100;
新
const uniformGroup = new UniformGroup({
uTime:{value:1, type:'f32',
});
uniformGroup.uniforms.uTime = 100; // still accessed the same!
了解此全新设置的最佳方式是玩耍和充分了解网格和着色器示例
滤镜
滤镜几乎都以相同的方式工作,除非你构建自定义滤镜。如果是这种情况,需要考虑以上提到的着色器更改。
旧
const filter = new Filter(vertex, fragment, {
uTime: 0.0,
});
新
const filter = new Filter({
glProgram: GlProgram.from({
fragment,
vertex,
}),
resources: {
timeUniforms: {
uTime: { value: 0.0, type: 'f32' },
},
},
});
如果你使用 社区滤镜,请注意 @pixi/filter-*
包不再针对 8.0 版维护,但是你可以直接从 pixi-filters
包导入作为子模块。
*旧
import { AdjustmentFilter } from '@pixi/filter-adjustment';
*新
import { AdjustmentFilter } from 'pixi-filters/adjustment';
ParticleContainer
ParticleContainer
已在 8.0 版中重新设计,可以容纳的粒子比以往多得多。你应该了解以下几个关键更改
ParticleContainer
不再接受精灵 (Sprite) 作为其子对象。相反,它需要 Particle
类(或实现 i= IParticle
接口的对象),该类遵循以下接口
export interface IParticle
{
x: number;
y: number;
scaleX: number;
scaleY: number;
anchorX: number;
anchorY: number;
rotation: number;
color: number;
texture: Texture;
}
进行此更改的原因是,精灵 (Sprite) 带有很多额外的属性和事件,这些属性和事件在处理大量粒子时通常不必要。这种方法明确消除了我们在 v7 中遇到的任何歧义,例如“为什么我的精灵 (Sprite) 不适用于滤镜?”或“为什么我无法在我自己的精灵 (Sprite) 中嵌套子对象?”这更具可预测性。此外,由于粒子的轻量级特性,这意味着我们可以呈现更多粒子!
因此,没有功能丢失——仅仅是 API 调整,可带来巨大的性能提升!
粒子也不会存储在 ParticleContainer
的 children
数组中,因为从技术上讲,粒子不属于场景图(出于性能原因)。相反,它们存储在称为 particleChildren
的扁平列表中,该列表是 ParticleContainer
类的一部分。你可以直接修改此数组以获得额外的速度,或者可以使用 addParticle
和 removeParticle
方法来管理粒子。
另一个优化是 ParticleContainer
不计算它自己的边界,这样做会抵消我们创造的性能收益!相反,由你在初始化 ParticleContainer
时提供 boundsArea
。
旧版
const container = new ParticleContainer();
for (let i = 0; i < 100000; i++) {
const particle = new Sprite(texture);
container.addChild(particle);
}
新版本
const container = new ParticleContainer();
for (let i = 0; i < 100000; i++) {
const particle = new Particle(texture);
container.addParticle(particle);
}
带有边界区域
const container = new ParticleContainer({
boundsArea:new Rectangle(0,0,500,500)
});
其他重大更改
DisplayObject
已被移除。Container
现在是所有 PixiJS 对象的基类。updateTransform
已被移除,因为节点不再包含任何渲染逻辑我们确实认识到许多人使用此函数来在每一帧上执行自定义逻辑,所以我们添加了一个新的
onRender
函数,可用于此目的。旧
class MySprite extends Sprite {
constructor() {
super();
this.updateTransform();
}
updateTransform() {
super.updateTransform();
// do custom logic
}
}新
class MySprite extends Sprite {
constructor() {
super();
this.onRender = this._onRender.bind(this);
}
_onRender() {
// do custom logic
}
}Mipmap 生成更改
- BaseTexture 的
mipmap
属性已更名为autoGenerateMipmaps
。 RenderTextures
的 Mipmap 已进行调整,以便开发人员负责更新它们的 Mipmap。Mipmap 生成可能代价高昂,并且由于我们处理纹理的新反应方式,我们不希望在不需要时意外生成 Mipmap。
- BaseTexture 的
const myRenderTexture = RenderTexture.create({width:100, height:100, autoGenerateMipmaps:true});
// do some rendering..
renderer.render({target:myRenderTexture, container:scene});
// now refresh mipmaps when you are ready
myRenderTexture.source.updateMipmaps();
- 由于 PixiJS 内部处理方式的新变化,精灵 (Sprite) 不再会被通知纹理的 UV 是否已修改。最佳做法是,不要在纹理创建后修改纹理 UV。最好让纹理做好准备(它们创建和存储的代价并不高)。
- 有时,你可能想采用一项特殊的技术,来对 UV 进行动画处理。在这种情况下,你需要负责更新精灵(值得注意的是,它可能自动更新 - 但由于新的优化特性,这无法得到保证)。但是,更新源数据(如视频纹理)始终会立即得到反映。
const texture = await Assets.load('bunny.png');
const sprite = new Sprite(texture);
texture.frame.width = texture.frame.width/2;
texture.update();
// guarantees the texture changes will be reflected on the sprite
sprite.onViewUpdate();
// alternatively you can hooke into the sprites event
texture.on('update', ()=>{sprite.onViewUpdate});
在精灵纹理发生变化时添加和移除事件会导致性能下降,而问题不能接受,尤其是在更换很多纹理时(比如,想象一下有很多关键帧纹理更换的射击游戏)。这就是我们现在将这项职责交给用户的原因。
容器剔除新方法
对于 PixiJS 的此版本,我们更改了容器上
cullable
属性的工作方式。此前,容器剔除是在渲染循环期间自动完成的。然而,我们已经移除了这个逻辑,并为用户提供了自己控制剔除发生时间的能力。通过此更改,我们添加了一些新属性
cullable
- 容器是否可以被剔除cullArea
- 将用于代替容器边界的剔除区域cullableChildren
- 容器的子项是否可以被剔除。这有助于优化大型场景
新
const container = new Container();
const view = new Rectangle(0, 0, 800, 600);
container.cullable = true;
container.cullArea = new Rectangle(0,0,400,400);
container.cullableChildren = false;
Culler.shared.cull(myContainer, view);
renderer.render(myContainer);还有一个
CullerPlugin
,它可以自动调用Culler.shared.cull
(如果想模拟旧有行为的话,则可以使用该方法),从而对每一帧进行剔除操作。import {extensions, CullerPlugin} from 'pixi.js'
extensions.add(CullerPlugin)重命名多个网格类
- 重命名
SimpleMesh
->MeshSimple
- 重命名
SimplePlane
->MeshPlane
- 重命名
SimpleRope
->MeshRope
- 重命名
Assets
的不赞成使用通知已被移除旧
import { Assets } from 'pixi.js';
Assets.add('bunny', 'bunny.png');新
import { Assets } from 'pixi.js';
Assets.add({ alias: 'bunny', src: 'bunny.png' });settings
对象已被移除旧
import { settings, BrowserAdapter } from 'pixi.js';
settings.RESOLUTION = 1;
settings.FAIL_IF_MAJOR_PERFORMANCE_CAVEAT = false;
settings.ADAPTER = BrowserAdapter;新
import { AbstractRenderer, DOMAdapter, BrowserAdapter } from 'pixi.js';
// Can also be passed into the renderer directly e.g `autoDetectRenderer({resolution: 1})`
AbstractRenderer.defaultOptions.resolution = 1;
// Can also be passed into the renderer directly e.g `autoDetectRenderer({failIfMajorPerformanceCaveat: false})`
AbstractRenderer.defaultOptions.failIfMajorPerformanceCaveat = false;
// See below for more information about changes to the adapter
DOMAdapter.set(BrowserAdapter);适配器和 Web Worker 的更改
settings.ADAPTER
已被移除,已由DOMAdapter
取代DOMAdapter
是一个静态类,可用于为整个应用程序设置适配器- PixiJS 内建有两个适配器
BrowserAdapter
和WebWorkerAdapter
BrowserAdapter
是默认适配器,且在浏览器中运行时使用它WebWorkerAdapter
在 Web worker 中运行时使用
旧
import { settings, WebWorkerAdapter } from 'pixi.js';
settings.ADAPTER = WebWorkerAdapter;
settings.ADAPTER.createCanvas();新
import { DOMAdapter, WebWorkerAdapter } from 'pixi.js';
DOMAdapter.set(WebWorkerAdapter);
DOMAdapter.get().createCanvas();
应用程序类型现在接受渲染器,而不是视图(@Zyie 在 https://github.com/pixijs/pixijs/pull/9740 中提出)
这是为了允许正确地输入
app.renderer
旧
const app = new Application<HTMLCanvasElement>();
新
// WebGL or WebGPU renderer
const app = new Application<Renderer<HTMLCanvasElement>>();
// WebGL specific renderer
const app = new Application<WebGLRenderer<HTMLCanvasElement>>();
// WebGPU specific renderer
const app = new Application<WebGPURenderer<HTMLCanvasElement>>();
Texture.from
不再从 URL 中载入纹理。使用
Texture.from
时,需要传入源代码,比如CanvasSource
/ImageSource
/VideoSource
,或者资源,比如HTMLImageElement
/HTMLCanvasElement
/HTMLVideoElement
,或者通过Assets.load
载入的字符串旧
import { Texture } from 'pixi.js';
const texture = Texture.from('https://i.imgur.com/IaUrttj.png');新
import { Assets, Texture } from 'pixi.js';
await Assets.load('https://i.imgur.com/IaUrttj.png');
const texture = Texture.from('https://i.imgur.com/IaUrttj.png');
Ticker
的回调现在会传递Ticker
实例,而不是时间差。这是为了更好地控制用于的时间单位。旧
Ticker.shared.add((dt)=> {
bunny.rotation += dt
});新
Ticker.shared.add((ticker)=> {
bunny.rotation += ticker.deltaTime;
});文本解析器已被重命名
TextFormat
->bitmapFontTextParser
XMLStringFormat
->bitmapFontXMLStringParser
XMLFormat
->bitmapFontXMLParser
默认
eventMode
现在是passive
,而不是auto
utils
已删除。所有函数都可以作为直接导入使用。旧
import { utils } from 'pixi.js';
utils.isMobile.any();新
import { isMobile } from 'pixi.js';
isMobile.any();container.getBounds()
现在返回一个Bounds
对象,而不是Rectangle
对象。你可以使用container.getBounds().rectangle
来访问矩形。旧
const bounds = container.getBounds();
新
const bounds = container.getBounds().rectangle;
3. 已弃用功能
PixiJS v7 的某些功能在 v8 中已被弃用。虽然它们仍然可用,但建议更新你的代码以使用新的替代品。有关用什么替换的详细信息,请参阅已弃用功能部分。
叶子节点不再允许子节点
只有
Containers
可以有子节点。这意味着Sprite
、Mesh
、Graphics
等不再可以有子节点。要复制旧行为,你可以创建一个
Container
并将叶子节点添加到其中。旧
const sprite = new Sprite();
const spriteChild = new Sprite();
sprite.addChild(spriteChild);新
const container = new Container();
const sprite = new Sprite();
const spriteChild = new Sprite();
container.addChild(sprite);
container.addChild(spriteChild);Application.view
已替换为Application.canvas
旧
const app = new Application({ view: document.createElement('canvas') });
document.body.appendChild(app.view);新
const app = new Application();
await app.init({ view: document.createElement('canvas') });
document.body.appendChild(app.canvas);NineSlicePlane
重命名为NineSliceSprite
SCALE_MODES
已替换为ScaleMode
字符串SCALE_MODES.NEAREST
->'nearest'
,SCALE_MODES.LINEAR
->'linear'
,
WRAP_MODES
已替换为WrapMode
字符串WRAP_MODES.CLAMP
->'clamp-to-edge'
,WRAP_MODES.REPEAT
->'repeat'
,WRAP_MODES.MIRRORED_REPEAT
->'mirror-repeat'
,
DRAW_MODES
已替换为Topology
字符串DRAW_MODES.POINTS
->'point-list'
,DRAW_MODES.LINES
->'line-list'
,DRAW_MODES.LINE_STRIP
->'line-strip'
,DRAW_MODES.TRIANGLES
->'triangle-list'
,DRAW_MODES.TRIANGLE_STRIP
->'triangle-strip'
,
构建函数已被很大程度上更改为接受对象而不是多个参数
旧
const blurFilter = new BlurFilter(8, 4, 1, 5);
const displacementFilter = new DisplacementFilter(sprite, 5);
const meshGeometry = new MeshGeometry(vertices, uvs, index);
const mesh = new Mesh(geometry, shader, state, drawMode);
const plane = new PlaneGeometry(width, height, segWidth, segHeight);
const nineSlicePlane = new NineSlicePlane(texture, leftWidth, topHeight, rightWidth, bottomHeight);
const tileSprite = new TileSprite(texture, width, height);
const text = new Text('Hello World', style);
const bitmapText = new BitmapText('Hello World', style);
const htmlText = new HTMLText('Hello World', style);新
const blurFilter = new BlurFilter({
blur: 8,
quality: 4,
resolution: 1,
kernelSize: 5,
});
const displacementFilter = new DisplacementFilter({
sprite,
scale: 5,
});
const meshGeometry = new MeshGeometry({
positions: vertices,
uvs,
indices: index,
topology: 'triangle-list';
shrinkBuffersToFit: boolean;
});
const mesh = new Mesh({
geometry
shader
texture
});
const plane = new PlaneGeometry({
width,
height,
verticesX: segWidth,
verticesY: segHeight,
});
const nineSliceSprite = new NineSliceSprite({
texture,
leftWidth,
topHeight,
rightWidth,
bottomHeight,
});
const tileSprite = new TileSprite({
texture,
width,
height,
});
const text = new Text({
text: 'Hello World',
style,
});
const bitmapText = new BitmapText({
text:'Hello World',
style,
});
const htmlText = new HTMLText({
text:'Hello World',
style,
});container.name
现在是container.label