<html>
<head>
<title>Fruit Physics</title>
</head>
<body>
<style>
body { margin: 0; display: flex; flex-direction: column; align-items: center; justify-content: center; height: 100vh; background: #f0f0f0; }
#box { position: relative; width: 80vw; height: 80vh; max-width: 1200px; max-height: 800px; border: 2px solid black; overflow: hidden; background: white; }
.emoji { position: absolute; font-size: 32px; width: 32px; height: 32px; display: flex; align-items: center; justify-content: center; user-select: none; pointer-events: none; }
#mix { margin-top: 10px; padding: 10px 20px; font-size: 18px; }
@keyframes spin { from { transform: rotate(0deg); } to { transform: rotate(1080deg); } }
#box.spinning { animation: spin 2s linear; }
</style>
<div id="box"></div>
<button id="mix">Mix</button>
<script>
const fruits = ['π', 'π', 'π', 'π', 'π', 'π', 'π', 'π', 'π', 'π«', 'π', 'π', 'π', 'π₯', 'π', 'π₯₯', 'π₯', 'π
', 'π', 'π₯'];
const num = 50;
const gravity = 0.2;
const elasticity = 0.7;
const friction = 0.99;
let particles = [];
let box = document.getElementById('box');
let rect = box.getBoundingClientRect();
let width = rect.width;
let height = rect.height;
const r = 16;
for (let i = 0; i < num; i++) {
let emoji = fruits[Math.floor(Math.random() * fruits.length)];
let div = document.createElement('div');
div.className = 'emoji';
div.innerHTML = emoji;
box.appendChild(div);
let x = Math.random() * (width - 2 * r) + r;
let y = Math.random() * 200 - 300;
let vx = 0;
let vy = 0;
particles.push({ elem: div, x, y, vx, vy, r });
}
let selected = null;
let prevMouseX = 0;
let prevMouseY = 0;
let isMixing = false;
box.addEventListener('mousedown', e => {
if (isMixing) return;
let mx = e.clientX - rect.left;
let my = e.clientY - rect.top;
for (let p of particles) {
let dx = mx - p.x;
let dy = my - p.y;
if (dx * dx + dy * dy < p.r * p.r * 1.5) {
selected = p;
prevMouseX = mx;
prevMouseY = my;
break;
}
}
});
document.addEventListener('mousemove', e => {
if (selected) {
rect = box.getBoundingClientRect();
let mx = e.clientX - rect.left;
let my = e.clientY - rect.top;
selected.x = mx;
selected.y = my;
selected.vx = mx - prevMouseX;
selected.vy = my - prevMouseY;
prevMouseX = mx;
prevMouseY = my;
}
});
document.addEventListener('mouseup', () => {
selected = null;
});
function collide(p1, p2) {
let dx = p2.x - p1.x;
let dy = p2.y - p1.y;
let dist = Math.sqrt(dx * dx + dy * dy);
if (dist < p1.r + p2.r && dist > 0) {
let overlap = (p1.r + p2.r - dist);
let nx = dx / dist;
let ny = dy / dist;
p1.x -= nx * overlap / 2;
p1.y -= ny * overlap / 2;
p2.x += nx * overlap / 2;
p2.y += ny * overlap / 2;
let v1n = p1.vx * nx + p1.vy * ny;
let v2n = p2.vx * nx + p2.vy * ny;
let delta = (v2n - v1n) * elasticity;
p1.vx += delta * nx;
p1.vy += delta * ny;
p2.vx -= delta * nx;
p2.vy -= delta * ny;
}
}
function updateParticle(p) {
p.vx *= friction;
p.vy *= friction;
p.vy += gravity;
p.x += p.vx;
p.y += p.vy;
if (p.x < p.r) { p.x = p.r; p.vx = -p.vx * elasticity; }
if (p.x > width - p.r) { p.x = width - p.r; p.vx = -p.vx * elasticity; }
if (p.y < p.r) { p.y = p.r; p.vy = -p.vy * elasticity; }
if (p.y > height - p.r) {
p.y = height - p.r;
p.vy = -p.vy * elasticity;
if (Math.abs(p.vy) < 0.5) p.vy = 0;
if (Math.abs(p.vx) < 0.5) p.vx = 0;
}
}
function animate() {
rect = box.getBoundingClientRect();
width = rect.width;
height = rect.height;
if (isMixing) {
requestAnimationFrame(animate);
return;
}
for (let i = 0; i < particles.length; i++) {
let p = particles[i];
if (p === selected) continue;
updateParticle(p);
for (let j = i + 1; j < particles.length; j++) {
let q = particles[j];
if (q !== selected) collide(p, q);
}
}
particles.forEach(p => {
p.elem.style.left = `${p.x - p.r}px`;
p.elem.style.top = `${p.y - p.r}px`;
});
requestAnimationFrame(animate);
}
animate();
document.getElementById('mix').addEventListener('click', () => {
if (isMixing) return;
isMixing = true;
box.classList.add('spinning');
box.addEventListener('animationend', () => {
box.classList.remove('spinning');
particles.forEach(p => {
p.x = Math.random() * (width - 2 * p.r) + p.r;
p.y = Math.random() * 200 - 300;
p.vx = (Math.random() - 0.5) * 10;
p.vy = 0;
});
isMixing = false;
}, { once: true });
});
</script>
</body>
</html>*