<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Interactive Circle Collision NFT</title>
<script src="
cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.0/p5.js"></script>
<style>
body { margin: 0; padding: 0; }
canvas { display: block; }
</style>
</head>
<body>
<script>
let balls = [];
const numBalls = 7;
let bgColor1, bgColor2, targetBgColor1, targetBgColor2;
const bgColorChangeSpeed = 0.005;
let draggedBall = null;
class Ball {
constructor(x, y, r) {
this.position = createVector(x, y);
this.velocity = p5.Vector.random2D().mult(3);
this.r = r;
this.m = r * 0.1;
this.color = color(random(255), random(255), random(255));
this.targetColor = color(random(255), random(255), random(255));
this.colorChangeSpeed = 0.01;
this.isDragging = false;
}
update() {
if (!this.isDragging) {
this.position.add(this.velocity);
}
this.updateColor();
}
updateColor() {
this.color = lerpColor(this.color, this.targetColor, this.colorChangeSpeed);
if (this.color.toString() === this.targetColor.toString()) {
this.targetColor = color(random(255), random(255), random(255));
}
}
checkBoundaryCollision() {
if (this.position.x > width - this.r || this.position.x < this.r) {
this.velocity.x *= -1;
this.position.x = constrain(this.position.x, this.r, width - this.r);
}
if (this.position.y > height - this.r || this.position.y < this.r) {
this.velocity.y *= -1;
this.position.y = constrain(this.position.y, this.r, height - this.r);
}
}
checkCollision(other) {
let distanceVect = p5.Vector.sub(other.position, this.position);
let distanceVectMag = distanceVect.mag();
let minDistance = this.r + other.r;
if (distanceVectMag < minDistance) {
let distanceCorrection = (minDistance - distanceVectMag) / 2.0;
let correctionVector = distanceVect.copy().normalize().mult(distanceCorrection);
other.position.add(correctionVector);
this.position.sub(correctionVector);
let theta = distanceVect.heading();
let sine = sin(theta);
let cosine = cos(theta);
let bTemp = [createVector(), createVector()];
bTemp[1].x = cosine * distanceVect.x + sine * distanceVect.y;
bTemp[1].y = cosine * distanceVect.y - sine * distanceVect.x;
let vTemp = [createVector(), createVector()];
vTemp[0].x = cosine * this.velocity.x + sine * this.velocity.y;
vTemp[0].y = cosine * this.velocity.y - sine * this.velocity.x;
vTemp[1].x = cosine * other.velocity.x + sine * other.velocity.y;
vTemp[1].y = cosine * other.velocity.y - sine * other.velocity.x;
let vFinal = [createVector(), createVector()];
vFinal[0].x = ((this.m - other.m) * vTemp[0].x + 2 * other.m * vTemp[1].x) / (this.m + other.m);
vFinal[0].y = vTemp[0].y;
vFinal[1].x = ((other.m - this.m) * vTemp[1].x + 2 * this.m * vTemp[0].x) / (this.m + other.m);
vFinal[1].y = vTemp[1].y;
bTemp[0].x += vFinal[0].x;
bTemp[1].x += vFinal[1].x;
let bFinal = [createVector(), createVector()];
bFinal[0].x = cosine * bTemp[0].x - sine * bTemp[0].y;
bFinal[0].y = cosine * bTemp[0].y + sine * bTemp[0].x;
bFinal[1].x = cosine * bTemp[1].x - sine * bTemp[1].y;
bFinal[1].y = cosine * bTemp[1].y + sine * bTemp[1].x;
other.position.x = this.position.x + bFinal[1].x;
other.position.y = this.position.y + bFinal[1].y;
this.position.add(bFinal[0]);
this.velocity.x = cosine * vFinal[0].x - sine * vFinal[0].y;
this.velocity.y = cosine * vFinal[0].y + sine * vFinal[0].x;
other.velocity.x = cosine * vFinal[1].x - sine * vFinal[1].y;
other.velocity.y = cosine * vFinal[1].y + sine * vFinal[1].x;
}
}
display() {
noStroke();
fill(this.color);
ellipse(this.position.x, this.position.y, this.r * 2);
}
contains(px, py) {
let d = dist(px, py, this.position.x, this.position.y);
return d < this.r;
}
drag(px, py) {
this.position.x = px;
this.position.y = py;
}
}
function setup() {
createCanvas(710, 400);
for (let i = 0; i < numBalls; i++) {
let x = random(width);
let y = random(height);
let r = random(20, 40);
balls.push(new Ball(x, y, r));
}
bgColor1 = color(random(255), random(255), random(255));
bgColor2 = color(random(255), random(255), random(255));
targetBgColor1 = color(random(255), random(255), random(255));
targetBgColor2 = color(random(255), random(255), random(255));
}
function draw() {
bgColor1 = lerpColor(bgColor1, targetBgColor1, bgColorChangeSpeed);
bgColor2 = lerpColor(bgColor2, targetBgColor2, bgColorChangeSpeed);
if (bgColor1.toString() === targetBgColor1.toString()) {
targetBgColor1 = color(random(255), random(255), random(255));
}
if (bgColor2.toString() === targetBgColor2.toString()) {
targetBgColor2 = color(random(255), random(255), random(255));
}
for(let y = 0; y < height; y++){
let inter = map(y, 0, height, 0, 1);
let c = lerpColor(bgColor1, bgColor2, inter);
stroke(c);
line(0, y, width, y);
}
for (let i = 0; i < balls.length; i++) {
let b = balls[i];
b.update();
b.display();
b.checkBoundaryCollision();
for (let j = i + 1; j < balls.length; j++) {
b.checkCollision(balls[j]);
}
}
}
function mousePressed() {
for (let i = balls.length - 1; i >= 0; i--) {
if (balls[i].contains(mouseX, mouseY)) {
draggedBall = balls[i];
draggedBall.isDragging = true;
break;
}
}
}
function mouseDragged() {
if (draggedBall) {
draggedBall.drag(mouseX, mouseY);
}
}
function mouseReleased() {
if (draggedBall) {
draggedBall.isDragging = false;
draggedBall = null;
}
}
</script>
</body>
</html>