JavaScript的p5.js库使用详解(下)
JavaScript的p5.js库使用详解(上)https://blog.csdn.net/cnds123/article/details/156979525
JavaScript的p5.js库使用详解(中)https://blog.csdn.net/cnds123/article/details/156981287
本文续接上篇 JavaScript的p5.js库使用详细介绍(中)
五、变换、投影和相机
变换(Transform)、投影(Projection)、相机(Camera) 是 p5.js (WEBGL 模式) 实现 2D/3D 视觉呈现与交互 的三大核心核心功能,三者分工明确、相辅相成、层层递进,共同决定了"最终在画布上能看到什么样的画面"。
✔ 变换 → 改变"物体本身的形态 / 位置";
✔ 投影 → 改变"3D 空间映射到 2D 画布的视觉规则";
✔ 相机 → 改变"观察者看这个 3D 空间的视角"。
三者组合,构成了 p5.js 中所有 2D 动画、3D 立体效果、视角交互的底层逻辑。
(一)变换
p5.js 中的 translate()/rotate()/scale()/shearX()/shearY() 所有变换函数,不是直接变换"图形本身",而是变换"整个坐标系"!
注意,变换顺序影响结果。
可以用push()、pop()隔离变换,避免相互干扰。
平移 → 把坐标系的原点挪到新位置;
旋转 → 把坐标系绕原点转一个角度;
缩放 → 把坐标系的单位像素放大 / 缩小;
坐标系变了,后续绘制的所有图形 / 文字,会"跟着坐标系一起变",看起来就像图形自己动了、转了、变大了。
一旦执行了translate()/rotate(),后续所有绘制的图形都会沿用这个"变换后的坐标系",除非用push()/pop()隔离,或手动重置坐标系。
1. 平移变换 translate()
完整语法(2 种写法,分模式)
2D 模式(无 WEBGL)
translate(x, y)
3D 模式(开启 WEBGL)
translate(x, y, z)
参数详解(严格匹配你掌握的坐标系规则,无偏差)
• x :X 轴平移距离 → 正 = 右移,负 = 左移(2D/3D 完全一致)
• y :Y 轴平移距离 → 正 = 下移,负 = 上移(2D/3D 完全一致,都是下 + 上 -)
• z :3D 专属,Z 轴平移距离 → 正 = 前移(出屏幕 / 朝向你),负 = 后移(入屏幕 / 远离你)
核心作用
修改坐标系的原点位置,比如 translate(100,200) 就是把画布的原点 (0,0) 从默认位置移动到 (100,200),后续所有图形都以这个新原点为基准绘制。
示例
translate(50, 80); // 2D:原点移到画布右下50,80
translate(50, 80, -30); //3D:右移50、下移80、前移30(出屏)
2. 旋转变换
2D 模式 旋转:只有一个函数 rotate(angle)
rotate(angle)
• 参数 angle :旋转弧度值,正数值 = 顺时针旋转,负数值 = 逆时针旋转(和你之前学的 2D 坐标系旋转规则一致)
• 旋转轴:固定绕「垂直于画布的虚拟 Z 轴」旋转,无需手动指定
3D 模式(开启 WEBGL) 旋转:三个专属函数,分轴旋转。
3D 模式下没有单独的rotate(),必须指定旋转轴,三个函数独立使用,可组合旋转,旋转方向匹配你的 3D 坐标系规则,是 3D 开发的核心:
rotateX(angle) // 绕 X 轴旋转(上下翻转,仰头/低头视角)
rotateY(angle) // 绕 Y 轴旋转(左右转圈,水平旋转,最常用!)
rotateZ(angle) // 绕 Z 轴旋转(平面旋转,和2D的rotate()完全等价)
• 参数 angle :弧度值,正负控制旋转方向,rotateY() 是 3D 中最常用的旋转函数(比如立方体水平转圈)
核心作用
修改坐标系的旋转角度,画布整体跟着旋转,后续绘制的图形也跟着旋转。
示例
rotate(PI/4); // 2D:顺时针旋转45度
rotateY(PI/6); // 3D:绕Y轴旋转30度,立方体水平转圈
rotateX(-PI/3); // 3D:绕X轴逆时针旋转60度,立方体上翻
3. 缩放变换 scale()
2D 模式(无 WEBGL)
scale(x, y) // 分别缩放X轴、Y轴
scale(s) // 简写:等比缩放,x=y=s,最常用
3D 模式(开启 WEBGL)
scale(x, y, z) // 分别缩放X/Y/Z三轴
scale(s) // 简写:等比缩放,x=y=z=s
参数详解
• x :X 轴缩放倍数,y:Y 轴缩放倍数,z:3D 专属 Z 轴缩放倍数
• 缩放规则(通用):
① 参数>1 → 放大,例:scale(2) 图形放大 2 倍;
② 0<参数<1 → 缩小,例:scale(0.5) 图形缩小到一半;
③ 参数=1 → 无缩放,默认值;
④ 参数为负数 → 缩放 + 镜像翻转,例:scale(-1) 图形左右翻转;
核心作用
修改坐标系的缩放比例,坐标系被拉伸 / 压缩后,图形的宽高 / 体积也会同步缩放,缩放中心是「当前坐标系原点」。
示例
scale(0.8); // 2D/3D通用:整体等比缩小到0.8倍
scale(2, 1); // 2D:X轴放大2倍,Y轴不变,图形变宽
scale(1,1,0.5); // 3D:Z轴缩小到0.5倍,图形变薄/变远
4. 错切变换 shearX(angle) / shearY(angle)
注意, shearX(angle) / shearY(angle)仅 2D 模式有效!3D 模式无效果。
☆ shearX() 和 shearY() 是纯 2D 变换函数,在开启 WEBGL 的 3D 模式下,这两个函数完全无效,写了也不会有任何效果!
语法(只有 2D 写法)
shearX(angle) // 沿X轴方向错切/倾斜
shearY(angle) // 沿Y轴方向错切/倾斜
参数详解
• angle :错切的角度,单位是弧度,正负控制倾斜方向;
核心作用 & 视觉效果
错切 = "倾斜变形",只改变图形的形状,不改变图形的面积,本质是让坐标系的 X/Y 轴发生"平行倾斜",图形跟着倾斜:
• shearX(angle) :图形的垂直边(Y 轴方向) 向左右倾斜,水平边保持水平;正数值→向右倾斜,负数值→向左倾斜;
• shearY(angle) :图形的水平边(X 轴方向) 向上下倾斜,垂直边保持垂直;正数值→向下倾斜,负数值→向上倾斜;
示例
shearX(PI/6); // 2D:图形垂直边向右倾斜30度,变成平行四边形
shearY(-PI/4); // 2D:图形水平边向上倾斜45度,变成平行四边形
变换函数的5 个通用黄金规则
规则 1:变换是"累加叠加"的,会持续生效
所有变换函数的效果会叠加到上一次的坐标系上,不会重置,例:
translate(50,50); // 原点右移50、下移50
translate(30,20); // 在上一次基础上,再右移30、下移20 → 总位移(80,70)
规则 2:push() + pop() 是"隔离变换"的手段
push() → 保存"当前的坐标系状态"(无变换的原始状态 / 上一次变换后的状态)
pop() → 恢复到 push() 保存的状态,隔断变换的累加效应
这是最核心的用法,也是你绘制多图形时必须用的,例:画两个独立旋转的方块,互不影响
push(); translate(100,100); rotate(PI/4); rect(0,0,50,50); pop();
push(); translate(200,200); rotate(PI/2); rect(0,0,50,50); pop();
规则 3:变换的"执行顺序"会影响最终效果,顺序不同,结果不同
translate() → rotate() → scale() ≠ rotate() → translate() → scale()
2D 旋转黄金公式(永远不变):先平移到图形中心 → 再旋转 → 最后绘制图形
规则 4:所有变换都基于"当前坐标系原点",不是画布的固定原点
比如 2D 默认原点在左上角,translate 后原点变了,后续的 rotate/scale 都是绕「新原点」执行。
规则 5:3D 模式下,变换函数和相机 / 投影兼容
3D 中,translate(x,y,z)/rotateX/Y/Z() 可以和 camera()/perspective()/orbitControl() 混用,变换的是「3D 坐标系」,相机看的是「变换后的坐标系和图形」,无冲突;唯一例外是 shearX/Y 无效。
下面给出综合示例。
示例 1:2D 模式 所有变换函数综合使用
html
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.7.0/p5.js"></script>
<script>
function setup() { createCanvas(400,400); }
function draw() {
background(240);
// 平移+旋转+缩放+错切 组合
push();
translate(200,200); // 原点移到画布中心
rotate(PI/6); // 顺时针旋转30度
scale(1.2); // 放大1.2倍
shearX(PI/12); // 轻微右倾斜15度
fill(255,0,0);
rect(-50,-50,100,100); // 以新原点为中心画正方形
pop();
}
</script>
运行截图:

