
本文详解在单页中嵌入多个 p5.js 实例时,因作用域混淆导致页面空白的常见错误,并提供标准化的 iife 封装方案、变量/函数作用域规范及 html 配合要点。
当使用 p5.js 的实例模式(Instance Mode)在单页中运行多个 sketch(如分别渲染页眉、页脚动画),最易踩坑的并非 HTML 结构,而是 JavaScript 作用域管理不当——尤其是混淆了 p5 全局对象方法与用户自定义变量/函数的调用方式。你的代码中大量误加 f. 前缀(如 f.particles2[i].move()),同时又遗漏关键处(如 mouseX 应为 f.mouseX),直接触发运行时错误(如 ReferenceError: particles2 is not defined),最终导致 p5 编辑器或浏览器页面“变白”(即脚本崩溃、渲染中断)。
✅ 正确的作用域划分原则
- 所有 p5 内置 API(如 createCanvas, background, mouseX, random, sqrt 等)必须通过 f. 调用;
- 所有你声明的变量(如 particles2, viscosity2, c2)、函数(如 handleFooterInteractions)、类(如 Particle2)均属于当前闭包作用域,禁止加 f. 前缀;
- 类内部访问 p5 方法仍需 f.(如 f.fill(), f.ellipse()),但访问自身属性用 this. 或直接引用(如 this.xPos);
- new p5(sketch, 'target-id') 中的 'target-id' 必须是 DOM 中已存在的元素 ID,且该元素需在 script 执行前存在(建议 script 放在 </body> 前)。
? 修正后的标准模板(含注释)
var s1 = function(f) {
// ✅ 自定义变量:无 f. 前缀
var particles2 = [];
var viscosity2;
var c2;
f.setup = function() {
// ✅ p5 方法必须带 f.
f.createCanvas(f.windowWidth, f.windowHeight);
f.frameRate(60);
f.noStroke();
c2 = f.color(13, 104, 167); // ✅ color 是 p5 方法 → f.color()
viscosity2 = 0.8;
for (var i = 0; i < 900; i++) {
// ✅ new Particle2 是自定义类 → 无 f.
particles2.push(new Particle2(
f.random(f.width / 8, f.width / 4),
f.random(f.height - f.height / 18, f.height + f.height / 15),
c2
));
}
};
f.draw = function() {
f.background(0);
// ✅ 自定义函数 → 无 f.
handleFooterInteractions();
// ✅ 访问自定义数组 → particles2.length(非 f.particles2)
for (var i = 0; i < particles2.length; i++) {
particles2[i].move(); // ✅ 自定义方法
particles2[i].display(); // ✅ 自定义方法
}
};
// ✅ 自定义类定义(无 f.),但内部调用 p5 方法需 f.
Particle2 = function(x, y, c) {
this.xPos = x;
this.yPos = y;
this.xVel = 0;
this.yVel = 0;
this.mass = f.random(0.005, 0.02); // ✅ random 是 p5 方法
this.colour = c;
this.move = function() {
this.xPos += this.xVel;
this.yPos += this.yVel;
};
this.display = function() {
f.fill(this.colour); // ✅ fill 是 p5 方法
f.ellipse(this.xPos, this.yPos, this.mass * 1000, this.mass * 1000);
};
};
// ✅ 自定义函数定义(无 f.)
handleFooterInteractions = function() {
for (var i = 0; i < particles2.length; i++) {
var accX = 0, accY = 0;
// 粒子间作用力(访问自定义数组 → particles2[j])
for (var j = 0; j < particles2.length; j++) {
if (i !== j) {
var dx = particles2[j].xPos - particles2[i].xPos;
var dy = particles2[j].yPos - particles2[i].yPos;
var dis = f.sqrt(dx * dx + dy * dy); // ✅ sqrt 是 p5 方法
if (dis < 1) dis = 1;
var force = (dis - 600) * particles2[j].mass / dis;
accX += force * dx;
accY += force * dy;
}
}
// 鼠标交互(✅ mouseX/mouseY 是 p5 属性 → 必须 f.mouseX)
var mx = f.mouseX - particles2[i].xPos;
var my = f.mouseY - particles2[i].yPos;
var dis = f.sqrt(mx * mx + my * my);
if (dis < 40) dis = 40;
if (dis > 50) dis = 50;
var force = (dis - 50) / (5 * dis);
accX += force * mx;
accY += force * my;
// ✅ 更新自定义粒子速度(访问自身属性,无 f.)
particles2[i].xVel = particles2[i].xVel * viscosity2 + accX * particles2[i].mass;
particles2[i].yVel = particles2[i].yVel * viscosity2 + accY * particles2[i].mass;
}
};
};
// ✅ 启动实例:传入 sketch 函数,不传 target ID(由 createCanvas 自动挂载到 body)
// 若需指定容器,请确保 HTML 中存在 <div id="footer"></div>,并改为:
// var myp5 = new p5(s1, 'footer');
var myp5 = new p5(s1);⚠️ 关键注意事项
HTML 容器必须提前声明:若使用 new p5(s1, 'footer'),务必在 <body> 中放置 <div id="footer"></div>,否则 p5 无法找到挂载点,抛出错误并中断执行;
不要混用全局模式与实例模式:避免在实例内写 function setup(){} 或直接调用 background(),这会污染全局作用域并引发冲突;
-
多实例部署示例:
<div id="header-sketch"></div> <main>...</main> <div id="footer-sketch"></div> <script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.9.4/p5.min.js"></script> <script>/* header sketch */</script> <script>/* footer sketch (如本文 s1) */</script>
每个 sketch 使用独立的 new p5(..., 'id'),互不干扰;
调试技巧:打开浏览器开发者工具(F12),在 Console 标签页查看具体报错行,90% 的“白屏”源于 ReferenceError 或 TypeError,精准定位 f. 误用位置。
遵循以上规范,即可稳定运行多个 p5.js 实例,彻底告别页面空白问题。核心口诀:p5 的归 f.,你的归自己。










