向 豆包 提问:编写一个 p5.js 脚本,模拟 42 个彩色小球在一个3D大球体内部弹跳。每个小球都应留下一条逐渐消失的轨迹。大球体应缓慢旋转,并显示透明的轮廓线。请确保实现适当的碰撞检测,使小球保持在球体内部。
cd p5-demo
copy .\node_modules\p5\lib\p5.min.js .
编写 p5_3d_42_balls.html 如下
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>3D Bouncing Balls in Sphere</title>
<script src="p5.min.js"></script>
</head>
<body>
<script>
let balls = [];
let sphereRadius = 200;
let rotationSpeed = 0.01;
function setup() {
createCanvas(800, 600, WEBGL);
for (let i = 0; i < 42; i++) {
balls.push(new Ball());
}
}
function draw() {
background(0);
rotateX(frameCount * rotationSpeed);
rotateY(frameCount * rotationSpeed);
// 绘制大球体
stroke(255, 100);
noFill();
sphere(sphereRadius);
// 更新并绘制小球
for (let ball of balls) {
ball.update();
ball.display();
}
}
class Ball {
constructor() {
this.pos = p5.Vector.random3D().mult(random(0, sphereRadius));
this.vel = p5.Vector.random3D().mult(random(1, 3));
this.color = color(random(255), random(255), random(255));
this.trail = [];
this.trailLength = 20;
}
update() {
this.pos.add(this.vel);
// 碰撞检测
let distanceToCenter = this.pos.mag();
if (distanceToCenter > sphereRadius) {
let normal = this.pos.copy().normalize();
this.vel.reflect(normal);
// 确保小球回到球体内
this.pos = normal.mult(sphereRadius - 0.1);
}
// 更新轨迹
this.trail.push(this.pos.copy());
if (this.trail.length > this.trailLength) {
this.trail.shift();
}
}
display() {
// 绘制轨迹
for (let i = 0; i < this.trail.length; i++) {
let alpha = map(i, 0, this.trail.length, 255, 0);
stroke(this.color.levels[0], this.color.levels[1], this.color.levels[2], alpha);
if (i > 0) {
line(this.trail[i - 1].x, this.trail[i - 1].y, this.trail[i - 1].z, this.trail[i].x, this.trail[i].y, this.trail[i].z);
}
}
// 绘制小球
fill(this.color);
noStroke();
push();
translate(this.pos.x, this.pos.y, this.pos.z);
sphere(5);
pop();
}
}
</script>
</body>
</html>
双击打开 p5_3d_42_balls.html

交互式分形树
-
描述: 创建一个分形树,用户可以通过鼠标或键盘控制树的生长角度、分支长度等参数。
-
编写 p5_branch.html 如下
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>p5 branch Example</title>
<script src="p5.min.js"></script>
</head>
<body>
<script>
// 创建分形树,用户可以通过鼠标控制树的生长角度、分支长度等参数。
function setup() {
createCanvas(800, 600);
angleMode(DEGREES);
}
function draw() {
background(50);
stroke(255);
translate(width/2, height);
branch(map(mouseX, 0, width, 50, 150));
}
function branch(len) {
line(0, 0, 0, -len);
translate(0, -len);
if (len > 4) {
push();
rotate(map(mouseY, 0, height, 20, 60));
branch(len * 0.67);
pop();
push();
rotate(-map(mouseY, 0, height, 20, 60));
branch(len * 0.67);
pop();
}
}
</script>
</body>
</html>
双击打开 p5_branch.html
动态波形生成器
-
描述: 创建一个动态波形,用户可以通过鼠标或键盘控制波形的频率、振幅或颜色。
-
编写 p5_wave.html 如下
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>p5 wave Example</title>
<script src="p5.min.js"></script>
</head>
<body>
<script>
let angle = 0;
let amplitude = 100;
let frequency = 0.02;
function setup() {
createCanvas(windowWidth, windowHeight);
}
function draw() {
background(0);
noFill();
stroke(255);
strokeWeight(2);
beginShape();
for (let x = 0; x < width; x += 10) {
let y = height / 2 + sin(angle + x * frequency) * amplitude;
vertex(x, y);
}
endShape();
angle += 0.05;
}
function mouseMoved() {
amplitude = map(mouseY, 0, height, 50, 200);
frequency = map(mouseX, 0, width, 0.01, 0.1);
}
</script>
</body>
</html>
双击打开 p5_wave.html