other.x && this.y < other.y + other.height && this.y + this.height > other.y; } handleCollision(other) { // Calculate centers const thisCenterX = this.x + this.width / 2; const thisCenterY = this.y + this.height / 2; const otherCenterX = other.x + other.width / 2; const otherCenterY = other.y + other.height / 2; // Calculate collision normal const dx = thisCenterX - otherCenterX; const dy = thisCenterY - otherCenterY; const distance = Math.sqrt(dx * dx + dy * dy); if (distance === 0) return; const normalX = dx / distance; const normalY = dy / distance; // Separate rectangles to prevent overlap const overlapX = (this.width + other.width) / 2 - Math.abs(dx); const overlapY = (this.height + other.height) / 2 - Math.abs(dy); if (overlapX < overlapY) { this.x += normalX * overlapX / 2; other.x -= normalX * overlapX / 2; } else { this.y += normalY * overlapY / 2; other.y -= normalY * overlapY / 2; } // Swap velocities for elastic collision const relativeVelocityX = this.velocityX - other.velocityX; const relativeVelocityY = this.velocityY - other.velocityY; const dotProduct = relativeVelocityX * normalX + relativeVelocityY * normalY; if (dotProduct < 0) { const impulse = 2 * dotProduct; this.velocityX -= impulse * normalX; this.velocityY -= impulse * normalY; other.velocityX += impulse * normalX; other.velocityY += impulse * normalY; } } draw() { // Pride flag colors (6 horizontal stripes) const prideColors = [ '#FF0018', // Red '#FFA52C', // Orange '#FFFF41', // Yellow '#008018', // Green '#0000F9', // Blue '#86007D' // Purple/Violet ]; const stripeHeight = this.height / prideColors.length; // Draw fading trail this.trail.forEach((point, index) => { const alpha = index / this.trail.length; const scale = 0.5 + (alpha * 0.5); // Trail gets smaller as it fades const trailWidth = point.width * scale; const trailHeight = point.height * scale; const trailX = point.x + (point.width * (1 - scale)) / 2; const trailY = point.y + (point.height * (1 - scale)) / 2; const trailStripeHeight = trailHeight / prideColors.length; // Draw rainbow stripes for trail prideColors.forEach((color, i) => { // Convert hex to rgba with alpha const r = parseInt(color.slice(1, 3), 16); const g = parseInt(color.slice(3, 5), 16); const b = parseInt(color.slice(5, 7), 16); ctx.fillStyle = `rgba(${r}, ${g}, ${b}, ${alpha * 0.6})`; ctx.fillRect( trailX, trailY + (i * trailStripeHeight), trailWidth, trailStripeHeight ); }); }); // Draw main rainbow rectangle with pride flag stripes prideColors.forEach((color, i) => { ctx.fillStyle = color; ctx.fillRect( this.x, this.y + (i * stripeHeight), this.width, stripeHeight ); }); } } // White rectangle with text class WhiteRectangle { constructor() { this.width = 240; this.height = 120; this.x = canvas.width / 3; this.y = canvas.height / 3; // DVD logo style: constant speed, diagonal movement const speed = 3; const angle = Math.random() * Math.PI * 2; this.velocityX = Math.cos(angle) * speed; this.velocityY = Math.sin(angle) * speed; this.text = "michal jest gejem"; } limitSpeed() { const speed = Math.sqrt(this.velocityX * this.velocityX + this.velocityY * this.velocityY); if (speed > MAX_SPEED) { const scale = MAX_SPEED / speed; this.velocityX *= scale; this.velocityY *= scale; } } update(otherRect) { // Limit speed before updating this.limitSpeed(); // Update position this.x += this.velocityX; this.y += this.velocityY; // Check collision with other rectangle if (otherRect && this.checkCollision(otherRect)) { this.handleCollision(otherRect); // Limit speed after collision this.limitSpeed(); } // Bounce off walls (DVD logo style) if (this.x <= 0 || this.x + this.width >= canvas.width) { this.velocityX = -this.velocityX; this.x = Math.max(0, Math.min(canvas.width - this.width, this.x)); this.limitSpeed(); } if (this.y <= 0 || this.y + this.height >= canvas.height) { this.velocityY = -this.velocityY; this.y = Math.max(0, Math.min(canvas.height - this.height, this.y)); this.limitSpeed(); } } checkCollision(other) { return this.x < other.x + other.width && this.x + this.width > other.x && this.y < other.y + other.height && this.y + this.height > other.y; } handleCollision(other) { // Calculate centers const thisCenterX = this.x + this.width / 2; const thisCenterY = this.y + this.height / 2; const otherCenterX = other.x + other.width / 2; const otherCenterY = other.y + other.height / 2; // Calculate collision normal const dx = thisCenterX - otherCenterX; const dy = thisCenterY - otherCenterY; const distance = Math.sqrt(dx * dx + dy * dy); if (distance === 0) return; const normalX = dx / distance; const normalY = dy / distance; // Separate rectangles to prevent overlap const overlapX = (this.width + other.width) / 2 - Math.abs(dx); const overlapY = (this.height + other.height) / 2 - Math.abs(dy); if (overlapX < overlapY) { this.x += normalX * overlapX / 2; other.x -= normalX * overlapX / 2; } else { this.y += normalY * overlapY / 2; other.y -= normalY * overlapY / 2; } // Swap velocities for elastic collision const relativeVelocityX = this.velocityX - other.velocityX; const relativeVelocityY = this.velocityY - other.velocityY; const dotProduct = relativeVelocityX * normalX + relativeVelocityY * normalY; if (dotProduct < 0) { const impulse = 2 * dotProduct; this.velocityX -= impulse * normalX; this.velocityY -= impulse * normalY; other.velocityX += impulse * normalX; other.velocityY += impulse * normalY; } } draw() { // Draw white rectangle ctx.fillStyle = '#FFFFFF'; ctx.fillRect(this.x, this.y, this.width, this.height); // Draw black border ctx.strokeStyle = '#000000'; ctx.lineWidth = 2; ctx.strokeRect(this.x, this.y, this.width, this.height); // Draw black text (larger font for bigger box) ctx.fillStyle = '#000000'; ctx.font = 'bold 24px Arial'; ctx.textAlign = 'center'; ctx.textBaseline = 'middle'; ctx.fillText(this.text, this.x + this.width / 2, this.y + this.height / 2); } } // Create rainbow and white rectangle const rainbow = new Rainbow(); const whiteRect = new WhiteRectangle(); // Animation loop function animate() { // Clear canvas with slight fade for trail effect ctx.fillStyle = 'rgba(0, 0, 0, 0.1)'; ctx.fillRect(0, 0, canvas.width, canvas.height); // Update and draw both rectangles (pass each other for collision detection) rainbow.update(whiteRect); rainbow.draw(); whiteRect.update(rainbow); whiteRect.draw(); requestAnimationFrame(animate); } // Start animation animate();