示例 2:3D 模式 核心变换函数 + orbitControl
运行截图:

(二)投影和相机
1.orbitControl() 这个函数的本质是p5.js 内置的"自动相机控制器"。简单说:没有orbitControl(),你的 3D 视角是"固定死的";有了它,你可以用鼠标自由控制相机视角。
• 它会自动帮你创建一个"虚拟相机",放在画布后方;
• 你鼠标左键拖拽 → 相机绕着 3D 物体旋转视角;
• 你鼠标滚轮缩放 → 相机靠近 / 远离 3D 物体;
• 你鼠标右键平移 → 相机平行移动视角;
• 它还会自动启用"透视投影",让 3D 物体有"近大远小"的真实立体感。
2.perspective() 透视投影(默认模式,最常用)语法:
perspective([fovy], [aspect], [near], [far])
所有参数都是"调整透视的细节",不写这个函数,p5.js 会用默认值,完全够用,新手 99% 的场景不用手动设置:
• fovy:视野角度(单位:弧度),默认值≈PI/3 → 简单说:数值越大,「相机的视野越广」,能看到更多东西,画面里的物体越小;数值越小,「视野越窄」,像望远镜,画面里的物体越大。
• aspect:画布的宽高比 → 一般写 width/height,和你的画布尺寸匹配,避免图形被拉伸变形。
• near:近裁剪面 → 相机能看到的最近距离,比这个距离更近的物体,会被「裁剪掉」(看不见)。
• far:远裁剪面 → 相机能看到的最远距离,比这个距离更远的物体,会被「裁剪掉」(看不见)。
3.ortho() 正交投影(平行投影,无透视感)语法:
ortho([left], [right], [bottom], [top], [near], [far])
参数是"相机能看到的画面边界",不写的话 p5.js 会用默认值,覆盖整个画布:
• left/right/bottom/top:相机能看到的"左右上下边界";
• near/far:相机能看到的"最近 / 最远距离"。
4.camera() 相机位置设置语法:
camera(eyeX, eyeY, eyeZ, centerX, centerY, centerZ, upX, upY, upZ)
camera()函数在 3D 空间中精准设置:观察者的位置、视线的目标点、相机的正立方向,但不会改变任何物体的位置、形态、大小,也不会改变投影规则,只改变"观察者的视角"。
其中有 9 个参数被逗号分成了3 个核心部分,每部分 3 个值,对应 X/Y/Z 轴,逻辑清晰,不用死记顺序:
第一组:eyeX, eyeY, eyeZ → 【相机在哪?】(你的眼睛的位置)
核心含义:设置"相机"这个点,在 3D 坐标系中的绝对坐标,所有值都遵循 3D 坐标系规则:
• eyeX:相机的左右位置 → 正 = 右,负 = 左;
• eyeY:相机的上下位置 → 正 = 下,负 = 上;
• eyeZ:相机的里外位置 → 正 = 内(远离屏幕),负 = 外(靠近屏幕);
第二组:centerX, centerY, centerZ → 【相机看哪?】(视线的目标点)
核心含义:设置相机"镜头对准的那个点",也是 3D 坐标系中的一个点,相机永远朝向这个点看。
• 默认值是 0,0,0 → 画布正中心,也就是所有 3D 物体的默认位置;
• 比如你把目标点设为100,0,0,相机就会一直看向画布右侧的那个点;
第三组:upX, upY, upZ → 【相机的头顶朝向?】(相机的正立方向)
核心含义:设置相机的"头顶"朝向哪个方向,决定相机是"正立""倒立"还是"倾斜"。
• 默认值是 0, -1, 0 → Y 轴负方向(向上),也就是相机"正立",这是最符合人类视角的状态;
• 如果改成0,1,0,相机就会"倒立",画面里的 3D 物体会上下翻转;
//p5.js WEBGL 3D 模式 官方默认相机位置(不用手写,自动生效)
camera(0, 0, (height/2) / tan(PI/6), 0, 0, 0, 0, -1, 0);
下面给出几个示例源码。
示例1:透视投影(默认)→ 近大远小,有立体感
html
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.7.0/p5.js"></script>
<script>
function setup() {
createCanvas(800, 600, WEBGL);
// 默认是perspective()透视投影,不用写
}
function draw() {
background(240);
ambientLight(220);
stroke(0); strokeWeight(1);
// 核心修改1:Z轴位置调换,让红色更靠近相机(z=-100)、蓝色更远(z=200)
// 近处的红色立方体 → 大
push();
translate(0, 0, -100); // z=-100:靠近相机
ambientMaterial(255,0,0);
box(80);
pop();
// 远处的蓝色立方体 → 小
push();
translate(0, 0, 200); // z=200:远离相机
ambientMaterial(0,100,220);
box(80);
pop();
// 核心修改2:添加鼠标视角控制,能旋转看立体效果
orbitControl();
}
</script>
效果:红色立方体大,蓝色立方体小,明显的透视感。开始有遮挡现象,按住鼠标左键拖拽 → 旋转视角,能看到立方体的 3D 立体形态,近大远小的透视感更明显。
示例2:正交投影 → 等大不变形,无立体感
html
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.7.0/p5.js"></script>
<script>
function setup() {
createCanvas(800, 600, WEBGL);
ortho(); // 开启正交投影
}
function draw() {
background(240);
ambientLight(220);
stroke(0); strokeWeight(1);
// 左+远 → 红色立方体 (Z=-200, X=-100)
push(); translate(-100, 0, -200); ambientMaterial(255,0,0); box(80); pop();
// 右+近 → 蓝色立方体 (Z=200, X=100)
push(); translate(100, 0, 200); ambientMaterial(0,100,220); box(80); pop();
orbitControl();
}
</script>
效果:两个立方体大小完全一样。
示例3:手动设置 camera () → 自定义视角看 3D 物体(无 orbitControl)
html
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.7.0/p5.js"></script>
<script>
function setup() {
createCanvas(800, 600, WEBGL);
// 手动设置相机:站在画布右侧+上方+远处,看向画布中心
camera(300, -200, 500, 0, 0, 0, 0, -1, 0);
}
function draw() {
background(240);
ambientLight(220);
stroke(0); strokeWeight(1);
ambientMaterial(255,120,0);
//rotateY(frameCount*0.01); // 注意这句:立方体绕Y轴缓慢自转
box(100);
}
</script>
效果:固定视角,看到立方体的"右上角斜视图",不用鼠标控制,完全是你手动设置的视角。不改其他代码,只改 camera () 里的数值,就能看到完全不同的固定视角,帮你快速掌握手动相机的精髓,都是实用视角:
✔ 视角 1:左侧 + 下方 + 近处 → 左下角斜视图(常用)
camera(-300, 200, 300, 0,0,0, 0,-1,0);
✔ 视角 2:正上方俯视 → 只能看到立方体的顶面
camera(0, -500, 500, 0,0,0, 0,-1,0);
✔ 视角 3:正前方平视 → 只能看到立方体的正面(平面正方形)
camera(0, 0, 500, 0,0,0, 0,-1,0);
✔ 视角 4:右侧 + 正前方 → 只能看到立方体的右侧面
camera(500, 0, 500, 0,0,0, 0,-1,0);
你可以试试,帮助你直观感受理解。
如果想让画面的代码视角固定,但立方体能自转,只需将 "box(100); "一句前的一句:
rotateY(frameCount*0.01);的注释去掉。
这样,立方体在固定视角下缓慢旋转,能看到更多面,立体感更强。
特别提示两点:
1.camera() 手动相机 和 orbitControl() 自动相机,是互斥关系,两者不能同时生效!
- 如果代码里同时写了 camera() 和 orbitControl() → 你的手动相机设置会被完全覆盖失效,画面依然会被鼠标控制;
- 想手动固定视角 → 只写 camera(),删掉 orbitControl();
- 想鼠标自由控制视角 → 只写 orbitControl(),删掉 camera();
原因:orbitControl() 本质是"自动帮你动态刷新 camera () 的参数",会覆盖手动设置的相机位置。
2.perspective()/ortho() (投影模式) + camera() (相机位置),必须写在 draw() 函数的最开头,或 setup() 里!
执行顺序:先设置投影模式 → 再设置相机位置 → 最后绘制 3D 物体(translate/rotate/box/sphere)
原因:投影和相机是"全局视角设置",必须先定义视角,再画物体,否则物体的显示效果会错乱。
六、交互功能
交互功能,本质是:通过"鼠标、键盘、触摸"等用户操作,实时修改 3D 场景的视角、 物体状态、动画参数,让静态的2D、3D 画面变成可操控、可互动的动态场景。
1. 鼠标交互
通过 p5.js 原生的鼠标事件函数,监听鼠标的"点击、松开、悬停"操作,实现"点击物体触发动作、鼠标移到物体上变色"等交互效果,2D/3D 通用,无任何兼容性问题。核心鼠标事件函数(全部写在draw()外面,独立定义):
(1). mousePressed() → 鼠标按下时触发一次(点击);
(2). mouseReleased() → 鼠标松开时触发一次;
(3). mouseIsPressed → 全局变量,判断鼠标是否处于按下状态(长按持续触发)。
示例源码:
html
<p>移动鼠标试试看</p>
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.7.0/p5.js"></script>
<script>
function setup() {
createCanvas(400, 400);
}
function draw() {
// 根据鼠标位置设置背景色
let r = map(mouseX, 0, width, 0, 255);
let g = map(mouseY, 0, height, 0, 255);
background(r, g, 150);
// 绘制随鼠标移动的形状
fill(255, 255, 0);
ellipse(mouseX, mouseY, 50, 50);
// 显示鼠标坐标
fill(0);
//text(`X: ${mouseX}, Y: ${mouseY}`, 20, 20);
text(`X: ${round(mouseX)}, Y: ${round(mouseY)}`, 20, 20); //用round()取整,完美消除小数位
}
</script>
运行截图

