From 551e56fa9dd341ac3c72a768d63c745fadb6267a Mon Sep 17 00:00:00 2001 From: treywisp Date: Thu, 2 Oct 2025 16:55:07 +0200 Subject: [PATCH] =?UTF-8?q?=D0=97=D0=B0=D0=B3=D1=80=D1=83=D0=B7=D0=B8?= =?UTF-8?q?=D1=82=D1=8C=20=D1=84=D0=B0=D0=B9=D0=BB=D1=8B=20=D0=B2=20=C2=AB?= =?UTF-8?q?frogs=C2=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frogs/frogs.js | 153 +++++++++++++++++++++++++++++++++++++++++++++++ frogs/index.html | 30 ++++++++++ 2 files changed, 183 insertions(+) create mode 100644 frogs/frogs.js create mode 100644 frogs/index.html diff --git a/frogs/frogs.js b/frogs/frogs.js new file mode 100644 index 0000000..810a238 --- /dev/null +++ b/frogs/frogs.js @@ -0,0 +1,153 @@ +const audio = document.getElementById('bg-music'); + +const container = document.getElementById('frog-container'); +const menuHeight = document.querySelector('header').offsetHeight; +const startText = document.getElementById('start-text'); +const startString = "Нажми в любую часть экрана"; + +const particlesContainer = document.querySelector('.particles'); +const particleCount = 60; +for (let i = 0; i < particleCount; i++) { + const particle = document.createElement('div'); + particle.classList.add('particle'); + const size = Math.random() * 8 + 2; + const posX = Math.random() * 100; + const posY = Math.random() * 100; + const duration = Math.random() * 5 + 5; + const baseColor = 200; + const variation = Math.random() * 20 - 10; + particle.style.background = `hsl(${baseColor + variation}, 100%, 50%)`; + particle.style.width = size + 'px'; + particle.style.height = size + 'px'; + particle.style.left = posX + '%'; + particle.style.top = posY + '%'; + particle.style.animationDuration = `${duration}s`; + particlesContainer.appendChild(particle); +} + +const totalFrogs = 33; +const frogs = []; +const occupiedZones = []; +let typingTimers = []; + +function checkOverlap(pos, zones) { + return zones.some(zone => + pos.x < zone.x + zone.w && + pos.x + pos.w > zone.x && + pos.y < zone.y + zone.h && + pos.y + pos.h > zone.y + ); +} + +function getRandomPosition(frog) { + const w = frog.el.offsetWidth; + const h = w; + const x = Math.random() * (window.innerWidth - w); + const y = Math.random() * (window.innerHeight - h - menuHeight) + menuHeight; + return { x, y, w, h }; +} + +function placeFrog(frog) { + let pos, attempts = 0; + do { + pos = getRandomPosition(frog); + attempts++; + if (attempts > 100) break; + } while (checkOverlap(pos, occupiedZones)); + occupiedZones.push(pos); + frog.el.style.left = pos.x + 'px'; + frog.el.style.top = pos.y + 'px'; +} + +function teleportFrogsSync() { + const zones = []; + frogs.forEach(frog => { + let pos, attempts = 0; + do { + pos = getRandomPosition(frog); + attempts++; + if (attempts > 100) break; + } while (checkOverlap(pos, zones)); + zones.push(pos); + frog.el.style.left = pos.x + 'px'; + frog.el.style.top = pos.y + 'px'; + }); +} + +function startTeleportationLoop() { + setInterval(teleportFrogsSync, 1000); +} + +function createFrog() { + const frog = document.createElement('img'); + frog.src = '../assets/frog.gif'; + frog.classList.add('frog'); + if (Math.random() < 0.5) frog.style.transform = 'scaleX(-1)'; + frog.style.opacity = 0; + container.appendChild(frog); + + frog.onload = () => { + const sizePx = window.innerWidth * (0.05 + Math.random() * 0.05); + frog.style.width = sizePx + 'px'; + frog.style.height = 'auto'; + requestAnimationFrame(() => { + placeFrog({ el: frog }); + const delay = Math.random() * 1300 + 200; + setTimeout(() => { frog.style.transition = `opacity ${delay}ms linear`; frog.style.opacity = 1; }, Math.random() * 1500); + }); + }; + return { el: frog }; +} + +function typeWriterEffect(text, container, delay = 50) { + typingTimers.forEach(t => clearTimeout(t)); + typingTimers = []; + container.innerHTML = ''; + [...text].forEach(char => { + const span = document.createElement('span'); + span.textContent = char === ' ' ? '\u00A0' : char; + container.appendChild(span); + }); + const spans = container.querySelectorAll('span'); + spans.forEach((span, i) => { + const timer = setTimeout(() => { span.style.opacity = 1; }, i * delay); + typingTimers.push(timer); + }); +} + +function reverseTypeWriter(container, delay = 50, callback) { + typingTimers.forEach(t => clearTimeout(t)); + typingTimers = []; + const spans = Array.from(container.querySelectorAll('span')).reverse(); + spans.forEach((span, i) => { setTimeout(() => { span.style.opacity = 0; }, i * delay); }); + setTimeout(callback, spans.length * delay + 50); +} + +let started = false; +let typingInProgress = false; + +document.addEventListener('click', () => { + if (started || typingInProgress) return; + + typingInProgress = true; + + reverseTypeWriter(startText, 40, () => { + typingInProgress = false; + started = true; + + audio.play().catch(() => {}); + for (let i = 0; i < totalFrogs; i++) { + frogs.push(createFrog()); + } + startTeleportationLoop(); + }); +}); + +typeWriterEffect(startString, startText, 40); + +window.addEventListener('resize', () => { + frogs.forEach(frog => { + const size = window.innerWidth * (0.05 + Math.random() * 0.05); + frog.el.style.width = size + 'px'; + }); +}); \ No newline at end of file diff --git a/frogs/index.html b/frogs/index.html new file mode 100644 index 0000000..e718662 --- /dev/null +++ b/frogs/index.html @@ -0,0 +1,30 @@ + + + + + + treywisp — frogs + + + + + +
+ +
+ +
+
+
+
+ + + + + \ No newline at end of file