<script>
class FruitPhysics {
constructor() {
this.container = document.getElementById('gameContainer');
this.fruits = [];
this.grabbedFruit = null;
this.grabOffset = { x: 0, y: 0 };
this.isMixing = false;
this.mixAngle = 0;
this.fruitsGrabbed = 0;
this.fruitEmojis = [
'🍎', '🍌', '🍇', '🍓', '🍊', '🍋', '🍉', '🍑', '🍍', '🥭',
'🍒', '🍐', '🥝', '🍅', '🥥', '🥑', '🍈', '🫐', '🍏', '🥭',
'🍎', '🍌', '🍇', '🍓', '🍊', '🍋', '🍉', '🍑', '🍍', '🥭',
'🍒', '🍐', '🥝', '🍅', '🥥', '🥑', '🍈', '🫐', '🍏', '🥭',
'🍎', '🍌', '🍇', '🍓', '🍊', '🍋', '🍉', '🍑', '🍍', '🥭'
];
this.init();
this.createFruits();
this.animate();
this.setupEventListeners();
}
init() {
const rect = this.container.getBoundingClientRect();
this.bounds = {
left: 10,
right: rect.width - 50,
top: 10,
bottom: rect.height - 50
};
}
createFruits() {
for (let i = 0; i < 50; i++) {
const fruit = document.createElement('div');
fruit.className = 'fruit';
fruit.textContent = this.fruitEmojis[i];
fruit.style.left = Math.random() * (this.bounds.right - 50) + 'px';
fruit.style.top = Math.random() * 200 + 'px';
this.container.appendChild(fruit);
this.fruits.push({
element: fruit,
x: parseFloat(fruit.style.left),
y: parseFloat(fruit.style.top),
vx: (Math.random() - 0.5) * 4,
vy: Math.random() * 2 + 1,
radius: 25,
mass: 1,
isGrabbed: false
});
}
}
updateFruit(fruit, deltaTime) {
if (fruit.isGrabbed || this.isMixing) return;
// Apply gravity
fruit.vy += 0.2 * deltaTime;
// Update position
fruit.x += fruit.vx * deltaTime;
fruit.y += fruit.vy * deltaTime;
// Boundary collision
if (fruit.x < this.bounds.left) {
fruit.x = this.bounds.left;
fruit.vx *= -0.8;
} else if (fruit.x > this.bounds.right) {
fruit.x = this.bounds.right;
fruit.vx *= -0.8;
}
if (fruit.y < this.bounds.top) {
fruit.y = this.bounds.top;
fruit.vy *= -0.8;
} else if (fruit.y > this.bounds.bottom) {
fruit.y = this.bounds.bottom;
fruit.vy *= -0.8;
fruit.vx *= 0.98; // Friction
}
// Apply mixing rotation
if (this.isMixing) {
const centerX = this.container.clientWidth / 2;
const centerY = this.container.clientHeight / 2;
const dx = fruit.x - centerX;
const dy = fruit.y - centerY;
const distance = Math.sqrt(dx * dx + dy * dy);
const angle = Math.atan2(dy, dx) + 0.05;
fruit.x = centerX + Math.cos(angle) * distance;
fruit.y = centerY + Math.sin(angle) * distance;
// Add centrifugal force
fruit.vx += dx * 0.001;
fruit.vy += dy * 0.001;
}
// Update element position
fruit.element.style.left = fruit.x + 'px';
fruit.element.style.top = fruit.y + 'px';
}
checkCollisions() {
for (let i = 0; i < this.fruits.length; i++) {
for (let j = i + 1; j < this.fruits.length; j++) {
const fruit1 = this.fruits[i];
const fruit2 = this.fruits[j];
if (fruit1.isGrabbed || fruit2.isGrabbed) continue;
const dx = fruit2.x - fruit1.x;
const dy = fruit2.y - fruit1.y;
const distance = Math.sqrt(dx * dx + dy * dy);
const minDistance = fruit1.radius + fruit2.radius;
if (distance < minDistance) {
// Collision response
const angle = Math.atan2(dy, dx);
const overlap = minDistance - distance;
// Separate fruits
const moveX = (overlap * 0.5) * Math.cos(angle);
const moveY = (overlap * 0.5) * Math.sin(angle);
fruit1.x -= moveX;
fruit1.y -= moveY;
fruit2.x += moveX;
fruit2.y += moveY;
// Bounce
const speed1 = Math.sqrt(fruit1.vx * fruit1.vx + fruit1.vy * fruit1.vy);
const speed2 = Math.sqrt(fruit2.vx * fruit2.vx + fruit2.vy * fruit2.vy);
fruit1.vx = -fruit1.vx * 0.8 + (Math.random() - 0.5) * 2;
fruit1.vy = -fruit1.vy * 0.8 + (Math.random() - 0.5) * 2;
fruit2.vx = -fruit2.vx * 0.8 + (Math.random() - 0.5) * 2;
fruit2.vy = -fruit2.vy * 0.8 + (Math.random() - 0.5) * 2;
}
}
}
}
grabFruit(x, y) {
for (let i = this.fruits.length - 1; i >= 0; i--) {
const fruit = this.fruits[i];
const dx = x - fruit.x;
const dy = y - fruit.y;
const distance = Math.sqrt(dx * dx + dy * dy);
if (distance < fruit.radius) {
this.grabbedFruit = fruit;
fruit.isGrabbed = true;
this.grabOffset.x = dx;
this.grabOffset.y = dy;
fruit.element.style.transform = 'scale(1.2)';
fruit.element.style.zIndex = '1000';
this.fruitsGrabbed++;
this.updateStats();
break;
}
}
}
moveGrabbedFruit(x, y) {
if (this.grabbedFruit) {
this.grabbedFruit.x = x - this.grabOffset.x;
this.grabbedFruit.y = y - this.grabOffset.y;
this.grabbedFruit.element.style.left = this.grabbedFruit.x + 'px';
this.grabbedFruit.element.style.top = this.grabbedFruit.y + 'px';
}
}
releaseFruit(x, y) {
if (this.grabbedFruit) {
this.grabbedFruit.isGrabbed = false;
this.grabbedFruit.element.style.transform = 'scale(1)';
this.grabbedFruit.element.style.zIndex = '1';
// Add throw velocity based on mouse movement
const throwStrength = 0.5;
this.grabbedFruit.vx = (x - this.grabbedFruit.x) * throwStrength;
this.grabbedFruit.vy = (y - this.grabbedFruit.y) * throwStrength;
this.grabbedFruit = null;
}
}
mixFruits() {
this.isMixing = true;
this.container.style.transition = 'transform 0.5s ease';
this.container.style.transform = 'rotate(360deg)';
setTimeout(() => {
this.isMixing = false;
this.container.style.transition = 'transform 0.3s ease';
this.container.style.transform = 'rotate(0deg)';
}, 3000);
}
resetFruits() {
this.fruits.forEach(fruit => {
fruit.x = Math.random() * (this.bounds.right - 50);
fruit.y = Math.random() * 200;
fruit.vx = (Math.random() - 0.5) * 4;
fruit.vy = Math.random() * 2 + 1;
fruit.isGrabbed = false;
fruit.element.style.left = fruit.x + 'px';
fruit.element.style.top = fruit.y + 'px';
fruit.element.style.transform = 'scale(1)';
fruit.element.style.zIndex = '1';
});
this.grabbedFruit = null;
this.fruitsGrabbed = 0;
this.updateStats();
}
updateStats() {
document.getElementById('stats').textContent =
`Fruits: 50 | Grabbed: ${this.fruitsGrabbed}`;
}
setupEventListeners() {
this.container.addEventListener('mousedown', (e) => {
const rect = this.container.getBoundingClientRect();
this.grabFruit(e.clientX - rect.left, e.clientY - rect.top);
});
this.container.addEventListener('mousemove', (e) => {
if (this.grabbedFruit) {
const rect = this.container.getBoundingClientRect();
this.moveGrabbedFruit(e.clientX - rect.left, e.clientY - rect.top);
}
});
this.container.addEventListener('mouseup', (e) => {
if (this.grabbedFruit) {
const rect = this.container.getBoundingClientRect();
this.releaseFruit(e.clientX - rect.left, e.clientY - rect.top);
}
});
// Touch events for mobile
this.container.addEventListener('touchstart', (e) => {
e.preventDefault();
const rect = this.container.getBoundingClientRect();
const touch = e.touches[0];
this.grabFruit(touch.clientX - rect.left, touch.clientY - rect.top);
});
this.container.addEventListener('touchmove', (e) => {
e.preventDefault();
if (this.grabbedFruit) {
const rect = this.container.getBoundingClientRect();
const touch = e.touches[0];
this.moveGrabbedFruit(touch.clientX - rect.left, touch.clientY - rect.top);
}
});
this.container.addEventListener('touchend', (e) => {
if (this.grabbedFruit) {
const rect = this.container.getBoundingClientRect();
const touch = e.changedTouches[0];
this.releaseFruit(touch.clientX - rect.left, touch.clientY - rect.top);
}
});
document.getElementById('mixButton').addEventListener('click', () => {
this.mixFruits();
});
document.getElementById('resetButton').addEventListener('click', () => {
this.resetFruits();
});
window.addEventListener('resize', () => {
this.init();
});
}
animate() {
let lastTime = 0;
const update = (currentTime) => {
const deltaTime = Math.min((currentTime - lastTime) / 16, 2);
lastTime = currentTime;
this.fruits.forEach(fruit => {
this.updateFruit(fruit, deltaTime);
});
this.checkCollisions();
requestAnimationFrame(update);
};
requestAnimationFrame(update);
}
}
// Initialize the physics simulation when the page loads
window.addEventListener('load', () => {
new FruitPhysics();
});
</script>
<script>
class FruitPhysics {
constructor() {
this.container = document.getElementById('gameContainer');
this.fruits = [];
this.grabbedFruit = null;
this.grabOffset = { x: 0, y: 0 };
this.isMixing = false;
this.mixAngle = 0;
this.fruitsGrabbed = 0;
this.fruitEmojis = [
'🍎', '🍌', '🍇', '🍓', '🍊', '🍋', '🍉', '🍑', '🍍', '🥭',
'🍒', '🍐', '🥝', '🍅', '🥥', '🥑', '🍈', '🫐', '🍏', '🥭',
'🍎', '🍌', '🍇', '🍓', '🍊', '🍋', '🍉', '🍑', '🍍', '🥭',
'🍒', '🍐', '🥝', '🍅', '🥥', '🥑', '🍈', '🫐', '🍏', '🥭',
'🍎', '🍌', '🍇', '🍓', '🍊', '🍋', '🍉', '🍑', '🍍', '🥭'
];
this.init();
this.createFruits();
this.animate();
this.setupEventListeners();
}
init() {
const rect = this.container.getBoundingClientRect();
this.bounds = {
left: 10,
right: rect.width - 50,
top: 10,
bottom: rect.height - 50
};
}
createFruits() {
for (let i = 0; i < 50; i++) {
const fruit = document.createElement('div');
fruit.className = 'fruit';
fruit.textContent = this.fruitEmojis[i];
fruit.style.left = Math.random() * (this.bounds.right - 50) + 'px';
fruit.style.top = Math.random() * 200 + 'px';
this.container.appendChild(fruit);
this.fruits.push({
element: fruit,
x: parseFloat(fruit.style.left),
y: parseFloat(fruit.style.top),
vx: (Math.random() - 0.5) * 4,
vy: Math.random() * 2 + 1,
radius: 25,
mass: 1,
isGrabbed: false
});
}
}
updateFruit(fruit, deltaTime) {
if (fruit.isGrabbed || this.isMixing) return;
// Apply gravity
fruit.vy += 0.2 * deltaTime;
// Update position
fruit.x += fruit.vx * deltaTime;
fruit.y += fruit.vy * deltaTime;
// Boundary collision
if (fruit.x < this.bounds.left) {
fruit.x = this.bounds.left;
fruit.vx *= -0.8;
} else if (fruit.x > this.bounds.right) {
fruit.x = this.bounds.right;
fruit.vx *= -0.8;
}
if (fruit.y < this.bounds.top) {
fruit.y = this.bounds.top;
fruit.vy *= -0.8;
} else if (fruit.y > this.bounds.bottom) {
fruit.y = this.bounds.bottom;
fruit.vy *= -0.8;
fruit.vx *= 0.98; // Friction
}
// Apply mixing rotation
if (this.isMixing) {
const centerX = this.container.clientWidth / 2;
const centerY = this.container.clientHeight / 2;
const dx = fruit.x - centerX;
const dy = fruit.y - centerY;
const distance = Math.sqrt(dx * dx + dy * dy);
const angle = Math.atan2(dy, dx) + 0.05;
fruit.x = centerX + Math.cos(angle) * distance;
fruit.y = centerY + Math.sin(angle) * distance;
// Add centrifugal force
fruit.vx += dx * 0.001;
fruit.vy += dy * 0.001;
}
// Update element position
fruit.element.style.left = fruit.x + 'px';
fruit.element.style.top = fruit.y + 'px';
}
checkCollisions() {
for (let i = 0; i < this.fruits.length; i++) {
for (let j = i + 1; j < this.fruits.length; j++) {
const fruit1 = this.fruits[i];
const fruit2 = this.fruits[j];
if (fruit1.isGrabbed || fruit2.isGrabbed) continue;
const dx = fruit2.x - fruit1.x;
const dy = fruit2.y - fruit1.y;
const distance = Math.sqrt(dx * dx + dy * dy);
const minDistance = fruit1.radius + fruit2.radius;
if (distance < minDistance) {
// Collision response
const angle = Math.atan2(dy, dx);
const overlap = minDistance - distance;
// Separate fruits
const moveX = (overlap * 0.5) * Math.cos(angle);
const moveY = (overlap * 0.5) * Math.sin(angle);
fruit1.x -= moveX;
fruit1.y -= moveY;
fruit2.x += moveX;
fruit2.y += moveY;
// Bounce
const speed1 = Math.sqrt(fruit1.vx * fruit1.vx + fruit1.vy * fruit1.vy);
const speed2 = Math.sqrt(fruit2.vx * fruit2.vx + fruit2.vy * fruit2.vy);
fruit1.vx = -fruit1.vx * 0.8 + (Math.random() - 0.5) * 2;
fruit1.vy = -fruit1.vy * 0.8 + (Math.random() - 0.5) * 2;
fruit2.vx = -fruit2.vx * 0.8 + (Math.random() - 0.5) * 2;
fruit2.vy = -fruit2.vy * 0.8 + (Math.random() - 0.5) * 2;
}
}
}
}
grabFruit(x, y) {
for (let i = this.fruits.length - 1; i >= 0; i--) {
const fruit = this.fruits[i];
const dx = x - fruit.x;
const dy = y - fruit.y;
const distance = Math.sqrt(dx * dx + dy * dy);
if (distance < fruit.radius) {
this.grabbedFruit = fruit;
fruit.isGrabbed = true;
this.grabOffset.x = dx;
this.grabOffset.y = dy;
fruit.element.style.transform = 'scale(1.2)';
fruit.element.style.zIndex = '1000';
this.fruitsGrabbed++;
this.updateStats();
break;
}
}
}
moveGrabbedFruit(x, y) {
if (this.grabbedFruit) {
this.grabbedFruit.x = x - this.grabOffset.x;
this.grabbedFruit.y = y - this.grabOffset.y;
this.grabbedFruit.element.style.left = this.grabbedFruit.x + 'px';
this.grabbedFruit.element.style.top = this.grabbedFruit.y + 'px';
}
}
releaseFruit(x, y) {
if (this.grabbedFruit) {
this.grabbedFruit.isGrabbed = false;
this.grabbedFruit.element.style.transform = 'scale(1)';
this.grabbedFruit.element.style.zIndex = '1';
// Add throw velocity based on mouse movement
const throwStrength = 0.5;
this.grabbedFruit.vx = (x - this.grabbedFruit.x) * throwStrength;
this.grabbedFruit.vy = (y - this.grabbedFruit.y) * throwStrength;
this.grabbedFruit = null;
}
}
mixFruits() {
this.isMixing = true;
this.container.style.transition = 'transform 0.5s ease';
this.container.style.transform = 'rotate(360deg)';
setTimeout(() => {
this.isMixing = false;
this.container.style.transition = 'transform 0.3s ease';
this.container.style.transform = 'rotate(0deg)';
}, 3000);
}
resetFruits() {
this.fruits.forEach(fruit => {
fruit.x = Math.random() * (this.bounds.right - 50);
fruit.y = Math.random() * 200;
fruit.vx = (Math.random() - 0.5) * 4;
fruit.vy = Math.random() * 2 + 1;
fruit.isGrabbed = false;
fruit.element.style.left = fruit.x + 'px';
fruit.element.style.top = fruit.y + 'px';
fruit.element.style.transform = 'scale(1)';
fruit.element.style.zIndex = '1';
});
this.grabbedFruit = null;
this.fruitsGrabbed = 0;
this.updateStats();
}
updateStats() {
document.getElementById('stats').textContent =
`Fruits: 50 | Grabbed: ${this.fruitsGrabbed}`;
}
setupEventListeners() {
this.container.addEventListener('mousedown', (e) => {
const rect = this.container.getBoundingClientRect();
this.grabFruit(e.clientX - rect.left, e.clientY - rect.top);
});
this.container.addEventListener('mousemove', (e) => {
if (this.grabbedFruit) {
const rect = this.container.getBoundingClientRect();
this.moveGrabbedFruit(e.clientX - rect.left, e.clientY - rect.top);
}
});
this.container.addEventListener('mouseup', (e) => {
if (this.grabbedFruit) {
const rect = this.container.getBoundingClientRect();
this.releaseFruit(e.clientX - rect.left, e.clientY - rect.top);
}
});
// Touch events for mobile
this.container.addEventListener('touchstart', (e) => {
e.preventDefault();
const rect = this.container.getBoundingClientRect();
const touch = e.touches[0];
this.grabFruit(touch.clientX - rect.left, touch.clientY - rect.top);
});
this.container.addEventListener('touchmove', (e) => {
e.preventDefault();
if (this.grabbedFruit) {
const rect = this.container.getBoundingClientRect();
const touch = e.touches[0];
this.moveGrabbedFruit(touch.clientX - rect.left, touch.clientY - rect.top);
}
});
this.container.addEventListener('touchend', (e) => {
if (this.grabbedFruit) {
const rect = this.container.getBoundingClientRect();
const touch = e.changedTouches[0];
this.releaseFruit(touch.clientX - rect.left, touch.clientY - rect.top);
}
});
document.getElementById('mixButton').addEventListener('click', () => {
this.mixFruits();
});
document.getElementById('resetButton').addEventListener('click', () => {
this.resetFruits();
});
window.addEventListener('resize', () => {
this.init();
});
}
animate() {
let lastTime = 0;
const update = (currentTime) => {
const deltaTime = Math.min((currentTime - lastTime) / 16, 2);
lastTime = currentTime;
this.fruits.forEach(fruit => {
this.updateFruit(fruit, deltaTime);
});
this.checkCollisions();
requestAnimationFrame(update);
};
requestAnimationFrame(update);
}
}
// Initialize the physics simulation when the page loads
window.addEventListener('load', () => {
new FruitPhysics();
});
</script>