2. 键盘交互
通过 p5.js 原生的键盘事件 / 键盘变量,监听键盘的「按键按下 / 松开」操作,实现 「按键控制 3D 物体移动 / 旋转 / 缩放、切换投影模式、暂停动画」 等核心交互,2D/3D 通用,是「操控物体行为」的最佳方式,没有之一。
(1). keyIsPressed → 全局变量,判断「是否有任意按键按下」(长按持续触发);
(2). key → 全局变量,获取「当前按下的具体按键」(比如 key === 'w'、key === 'ArrowUp')。
示例源码:
html
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.7.0/p5.js"></script>
<script>
let x = 200;
let y = 200;
function setup() {
createCanvas(400, 400);
}
function draw() {
background(220);
fill(0, 100, 200);
ellipse(x, y, 50, 50);
fill(0);
text("使用方向键移动", 150, 380);
}
function keyPressed() {
// 方向键控制移动
if (keyCode === LEFT_ARROW) {
x -= 10;
} else if (keyCode === RIGHT_ARROW) {
x += 10;
} else if (keyCode === UP_ARROW) {
y -= 10;
} else if (keyCode === DOWN_ARROW) {
y += 10;
}
// 限制在画布内
x = constrain(x, 25, width - 25);
y = constrain(y, 25, height - 25);
}
</script>
运行截图

