PixiJS — 浏览器 2D 场景图与多后端渲染器
已复核PixiJS 是一棵 2D Container 树加上可替换的渲染后端。日常类比:原生 Canvas 像你拿铅笔一笔一笔画;Pixi 先把“要画什么”挂到 app.stage 上,再由 BatcherPipe / RenderPipe 翻译成 WebGL、WebGPU 或 Canvas 指令。
import { Application, Sprite, Assets } from 'pixi.js'
const app = new Application()await app.init({ width: 800, height: 600, background: '#1099bb' })document.body.appendChild(app.canvas)
const tex = await Assets.load('/bunny.png')const bunny = new Sprite(tex)app.stage.addChild(bunny)固定 8.20.1 起,构造函数不再接受 options;debug 构建会提示改走 await app.init()。app.view 已弃用,挂到页面上的是 app.canvas。
不读固定 8.20.1 源码,v8 很容易被 v7 教程带跑:
- 为什么
new Application({ width: 800 })不会得到可用 renderer——options 必须进init() - 为什么默认不是 WebGPU——
autoDetectRenderer的优先级是webgl → webgpu → canvas - 为什么
ParticleContainer.addChild(sprite)会抛错——粒子容器只收addParticle - 为什么
ticker.deltaTime不能当毫秒用——它是无量纲标量,约 60 FPS 时 ≈ 1
固定版本可以看成五步:
-
异步装配:
init()调用autoDetectRenderer,再按注册顺序初始化 Application plugins。TickerPlugin默认autoStart: true,之后一般不必手写app.render()。 -
选后端:无
preference时按['webgl', 'webgpu', 'canvas']探测。字符串 preference 会插到队首并保留其余回退;数组则只尝试列出的后端。 -
Container 树:普通显示对象用
addChild。父节点变换会累到子节点。短周期隐藏用visible = false,不要反复拆挂。 -
资源:
Assets.load走 Resolver + Loader + Cache。进度回调不能代替返回的 Promise。 -
专用管线:共享系统里有
BatcherPipe、RenderGroupPipe、FilterPipe。ParticleContainer与 filter 各有自己的限制,不能按普通 Sprite 树外推。
案例 1:最小闭环与 ticker 标量
Section titled “案例 1:最小闭环与 ticker 标量”import { Application, Sprite, Assets } from 'pixi.js'
const app = new Application()await app.init({ width: 800, height: 600, background: '#1099bb' })document.body.appendChild(app.canvas)
const bunny = new Sprite(await Assets.load('/bunny.png'))bunny.anchor.set(0.5)app.stage.addChild(bunny)
app.ticker.add((ticker) => { bunny.rotation += 0.01 * ticker.deltaTime})deltaTime = deltaMS * Ticker.targetFPMS。60 FPS 时它接近 1,不是 16.6。把 0.01 * deltaTime 理解成“每毫秒转 0.01”会错一个数量级。
案例 2:ParticleContainer 不收 Sprite
Section titled “案例 2:ParticleContainer 不收 Sprite”import { ParticleContainer, Particle, Assets } from 'pixi.js'
const tex = await Assets.load('/star.png')const pc = new ParticleContainer({ dynamicProperties: { position: true, rotation: true },})for (let i = 0; i < 1000; i++) { pc.addParticle(new Particle({ texture: tex, x: Math.random() * 800, y: Math.random() * 600, }))}app.stage.addChild(pc)addChild / addChildAt 会抛「Please use ParticleContainer.addParticle()」。默认只动态更新 position;要转、要变色、要改 UV,必须在 dynamicProperties 里打开对应开关。
案例 3:filter 是离屏目标,不是一行特效
Section titled “案例 3:filter 是离屏目标,不是一行特效”import { Container, BlurFilter } from 'pixi.js'
const wheel = new Container()wheel.filters = [new BlurFilter({ strength: 2 })]app.stage.addChild(wheel)FilterSystem 从 TexturePool 取离屏纹理再画回。源码写明:RenderGroup 与 filter 不兼容;需要 blending 的嵌套 filter 也有已知限制。不断开关 filter 时,更稳的是改 filter.enabled,而不是每帧换新数组。本文没有测量移动端耗时。
- 把 v7 构造函数抄到 v8:
new Application(options)在 8.20.1 只触发弃用警告,不会完成 renderer 装配。必须await init()。 app.view当 canvas:viewgetter 已标v8_0_0弃用,请用app.canvas。- 默认后端当成 WebGPU:没有
preference时先试 WebGL。数组preference: ['webgpu']才会排除 WebGL。 - Graphics 继续
beginFill().drawRect():固定版本路径是rect().fill()。 - 把
deltaTime当毫秒:它是按targetFPMS归一化的标量。物理积分若当毫秒用,速度会差几十倍。
适用 vs 不适用场景
Section titled “适用 vs 不适用场景”适用:
- 同屏大量 2D 精灵、粒子、营销页动效,需要场景图而不是 DOM
- 数据可视化里“算坐标用别的库、画点用 Pixi”
- 想显式选择 WebGL / WebGPU / Canvas,而不是绑死一种后端
不适用:
- 3D 场景与透视相机 → threejs
- 静态信息图或普通页面布局 → CSS / SVG
- 需要开箱即用的物理、输入和关卡流程 → phaser 一类游戏框架
- 把 filter 叠在 RenderGroup 上,或把嵌套 blending filter 当成已保证行为
固定版本边界
Section titled “固定版本边界”- 本文绑定
pixijs/pixijs@3b6b5635deb9edd09f3eafd548b1e82685853ea7,tag 与 npm latest 均为8.20.1。 - GitHub annotated tag 与 npm
gitHead指向同一提交。 - 生产依赖包括
eventemitter3、earcut、tiny-lru等,不是零依赖核心。 - 默认探测顺序把 WebGL 放在 WebGPU 前面;这是源码优先级,不是性能测试结论。
- 本文未安装依赖、运行 unit/visual tests、创建 WebGL 上下文或测量 FPS,状态保持
UNVERIFIED。
- v8 的入口是异步合同——构造只建空壳,
init()才选 renderer、装 plugin。 - 默认后端是安全优先——WebGL 先于 WebGPU,数组 preference 才是白名单。
- 场景图 API 不是统一的——Sprite 走
addChild,粒子走addParticle。 - 时间单位写在字段名里也不一定是毫秒——
deltaTime是标量,deltaMS/elapsedMS才是毫秒。
const app = new Application({ width: 800, height: 600 }),不调用init()。app.canvas能用吗?new ParticleContainer().addChild(sprite)在固定 8.20.1 会怎样?await app.init()不传preference。探测顺序的第一项是 WebGPU 吗?
检查点:
- 不能当可用 canvas。构造函数不再装配 renderer;
canvasgetter 读的是this.renderer.canvas。 - 抛错。必须
addParticle,不能把 Sprite 当粒子子节点。 - 不是。默认优先级第一项是
webgl,然后才是webgpu与canvas。
- 官方文档:pixijs.com
- 固定源码:pixijs/pixijs —— 本文绑定提交
3b6b5635deb9edd09f3eafd548b1e82685853ea7 - threejs —— 同主题 3D 场景图对照
- phaser —— 带游戏循环与物理的 HTML5 框架
- threejs —— 3D 场景图 / 相机 / PBR 对照
- phaser —— 更完整的 2D 游戏框架
- konva —— Canvas2D 场景图对照
- gsap —— 常与 Pixi 搭配的补间层,不是渲染器
- aseprite —— Aseprite — 像素艺术 / 动画编辑器
- cocos2d-x —— Cocos2d-x — 一份 C++ 代码把 2D 手游跑遍 iOS / Android
- defold —— Defold — King 出品的 Lua 跨平台游戏引擎
- dragonbones —— DragonBones — 国产开源 2D 骨骼动画运行时
- excalidraw —— Excalidraw — 手绘风协作白板
- gimp —— GIMP — GNU 图像处理程序
- glsl-canvas —— glslCanvas — Book of Shaders 配套库
- godot —— Godot — 开源游戏引擎和编辑器
- heaps —— Heaps — Haxe 跨平台高性能游戏引擎
- lottie —— lottie-web — 把 AE 动画变成网页可播放的 JSON
- love2d —— LÖVE — 用 Lua 写 2D 游戏的轻量框架
- melonjs —— melonJS — 轻量 JS 2D 游戏引擎
- mind-ar-js —— MindAR — 不装原生 SDK 的浏览器图像/人脸 AR
- phaser —— Phaser — HTML5 2D 游戏框架
- piskel —— Piskel — Web 像素艺术编辑器
- planck —— planck.js — 纯 JS Box2D 生态
- regl —— regl — 函数式 WebGL 封装
- rive —— Rive — 把矢量动画做成可交互组件的运行时
- spectorjs —— Spector.js — WebGL/WebGPU 调试器
- spine-runtimes —— Spine Runtimes — 2D 骨骼动画运行时
- tiled —— Tiled Map Editor — 通用 2D 关卡编辑
- twgl —— TWGL — 极薄 WebGL helpers