diff --git a/index.html b/index.html index 9eb540e..da17d11 100644 --- a/index.html +++ b/index.html @@ -2,9 +2,20 @@ - Hola mundo + + Hola Mundo + -

Hola mundo 11

+ +
+ +
+

Hola Mundo

+

+

+
+ + - \ No newline at end of file + diff --git a/main.js b/main.js new file mode 100644 index 0000000..05d055e --- /dev/null +++ b/main.js @@ -0,0 +1,112 @@ +// Particle field +const canvas = document.getElementById('particles'); +const ctx = canvas.getContext('2d'); + +let W, H, particles; + +function resize() { + W = canvas.width = window.innerWidth; + H = canvas.height = window.innerHeight; +} + +function createParticles() { + const count = Math.floor((W * H) / 8000); + particles = Array.from({ length: count }, () => ({ + x: Math.random() * W, + y: Math.random() * H, + vx: (Math.random() - 0.5) * 0.4, + vy: (Math.random() - 0.5) * 0.4, + r: Math.random() * 1.5 + 0.5, + hue: Math.random() > 0.5 ? 180 : 300, + })); +} + +const CONNECT_DIST = 120; + +function draw() { + ctx.clearRect(0, 0, W, H); + + for (let i = 0; i < particles.length; i++) { + const p = particles[i]; + p.x += p.vx; + p.y += p.vy; + if (p.x < 0 || p.x > W) p.vx *= -1; + if (p.y < 0 || p.y > H) p.vy *= -1; + + ctx.beginPath(); + ctx.arc(p.x, p.y, p.r, 0, Math.PI * 2); + ctx.fillStyle = `hsla(${p.hue}, 100%, 70%, 0.8)`; + ctx.fill(); + + for (let j = i + 1; j < particles.length; j++) { + const q = particles[j]; + const dx = p.x - q.x; + const dy = p.y - q.y; + const dist = Math.sqrt(dx * dx + dy * dy); + if (dist < CONNECT_DIST) { + ctx.beginPath(); + ctx.moveTo(p.x, p.y); + ctx.lineTo(q.x, q.y); + ctx.strokeStyle = `hsla(${p.hue}, 100%, 70%, ${1 - dist / CONNECT_DIST})`; + ctx.lineWidth = 0.4; + ctx.stroke(); + } + } + } + + requestAnimationFrame(draw); +} + +window.addEventListener('resize', () => { resize(); createParticles(); }); +resize(); +createParticles(); +draw(); + + +// Typewriter subtitle +const lines = [ + 'bienvenido al universo digital', + 'cada bit cuenta una historia', + 'el código es poesía', +]; +let lineIdx = 0, charIdx = 0, deleting = false; +const typed = document.getElementById('typed'); + +function typeLoop() { + const current = lines[lineIdx]; + if (!deleting) { + charIdx++; + typed.textContent = current.slice(0, charIdx); + if (charIdx === current.length) { + deleting = true; + setTimeout(typeLoop, 2000); + return; + } + } else { + charIdx--; + typed.textContent = current.slice(0, charIdx); + if (charIdx === 0) { + deleting = false; + lineIdx = (lineIdx + 1) % lines.length; + } + } + setTimeout(typeLoop, deleting ? 40 : 80); +} + +typeLoop(); + + +// Live elapsed counter +const start = Date.now(); +const counterEl = document.getElementById('counter'); + +function updateCounter() { + const s = Math.floor((Date.now() - start) / 1000); + const h = String(Math.floor(s / 3600)).padStart(2, '0'); + const m = String(Math.floor((s % 3600) / 60)).padStart(2, '0'); + const sec = String(s % 60).padStart(2, '0'); + counterEl.textContent = `UPTIME :: ${h}:${m}:${sec}`; + requestAnimationFrame(updateCounter); +} + +updateCounter(); diff --git a/style.css b/style.css new file mode 100644 index 0000000..e711520 --- /dev/null +++ b/style.css @@ -0,0 +1,125 @@ +* { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +body { + background: #0a0a0f; + display: flex; + justify-content: center; + align-items: center; + min-height: 100vh; + font-family: 'Courier New', monospace; + overflow: hidden; +} + +canvas { + position: fixed; + top: 0; + left: 0; + z-index: 0; +} + +.container { + position: relative; + z-index: 1; + text-align: center; +} + +.glitch { + font-size: clamp(3rem, 10vw, 8rem); + font-weight: 900; + color: #fff; + text-transform: uppercase; + letter-spacing: 0.1em; + position: relative; + animation: glitch-main 3s infinite; +} + +.glitch::before, +.glitch::after { + content: attr(data-text); + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; +} + +.glitch::before { + color: #0ff; + animation: glitch-before 3s infinite; + clip-path: polygon(0 30%, 100% 30%, 100% 50%, 0 50%); +} + +.glitch::after { + color: #f0f; + animation: glitch-after 3s infinite; + clip-path: polygon(0 60%, 100% 60%, 100% 80%, 0 80%); +} + +@keyframes glitch-main { + 0%, 90%, 100% { transform: translate(0); } + 92% { transform: translate(-2px, 1px); } + 94% { transform: translate(2px, -1px); } + 96% { transform: translate(-1px, 2px); } +} + +@keyframes glitch-before { + 0%, 90%, 100% { transform: translate(0); opacity: 0; } + 91% { transform: translate(-4px, 0); opacity: 0.8; } + 93% { transform: translate(4px, 0); opacity: 0.8; } + 95% { opacity: 0; } +} + +@keyframes glitch-after { + 0%, 90%, 100% { transform: translate(0); opacity: 0; } + 92% { transform: translate(4px, 0); opacity: 0.8; } + 94% { transform: translate(-4px, 0); opacity: 0.8; } + 96% { opacity: 0; } +} + +.subtitle { + margin-top: 1rem; + font-size: clamp(0.8rem, 2vw, 1.2rem); + color: #0ff; + letter-spacing: 0.5em; + text-transform: uppercase; + opacity: 0; + animation: fade-in 1s ease forwards 0.5s; +} + +.counter { + margin-top: 2rem; + font-size: 0.85rem; + color: #444; + letter-spacing: 0.3em; + animation: fade-in 1s ease forwards 1s; + opacity: 0; +} + +#typed { + color: #0f0; +} + +@keyframes fade-in { + to { opacity: 1; } +} + +.scanline { + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + background: repeating-linear-gradient( + to bottom, + transparent 0px, + transparent 3px, + rgba(0, 0, 0, 0.08) 3px, + rgba(0, 0, 0, 0.08) 4px + ); + pointer-events: none; + z-index: 2; +}