跳至主体内容
版本:v8.x

v8 迁移指南

欢迎使用 PixiJS v8 迁移指南!此文档旨在帮助您顺利地将项目从 PixiJS v7 过渡到最新、最棒的 PixiJS v8。请遵循以下步骤以确保迁移成功。

目录

  1. 简介
  2. 重大更改
  3. 弃用功能
  4. 资源

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 等效
drawChamferRectchamferRect
drawCirclecircle
drawEllipseellipse
drawFilletRectfilletRect
drawPolygonpoly
drawRectrect
drawRegularPolygonregularPoly
drawRoundedPolygonroundPoly
drawRoundedRectroundRect
drawRoundedShaperoundShape
drawStarstar
  • 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 函数。与 strokefill 一样,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!

了解此全新设置的最佳方式是玩耍和充分了解网格和着色器示例

旧: 7.x 示例 新: 8.x 示例

滤镜

滤镜几乎都以相同的方式工作,除非你构建自定义滤镜。如果是这种情况,需要考虑以上提到的着色器更改。

  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' },
},
},
});

旧: 7.x 示例 新: 8.x 示例

如果你使用 社区滤镜,请注意 @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 调整,可带来巨大的性能提升!

粒子也不会存储在 ParticleContainerchildren 数组中,因为从技术上讲,粒子不属于场景图(出于性能原因)。相反,它们存储在称为 particleChildren 的扁平列表中,该列表是 ParticleContainer 类的一部分。你可以直接修改此数组以获得额外的速度,或者可以使用 addParticleremoveParticle 方法来管理粒子。

另一个优化是 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。
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 内建有两个适配器 BrowserAdapterWebWorkerAdapter
        • 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 可以有子节点。这意味着 SpriteMeshGraphics 等不再可以有子节点。

    要复制旧行为,你可以创建一个 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

4. 资源