如何优化p5.js像素模拟器项目?性能优化方案求助
p5.js像素模拟器性能优化方案
我开发了一个p5.js像素模拟器,但运行性能很差,生成沙子几秒后就卡顿,违背了网页应用流畅的初衷。我怀疑问题出在粒子数组的各类检查操作上,现在需要全面的优化方案,不仅针对粒子数组,还要覆盖整个代码逻辑。
原项目代码
主文件代码
let alter = true; let particles = [] function setup() { let canvas = createCanvas(windowWidth, windowHeight); frameRate(120); } var a = ['rgb(244,220,148)', 'rgb(236,211,140)', 'rgb(252,228,156)', 'rgb(252,220,149)', 'rgb(244,212,148)', 'rgb(228,204,132)', 'rgb(240,220,156)'] function sandColor() { return color(a[Math.floor(Math.random() * a.length)]); } function drect(c, x, y, l, w) { noStroke(); fill(c); rect(x, y, l, w); } class Particle { constructor(p, c, x, y, s) { this.p = p; this.c = c; this.x = x; this.y = y; this.s = s; } draw() { drect(this.c, this.x, this.y, this.s, this.s); } } function check(x, y) { return color(get(x, y)); } function draw() { drect(color(37, 150, 190), 0, 0, windowWidth, windowHeight) tw = 4; th = 4; for (let i = 0; i < particles.length; i++) { particles[i].draw() } alter = !(alter) if (!alter) { for (let i = 0; i < particles.length; i++) { if (particles[i].p == 's') { let down = false if (JSON.stringify(check(particles[i].x, particles[i].y + 4).levels) == '[37,150,190,255]') { particles[i].y += 4; down = true; } if (!down) { let r = Math.floor(Math.random() * 2); if (r == 0) { if (JSON.stringify(check(particles[i].x - 4, particles[i].y + 4).levels) == '[37,150,190,255]') { particles[i].y += 4; particles[i].x -= 4; } else { if (JSON.stringify(check(particles[i].x + 4, particles[i].y + 4).levels) == '[37,150,190,255]') { particles[i].y += 4; particles[i].x += 4; } } } } } } if (mouseIsPressed) { for (let i = 0; i < 6; i++) { for (let j = 0; j < 6; j++) { let p = 's' let c = sandColor() let x = (Math.floor(mouseX / tw)) * tw + (i * 4) - 9; let y = (Math.floor(mouseY / th)) * th + (j * 4) - 9; let s = 4; let sand = new Particle(p, c, x, y, s) let d = true; for (let m = 0; m < particles.length; m++) { if (particles[m].x == x && particles[m].y == y && particles[m].p == "s") { d = false; } } if (d) { drect(c, x, y, s, s) particles.push(sand) } } } } } } function windowResized() { resizeCanvas(windowWidth, windowHeight); } document.addEventListener('contextmenu', event => event.preventDefault());
HTML引入
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.2/p5.min.js"></script>
优化方案与实施
1. 替换画布颜色检测为网格哈希表
get(x,y)和JSON.stringify对比颜色是性能瓶颈核心,改用网格记录粒子位置:
- 创建全局
grid对象,键为x,y拼接字符串,值存储粒子类型 - 检查位置是否为空直接查询
grid,时间复杂度O(1)
2. 移除粒子添加时的全数组遍历
利用grid直接判断目标位置是否有粒子,避免O(n)的遍历检查:
- 鼠标生成粒子时,先查
grid[${x},${y}],不存在才添加新粒子
3. 优化粒子更新循环
- 逆序遍历沙子粒子,避免下方粒子移动干扰上方粒子判断
- 单独维护沙子数组
sandParticles,减少类型判断分支开销
4. 减少重绘开销
- 用
background()替代自定义drect绘制背景,p5.js内部优化更高效 - 批量绘制所有粒子,减少
rect()调用次数
5. 常量与预计算优化
- 定义
CELL_SIZE = 4全局常量,统一粒子大小和网格单元尺寸 - 预转换沙子颜色数组为p5.js
color对象,避免每次调用color()转换字符串
6. 简化帧更新逻辑
去掉alter隔帧更新逻辑,每帧都处理粒子下落,保证动画流畅性
优化后完整代码
const CELL_SIZE = 4; const BACKGROUND_COLOR = color(37, 150, 190); // 预转换沙子颜色为p5.js color对象 const SAND_COLORS = [ color(244,220,148), color(236,211,140), color(252,228,156), color(252,220,149), color(244,212,148), color(228,204,132), color(240,220,156) ]; let sandParticles = []; let grid = {}; // 键格式:"x,y",值为粒子类型(如's') function setup() { createCanvas(windowWidth, windowHeight); frameRate(120); noStroke(); } function sandColor() { return SAND_COLORS[Math.floor(Math.random() * SAND_COLORS.length)]; } class SandParticle { constructor(c, x, y) { this.c = c; this.x = x; this.y = y; // 加入网格 grid[`${x},${y}`] = 's'; } draw() { fill(this.c); rect(this.x, this.y, CELL_SIZE, CELL_SIZE); } update() { let downX = this.x; let downY = this.y + CELL_SIZE; let downLeftX = this.x - CELL_SIZE; let downRightX = this.x + CELL_SIZE; let moved = false; // 检查正下方 if (!grid[`${downX},${downY}`]) { // 从网格移除旧位置 delete grid[`${this.x},${this.y}`]; this.y = downY; grid[`${this.x},${this.y}`] = 's'; moved = true; } else if (!moved) { // 随机检查左下或右下 const checkLeft = Math.random() < 0.5; if (checkLeft && !grid[`${downLeftX},${downY}`]) { delete grid[`${this.x},${this.y}`]; this.x = downLeftX; this.y = downY; grid[`${this.x},${this.y}`] = 's'; } else if (!grid[`${downRightX},${downY}`]) { delete grid[`${this.x},${this.y}`]; this.x = downRightX; this.y = downY; grid[`${this.x},${this.y}`] = 's'; } } } } function draw() { // 绘制背景 background(BACKGROUND_COLOR); // 批量绘制所有沙子粒子 sandParticles.forEach(particle => particle.draw()); // 逆序更新沙子粒子,避免下方粒子移动干扰上方 for (let i = sandParticles.length - 1; i >= 0; i--) { sandParticles[i].update(); } // 鼠标生成沙子 if (mouseIsPressed) { const baseX = Math.floor(mouseX / CELL_SIZE) * CELL_SIZE; const baseY = Math.floor(mouseY / CELL_SIZE) * CELL_SIZE; // 生成3x3区域的沙子(原6x6调整为3x3避免粒子过多,可按需修改) for (let i = -1; i <= 1; i++) { for (let j = -1; j <= 1; j++) { const x = baseX + i * CELL_SIZE; const y = baseY + j * CELL_SIZE; // 检查网格中是否已有粒子 if (!grid[`${x},${y}`]) { const sand = new SandParticle(sandColor(), x, y); sandParticles.push(sand); } } } } } function windowResized() { resizeCanvas(windowWidth, windowHeight); // 窗口 resize 时清空网格和粒子(可选,也可保留但需处理超出画布的粒子) grid = {}; sandParticles = []; } document.addEventListener('contextmenu', event => event.preventDefault());
内容的提问来源于stack exchange,提问作者user19897564
相关产品推荐
相关产品推荐

