You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React环境下p5.js Sketch窗口放大后崩溃问题排查

问题:react-p5实现柏林噪声流场时,放大窗口Sketch崩溃

我在React项目中使用react-p5实现了Perlin噪声流场,初始浏览器窗口尺寸设为1000×1000。缩小窗口时一切正常,但拖拽窗口边缘或全屏操作放大窗口时,Sketch会崩溃,无法从初始窗口尺寸放大。

问题根源

崩溃的核心原因是窗口放大时,windowResized函数持续向particles数组追加新粒子,导致数组无限膨胀:

  • 每次窗口尺寸变大,nums会计算出更大的数值,循环从0到nums不断将新粒子push进数组
  • 粒子数组越变越大,draw循环中的遍历操作会占用极高CPU和内存,最终导致Sketch崩溃
  • 缩小窗口时,nums值变小,但代码未清理超出数量的旧粒子,虽不会立刻崩溃,但长期存在内存泄漏风险

修复步骤

  1. 重置粒子数组而非追加:窗口resize时先清空旧的particles数组,再重新生成对应数量的粒子,避免数组无限膨胀
  2. 优化粒子数量计算逻辑:根据窗口面积计算粒子数,比原有的(w+h)/1.5更合理,避免大窗口下粒子数暴增
  3. 减少DOM操作开销:提前获取CSS变量值,避免在draw循环中重复调用getComputedStyle

修复后的完整代码

// A Perlin Noise Flow Field
import Sketch from 'react-p5'
import "../../App.css"

const Flow = () => {
    let w, h, nums;
    let density = 0.005
    let zOff = 0;
    let scl = 0.001;
    let speed = 12;
    let particles = []
    // 提前获取CSS变量,避免draw循环重复操作DOM
    const bgColor = getComputedStyle(document.documentElement).getPropertyValue('--main-bg-color');
    const textColor = getComputedStyle(document.documentElement).getPropertyValue('--main-text-color');

    const setup = (p5, canvasParentRef) => {
        w = p5.windowWidth
        h = p5.windowHeight
        // 根据窗口面积计算粒子数,避免大窗口粒子过多
        nums = Math.ceil((w * h) / 8000);
        p5.frameRate(30)
        p5.createCanvas(w, h, p5.WEBGL).parent(canvasParentRef)
        initParticles(p5);
    }

    const draw = (p5) => {
        p5.background(bgColor);
        p5.stroke(textColor);
        p5.translate(-w / 2, -h / 2)
        p5.strokeWeight(3)

        for (let i = 0; i < particles.length; i++) {
            let p = particles[i]
            p5.point(p.x, p.y)
            let n = p5.noise(p.x * scl, p.y * scl, zOff * 3);
            let angle = p5.TWO_PI * n
            p.x += Math.cos(angle) * speed
            p.y += Math.sin(angle) * speed
            if (!onScreen(p)) {
                p.x = p5.random(w)
                p.y = p5.random(h)
            }
        }

        zOff += 0.002
    }

    const onScreen = (v) => {
        return v.x >= 0 && v.x <= w && v.y >= 0 && v.y <= h
    }

    const initParticles = (p5) => {
        // 清空旧数组并重新生成粒子
        particles = [];
        for (let i = 0; i < nums; i++) {
            particles.push(p5.createVector(p5.random(w), p5.random(h)))
        }
    }

    const windowResized = (p5) => {
        w = p5.windowWidth;
        h = p5.windowHeight;
        nums = Math.ceil((w * h) / 8000);
        p5.resizeCanvas(w, h);
        initParticles(p5);
    };

    return (
        <Sketch setup={setup} draw={draw} windowResized={windowResized} />
    )
}

export default Flow

额外优化建议

  • 可以给粒子数量设置上限,避免超大型窗口时性能下降
  • 若需保留部分旧粒子,可只补充差值数量的粒子,而非全部重置(重置方式最简单高效)
  • 可根据设备性能动态调整frameRate,平衡流畅度与性能

内容的提问来源于stack exchange,提问作者Shivam Singh

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.09 04:07:33