向 豆包 提问:编写一个 p5.js 脚本,模拟 42 个彩色小球在一个3D大球体内部弹跳。每个小球都应留下一条逐渐消失的轨迹。大球体应缓慢旋转,并显示透明的轮廓线。请确保实现适当的碰撞检测,使小球保持在球体内部。
cd p5-demo
copy .\node_modules\p5\lib\p5.min.js .
编写 p5_3d_42_balls.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 如下
<!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 如下
<!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