From 792fbfa5bb488a7f5981ca058949c2774df290af Mon Sep 17 00:00:00 2001 From: Ronnie Date: Thu, 29 May 2025 21:30:12 -0400 Subject: [PATCH] first commit --- CNAME | 1 + README.md | 25 +++++++++ index.html | 24 ++++++++ script.js | 157 +++++++++++++++++++++++++++++++++++++++++++++++++++++ styles.css | 122 +++++++++++++++++++++++++++++++++++++++++ 5 files changed, 329 insertions(+) create mode 100644 CNAME create mode 100644 README.md create mode 100644 index.html create mode 100644 script.js create mode 100644 styles.css diff --git a/CNAME b/CNAME new file mode 100644 index 0000000..ce27d69 --- /dev/null +++ b/CNAME @@ -0,0 +1 @@ +catchthedot.ronniie.dev \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..d28dea4 --- /dev/null +++ b/README.md @@ -0,0 +1,25 @@ +# Catch the Dot + +A fun web-based game where you try to catch elusive dots that multiply and move around the screen. The dots are designed to be challenging to catch, with smooth movement patterns and smart mouse avoidance. + +## Features + +- Smooth dot movement with physics-based motion +- Dots multiply over time +- Smart mouse avoidance behavior +- Modern UI with glass-morphism effects +- Fullscreen gameplay +- Pacman-style screen wrapping + +## How to Play + +1. Open `index.html` in a web browser +2. Move your mouse to interact with the dots +3. Watch as the dots multiply and try to catch them +4. The counter shows how many dots are currently on screen + +## Technologies Used + +- HTML5 +- CSS3 +- JavaScript (Vanilla) \ No newline at end of file diff --git a/index.html b/index.html new file mode 100644 index 0000000..6af2c4a --- /dev/null +++ b/index.html @@ -0,0 +1,24 @@ + + + + + + Catch the Dot + + + +
+
+

Catch the Dot

