PIXI.js代码占用大量内存致Chrome崩溃,求高效重复绘图方案
问题分析与优化方案
你的代码导致Chrome内存占用到1.5G后崩溃的原因其实很好理解:每次调用graphics.drawRect()都会在PIXI.Graphics对象中累积新的绘制指令。随着app.ticker每帧执行render函数,这些指令会无限制地堆积——毕竟每一个小矩形的绘制坐标、尺寸、颜色信息都被完整保存在内存里,最终直接撑爆内存。
下面根据不同的需求场景,给你几种高效的优化方案:
方案1:仅显示单个动态矩形(最轻量化)
如果你的需求只是让一个3x3的矩形随机切换位置,不需要保留历史绘制的所有矩形,那每次渲染前清空Graphics的绘制指令就行:
const width = window.innerWidth; const height = window.innerHeight; const app = new PIXI.Application({ width, height }); const graphics = new PIXI.Graphics(); app.stage.addChild(graphics); app.loader.load(setup); function setup() { document.body.appendChild(app.view); app.ticker.add(render); } function render() { // 关键步骤:清空之前所有的绘制指令 graphics.clear(); // 清空后需要重新设置填充色 graphics.beginFill(16763904); const x = randomInt(0, width - 3); // 减去方块宽度,避免超出屏幕边缘 const y = randomInt(0, height - 3); graphics.drawRect(x, y, 3, 3); graphics.endFill(); // 可选,养成良好习惯 } function randomInt(min, max) { return Math.floor(Math.random() * (max - min + 1)) + min; }
方案2:保留大量矩形(推荐最优方案)
如果需要保留所有绘制过的矩形,PIXI.Graphics的累积式绘制肯定不行,推荐用**PIXI.ParticleContainer + 预生成纹理**的组合——这是PIXI里渲染大量相同元素的效率天花板:
const width = window.innerWidth; const height = window.innerHeight; const app = new PIXI.Application({ width, height }); // 预先生成一个3x3红色方块的纹理(所有矩形共用这个纹理) const texture = PIXI.RenderTexture.create({ width: 3, height: 3 }); const tempGraphics = new PIXI.Graphics(); tempGraphics.beginFill(16763904); tempGraphics.drawRect(0, 0, 3, 3); tempGraphics.endFill(); // 把临时Graphics的内容渲染到纹理上 app.renderer.render(tempGraphics, { renderTexture: texture }); tempGraphics.destroy(); // 用完临时对象立刻销毁,释放内存 // 使用ParticleContainer,比普通Container高效N倍,专门用来批量处理大量相同元素 const particleContainer = new PIXI.ParticleContainer(10000, { position: true, // 只开启我们需要的属性,进一步优化性能 scale: false, rotation: false, alpha: false }); app.stage.addChild(particleContainer); app.loader.load(setup); function setup() { document.body.appendChild(app.view); app.ticker.add(render); } function render() { const x = randomInt(0, width - 3); const y = randomInt(0, height - 3); // 创建一个使用预生成纹理的Sprite const rectSprite = new PIXI.Sprite(texture); rectSprite.position.set(x, y); particleContainer.addChild(rectSprite); // 可选:如果不想让元素无限增加,可以限制总数,移除最早的元素 // if (particleContainer.children.length > 10000) { // particleContainer.removeChildAt(0); // } } function randomInt(min, max) { return Math.floor(Math.random() * (max - min + 1)) + min; }
为什么这个方案高效?
ParticleContainer会把所有子元素的位置数据打包成GPU能批量处理的缓冲区,大幅减少CPU和GPU之间的通信开销。- 所有矩形共用同一个纹理,避免了重复的纹理加载与绑定,内存占用会比
Graphics方式低几个数量级。
方案3:Canvas纹理动态绘制(适合中等数量场景)
如果你的矩形数量没到十万级,只是几千个左右,也可以用Canvas作为绘制载体,再把Canvas转为PIXI纹理来渲染:
const width = window.innerWidth; const height = window.innerHeight; const app = new PIXI.Application({ width, height }); // 创建一个和窗口一样大的Canvas const canvas = document.createElement('canvas'); canvas.width = width; canvas.height = height; const ctx = canvas.getContext('2d'); ctx.fillStyle = '#FF5500'; // 对应你用的颜色值16763904 // 将Canvas转为PIXI纹理,生成Sprite添加到舞台 const canvasTexture = PIXI.Texture.from(canvas); const canvasSprite = new PIXI.Sprite(canvasTexture); app.stage.addChild(canvasSprite); app.loader.load(setup); function setup() { document.body.appendChild(app.view); app.ticker.add(render); } function render() { const x = randomInt(0, width); const y = randomInt(0, height); // 在Canvas上画矩形 ctx.fillRect(x, y, 3, 3); // 通知PIXI纹理已更新,让舞台重新渲染 canvasTexture.update(); } function randomInt(min, max) { return Math.floor(Math.random() * (max - min + 1)) + min; }
这个方案实现起来更简单,内存占用比Graphics低,但性能不如ParticleContainer,适合中等数量的场景。
内容的提问来源于stack exchange,提问作者Bulkan
相关产品推荐
相关产品推荐