七、动画和粒子系统简介
1. 动画基础
p5.js 实现动画的核心原理:动画 = draw() 函数的"自动循环重绘" + "变量的逐帧增量修改"。p5.js 实现动画前面有些例子已经展示过了,没有任何高深奥秘,是已经介绍过的基础知识的运用,现在概括总结。要点:
• 静态图形,只要逐帧修改变量,就能变成动画。
• 动画本质是"帧循环 + 变量增量",2D 控 X/Y,3D 控 X/Y/Z;
• 变换是动画载体,交互是动画灵魂,投影 / 相机是 3D 动画的视角补充。
简单示例
html
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.7.0/p5.js"></script>
<script>
let angle = 0;
let size = 50;
function setup() {
createCanvas(400, 400);
angleMode(DEGREES); // 使用角度制
}
function draw() {
background(220);
// 平移坐标系
push();
translate(width/2, height/2);
// 旋转
rotate(angle);
// 缩放
let scaleFactor = map(sin(frameCount * 2), -1, 1, 0.5, 1.5);
scale(scaleFactor);
// 绘制
fill(100, 200, 255);
rect(-50, -50, 100, 100);
pop();
// 更新角度
angle += 2;
}
</script>
运行截图

2. 粒子系统基础
粒子系统本质是"大量微小独立粒子的群体动画集合",用简单的规则控制无数粒子的行为,形成极具视觉效果的动态场景。本质是"数组 + 循环 + 基础动画"。
粒子系统是游戏、创意编程、可视化的核心技术,几乎所有炫酷的动态效果,都是粒子系统实现的:
√ 2D 场景:雨滴、雪花、火焰、爆炸、烟雾、流水、画笔轨迹、星空、烟花;
√ 3D 场景:3D 星云、行星轨迹、立体爆炸、粒子流、3D 烟雾、光影特效。
粒子系统的"2D/3D 通用优化规则"(避坑)
• 必须清理"死亡粒子":用splice()删除生命周期结束的粒子,否则数组会越来越大,导致动画卡顿;
• 控制粒子数量:一次生成的粒子数不要太多(比如不超过 1000),否则会影响性能;
• 3D 粒子可以用sphere()/box()做粒子,也可以用point()做光点,后者性能更好。
示例
html
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.7.0/p5.js"></script>
<script>
let particles = [];
function setup() {
createCanvas(600, 400);
// 初始化粒子
for (let i = 0; i < 100; i++) {
particles.push({
x: random(width),
y: random(height),
vx: random(-1, 1),
vy: random(-1, 1),
size: random(5, 15),
color: [random(255), random(255), random(255)]
});
}
}
function draw() {
background(0, 20); // 半透明背景创建拖尾效果
// 更新和绘制所有粒子
for (let p of particles) {
// 更新位置
p.x += p.vx;
p.y += p.vy;
// 边界检查
if (p.x < 0 || p.x > width) p.vx *= -1;
if (p.y < 0 || p.y > height) p.vy *= -1;
// 鼠标吸引
let dx = mouseX - p.x;
let dy = mouseY - p.y;
let distance = sqrt(dx*dx + dy*dy);
if (distance < 100) {
p.vx += dx * 0.001;
p.vy += dy * 0.001;
}
// 绘制粒子
fill(p.color[0], p.color[1], p.color[2]);
noStroke();
ellipse(p.x, p.y, p.size, p.size);
}
}
</script>
运行截图