+
+ Dots: + 1 +
+
+ +
+ + + \ No newline at end of file diff --git a/script.js b/script.js new file mode 100644 index 0000000..fd08c44 --- /dev/null +++ b/script.js @@ -0,0 +1,157 @@ +const gameArea = document.querySelector('.game-area'); +const dotCountDisplay = document.getElementById('dotCount'); +const message = document.querySelector('.message'); + +let dots = []; +let lastMouseX = 0; +let lastMouseY = 0; +let lastMouseTime = 0; +let mouseVelocityX = 0; +let mouseVelocityY = 0; + +class Dot { + constructor(x, y) { + this.element = document.createElement('div'); + this.element.className = 'dot'; + this.x = x; + this.y = y; + this.velocityX = (Math.random() - 0.5) * 200; + this.velocityY = (Math.random() - 0.5) * 200; + this.lastDuplication = Date.now(); + this.acceleration = 0; + gameArea.appendChild(this.element); + this.updatePosition(); + } + + updatePosition() { + this.element.style.left = `${this.x}px`; + this.element.style.top = `${this.y}px`; + } + + move() { + this.velocityX += (Math.random() - 0.5) * 20; + this.velocityY += (Math.random() - 0.5) * 20; + + this.x += this.velocityX * 0.016; + this.y += this.velocityY * 0.016; + + if (this.x < 0) { + this.x = window.innerWidth; + } else if (this.x > window.innerWidth) { + this.x = 0; + } + + if (this.y < 0) { + this.y = window.innerHeight; + } else if (this.y > window.innerHeight) { + this.y = 0; + } + + this.velocityX *= 0.98; + this.velocityY *= 0.98; + + const currentSpeed = Math.sqrt(this.velocityX * this.velocityX + this.velocityY * this.velocityY); + if (currentSpeed < 100) { + const angle = Math.atan2(this.velocityY, this.velocityX); + this.velocityX = Math.cos(angle) * 100; + this.velocityY = Math.sin(angle) * 100; + } + + if (currentSpeed > 400) { + const angle = Math.atan2(this.velocityY, this.velocityX); + this.velocityX = Math.cos(angle) * 400; + this.velocityY = Math.sin(angle) * 400; + } + + this.updatePosition(); + + const now = Date.now(); + if (now - this.lastDuplication > 3000 && Math.random() < 0.002) { + this.duplicate(); + this.lastDuplication = now; + } + } + + duplicate() { + if (dots.length < 8) { + const newDot = new Dot(this.x, this.y); + newDot.velocityX = this.velocityX * 1.2; + newDot.velocityY = this.velocityY * 1.2; + dots.push(newDot); + updateDotCount(); + } + } + + avoidMouse(mouseX, mouseY, mouseVelX, mouseVelY) { + const dx = this.x - mouseX; + const dy = this.y - mouseY; + const distance = Math.sqrt(dx * dx + dy * dy); + + if (distance < 300) { + const speed = 300; + const mouseSpeed = Math.sqrt(mouseVelX * mouseVelX + mouseVelY * mouseVelY); + + const dotToMouseAngle = Math.atan2(dy, dx); + const mouseMovementAngle = Math.atan2(mouseVelY, mouseVelX); + const angleDiff = Math.abs(dotToMouseAngle - mouseMovementAngle); + + const distanceFactor = Math.min(1, (300 - distance) / 300); + + if (angleDiff < Math.PI / 2) { + const targetSpeed = speed + mouseSpeed * 800 * distanceFactor; + const currentSpeed = Math.sqrt(this.velocityX * this.velocityX + this.velocityY * this.velocityY); + const acceleration = Math.min(0.2, (targetSpeed - currentSpeed) / 1000); + + this.velocityX = this.velocityX * (1 - acceleration) + Math.cos(dotToMouseAngle) * targetSpeed * acceleration; + this.velocityY = this.velocityY * (1 - acceleration) + Math.sin(dotToMouseAngle) * targetSpeed * acceleration; + } + } + } +} + +function updateDotCount() { + dotCountDisplay.textContent = dots.length; +} + +function initializeGame() { + const firstDot = new Dot( + Math.random() * window.innerWidth, + Math.random() * window.innerHeight + ); + dots.push(firstDot); + updateDotCount(); +} + +function moveDot(event) { + const currentTime = Date.now(); + const timeDiff = currentTime - lastMouseTime; + + if (lastMouseTime !== 0) { + mouseVelocityX = (event.clientX - lastMouseX) / timeDiff; + mouseVelocityY = (event.clientY - lastMouseY) / timeDiff; + } + + lastMouseX = event.clientX; + lastMouseY = event.clientY; + lastMouseTime = currentTime; + + dots.forEach(dot => dot.avoidMouse(event.clientX, event.clientY, mouseVelocityX, mouseVelocityY)); +} + +function updateDots() { + dots.forEach(dot => dot.move()); + requestAnimationFrame(updateDots); +} + +gameArea.addEventListener('mousemove', moveDot); + +initializeGame(); +updateDots(); + +window.addEventListener('resize', () => { + dots.forEach(dot => { + dot.x = Math.min(dot.x, window.innerWidth); + dot.y = Math.min(dot.y, window.innerHeight); + dot.updatePosition(); + }); +}); \ No newline at end of file diff --git a/styles.css b/styles.css new file mode 100644 index 0000000..4193da7 --- /dev/null +++ b/styles.css @@ -0,0 +1,122 @@ +* { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +body { + font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; + background: #0f0f0f; + min-height: 100vh; + overflow: hidden; + color: #ffffff; +} + +.game-container { + background: rgba(255, 255, 255, 0.1); + backdrop-filter: blur(10px); + border-radius: 20px; + padding: 2rem; + box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3); + width: 90%; + max-width: 800px; +} + +.game-header { + position: fixed; + top: 2rem; + left: 50%; + transform: translateX(-50%); + text-align: center; + z-index: 100; + background: rgba(255, 255, 255, 0.05); + padding: 1.5rem 3rem; + border-radius: 20px; + backdrop-filter: blur(10px); + border: 1px solid rgba(255, 255, 255, 0.1); + box-shadow: 0 8px 32px rgba(0, 0, 0, 0.2); +} + +h1 { + font-size: 3rem; + font-weight: 800; + margin-bottom: 0.5rem; + background: linear-gradient(45deg, #ff3366, #ff6b6b); + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; + letter-spacing: -1px; +} + +.score-container { + font-size: 1.4rem; + color: #ff3366; + font-weight: 600; + text-shadow: 0 0 20px rgba(255, 51, 102, 0.3); +} + +.game-area { + position: fixed; + top: 0; + left: 0; + width: 100vw; + height: 100vh; + background: radial-gradient(circle at center, #1a1a1a 0%, #0f0f0f 100%); + cursor: pointer; +} + +.dot { + position: absolute; + width: 24px; + height: 24px; + background: #ff3366; + border-radius: 50%; + transform: translate(-50%, -50%); + transition: transform 0.1s ease-out; + box-shadow: 0 0 30px rgba(255, 51, 102, 0.5); + z-index: 1000; +} + +.dot::before { + content: ''; + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + width: 100%; + height: 100%; + background: rgba(255, 51, 102, 0.3); + border-radius: 50%; + animation: ripple 2s ease-out infinite; +} + +.game-footer { + position: fixed; + bottom: 2rem; + left: 50%; + transform: translateX(-50%); + text-align: center; + font-size: 1.2rem; + color: #ffffff; + background: rgba(255, 255, 255, 0.05); + padding: 1rem 2rem; + border-radius: 15px; + backdrop-filter: blur(10px); + border: 1px solid rgba(255, 255, 255, 0.1); + box-shadow: 0 8px 32px rgba(0, 0, 0, 0.2); +} + +.message { + font-weight: 500; + color: #ff3366; + text-shadow: 0 0 20px rgba(255, 51, 102, 0.3); +} + +@keyframes ripple { + 0% { transform: translate(-50%, -50%) scale(1); opacity: 0.5; } + 100% { transform: translate(-50%, -50%) scale(2); opacity: 0; } +} + +@keyframes float { + 0%, 100% { transform: translateX(-50%) translateY(0); } + 50% { transform: translateX(-50%) translateY(-10px); } +} \ No newline at end of file