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();