面向初学者的 p5.js 实战入门指南,详细讲解 Web 编辑器与本地代码工程的搭建步骤,深入解析 setup 与 draw 生命周期及画布坐标系,并带你一步步实现第一个交互式动态图形。
p5.js 新手入门:环境搭建与第一个动态图形
时间:2026-11-02
浏览:87
文章目录
requestAnimationFrame 递归循环。p5.js 将底层复杂绘制与动画时序抽象为清晰的执行逻辑,开发者只需关注画面元素本身。本文将带你从零完成开发环境搭建,透彻理解 p5.js 的画布坐标与生命周期机制,并完整编写出一个具备平滑位移与边缘碰撞反弹的交互式动态图形。
一、开发环境准备:Web 编辑器与本地工程化
编写 p5.js 程序通常有两种途径:官方在线 Web 编辑器与本地代码工程。对于完全刚接触图形编程的场景,在线编辑器能够做到打开浏览器即写即测;而若需要将代码集成至真实前端项目或进行长期维护,本地开发则是标准实践。
访问 p5.js 官方在线编辑器网站(editor.p5js.org),页面左侧是 JavaScript 代码编辑区,右侧是实时渲染画布。系统默认已经为你生成了最基础的代码模板:
function setup() {
createCanvas(400, 400);
}
function draw() {
background(220);
}
点击左上角的“运行(Play)”按钮,右侧便会生成一块 400×400 像素的灰色画布。这种方式免去了所有依赖安装环节,适合快速验证算法或做小实验。
在本地计算机上新建一个文件夹,例如命名为 my-p5-project。在该目录下创建两个基础文件:index.html 和 sketch.js。
1. 编写宿主页面 index.html
在 index.html 中,通过 CDN 引入 p5.js 官方构建脚本,随后引入自己的逻辑脚本 sketch.js。注意脚本的引入顺序不能颠倒:
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>My First p5.js Project</title>
<style>
body {
margin: 0;
padding: 0;
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
background-color: #111;
}
</style>
</head>
<body>
<!-- 步骤一:引入 p5.js 核心库 -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.9.4/p5.min.js"></script>
<!-- 步骤二:引入我们编写的动画逻辑 -->
<script src="sketch.js"></script>
</body>
</html>
2. 编写逻辑脚本 sketch.js
在同级目录下的 sketch.js 文件中写入最简结构:
function setup() {
createCanvas(600, 400);
}
function draw() {
background(30);
}
index.html(即 file:/// 协议),这会导致部分浏览器在后续加载字体、图片或音频资源时触发安全拦截报错。建议在 VS Code 中安装 Live Server 插件,右键选择 “Open with Live Server”,通过本地 HTTP 服务(http://127.0.0.1:5500)运行。
二、p5.js 运行本质:画布坐标与双生命周期
很多初学者写出静态画面很容易,但一涉及“让物体动起来”就容易出现重叠、闪烁或位移失效的问题。理解 p5.js 的两个核心要素是编写动态图形的前提:屏幕坐标系 与 双生命周期机制。
1. 屏幕坐标系的轴向规则
传统的初等数学笛卡尔坐标系中,原点位于左下角或中心,Y 轴向上为正方向。而在网页 Canvas 与 p5.js 中,坐标系规则如下:
- 原点 (0, 0) 严格锚定在画布的左上角。
- 水平 X 轴:从左往右递增,最大宽度为系统变量
width。 - 垂直 Y 轴:从上往下递增,最大高度为系统变量
height。
这意味着,如果要让一个图形“向下坠落”,其 Y 坐标应当不断增加;反之向上升起时,Y 坐标应当不断减小。
2. setup() 与 draw() 的协同生命周期
p5.js 的运行核心依赖两个内置函数,它们的分工极其明确:
| 生命周期函数 | 执行频次与时机 | 核心职能 | 典型调用 API |
|---|---|---|---|
setup() |
仅在程序启动时执行 1 次 | 创建画布环境、定义初始帧率、初始化全局变量 | createCanvas(), frameRate() |
draw() |
在 setup 之后以约 60 FPS 高频循环执行 | 逐帧计算图形新位置、擦除旧帧、重绘几何形体 | background(), circle(), rect() |
draw() 函数中移除了 background(30) 这一行,上一帧绘制的圆形并不会自动消失。随着物体位移,所有历史位置的像素将全部留在画布上,形成一条连续涂抹的“拖尾条带”。动态动画的本质并不是物理世界中物体的连续平移,而是“擦除上一帧 → 计算新位置 → 绘制新画面”在 1 秒内重复 60 次所带来的视觉暂留效应。
三、实战构建:第一个动态反弹图形
掌握了生命周期与坐标规则后,我们通过分步推导的方式,实现一个带有物理反弹与鼠标动态交互的彩色动效球体。
第 1 步:绘制静态几何圆形
在 p5.js 中,绘制一个圆只需要使用 circle(x, y, d) 函数,其中三个参数分别代表圆心 X 坐标、圆心 Y 坐标和直径大小。配合 fill() 设置填充色,配合 noStroke() 移除边框描边:
function setup() {
createCanvas(600, 400);
}
function draw() {
background(20, 35, 55);
noStroke();
fill(119, 185, 174); // 柔和浅青色
circle(300, 200, 40); // 画布中心静态圆
}
第 2 步:提取状态变量并建立逐帧位移
为了让圆形动起来,圆心的 X 与 Y 不能再写死为常数,而必须提取为全局变量。我们在每次 draw() 执行时,为 X 和 Y 分别累加一个位移增量(即速度 vx 与 vy):
// 全局状态定义
let x = 100;
let y = 100;
let vx = 4; // 水平每帧移动 4 像素
let vy = 3; // 垂直每帧移动 3 像素
let radius = 20;
function setup() {
createCanvas(600, 400);
}
function draw() {
background(20, 35, 55);
// 1. 累加坐标产生运动
x += vx;
y += vy;
// 2. 在新位置绘制圆形
noStroke();
fill(119, 185, 174);
circle(x, y, radius * 2);
}
此时运行代码,你会看到小球向右下方匀速飞行。但几秒钟后,小球就会飞出画布边缘,消失在视野之外。我们需要引入边界反弹检测。
第 3 步:加入边界碰撞反弹算法
反弹在物理数学中的本质是对应方向的速度取相反数。当小球碰到右侧边缘时,水平速度 vx 从 +4 变成 -4,小球就会向左折返。
值得注意的是,圆形的定位基准点是圆心。为了避免半个球体穿透边界才触发反弹,必须把球体半径 radius 计入判断条件:
- 左右边界判断:
if (x + radius >= width || x - radius <= 0) { vx = -vx; } - 上下边界判断:
if (y + radius >= height || y - radius <= 0) { vy = -vy; }
第 4 步:注入动态交互与完整代码
为了让图形更具趣味性,我们利用 p5.js 内置的 mouseX、mouseY 和 mouseIsPressed,实现当鼠标在画布上移动时,绘制一条引力引导线;当点击鼠标时,小球瞬时随机变换配色。以下为完整、可直接在 sketch.js 运行的代码:
/**
* p5.js 第一个动态图形:物理反弹小球与鼠标交互
* 文件名:sketch.js
*/
// 小球运动与尺寸状态
let ballX = 150;
let ballY = 120;
let speedX = 4.5;
let speedY = 3.2;
const ballRadius = 22;
// 动态外观配色
let ballColor = { r: 119, g: 185, b: 174 };
function setup() {
// 初始化画布尺寸为 640 x 400
createCanvas(640, 400);
frameRate(60);
}
function draw() {
// 1. 擦除上一帧画面(保持深蓝底色)
background(20, 37, 55);
// 2. 更新位置状态
ballX += speedX;
ballY += speedY;
// 3. 水平方向左右边界检测
if (ballX + ballRadius >= width || ballX - ballRadius <= 0) {
speedX = -speedX; // 水平速度取反
}
// 4. 垂直方向上下边界检测
if (ballY + ballRadius >= height || ballY - ballRadius <= 0) {
speedY = -speedY; // 垂直速度取反
}
// 5. 交互:从小球向当前鼠标指针绘制虚线指示引力
stroke(131, 173, 208, 120);
strokeWeight(1.5);
line(ballX, ballY, mouseX, mouseY);
// 6. 渲染主体小球
noStroke();
fill(ballColor.r, ballColor.g, ballColor.b);
circle(ballX, ballY, ballRadius * 2);
// 7. 渲染中心高光点增强立体感
fill(255, 255, 255, 180);
circle(ballX - 6, ballY - 6, 8);
}
// 鼠标点击事件:瞬时随机更换主体颜色
function mousePressed() {
ballColor = {
r: floor(random(100, 255)),
g: floor(random(150, 255)),
b: floor(random(180, 255))
};
}
四、新手避坑排查与核心要点总结
在初次动手运行以上代码时,若遇到画面不符合预期的情况,可以对照以下四个最常见的初学者问题进行快速自查:
初学者容易把 let ballX = 150 写在 draw() 函数内部。因为 draw() 每秒反复执行 60 次,每次都会把 ballX 重新赋值为 150,哪怕后面写了 ballX += speedX,下一帧又会被重置。因此,需要随时间持续累积的状态变量必须声明在全局作用域。
很多新手试图在文件顶部写 let ballX = width / 2;。然而,在 setup() 中的 createCanvas() 执行完毕之前,p5.js 的 width 和 height 变量尚未完成初始化,此时它们的值为 0 或 undefined,会导致计算得到 NaN 从而使图形完全无法渲染。如果需要依据画布尺寸初始化坐标,应当在 setup() 函数体内对变量进行赋值。
五、结语与进阶延伸
通过本篇指南,我们完成了本地 p5.js 工程的标准配置,厘清了左上角屏幕坐标系与数学坐标系的差异,并通过 setup() 的单次初始化与 draw() 的高频重绘,构建了第一个具备速度向量与边界反弹的动态图形。
完成这个小球案例只是进入创意编程的大门。在掌握了基本位移逻辑后,你可以在此基础上进一步扩展:尝试使用数组批量管理数十个小球,为它们加上基于距离的相互排斥力;或者引入三角函数 sin() 与 cos() 让图形产生如波浪般的有机振荡。图形编程的魅力正在于,用最质朴的数学与代码逻辑,在屏幕上赋予几何体灵动的生命力。
实用工具推荐
绿化、压缩、噪点和虚拟水印,自由调节做旧程度。
选择画质和输出格式,预览压缩前后的大小。