Add creative Hola Mundo with particles, glitch effect and typewriter
- index.html: structure linking external CSS and JS - style.css: futuristic dark theme with glitch animation and scanlines - main.js: canvas particle field, typewriter loop, live uptime counter Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
17
index.html
17
index.html
@@ -2,9 +2,20 @@
|
|||||||
<html lang="es">
|
<html lang="es">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<title>Hola mundo</title>
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Hola Mundo</title>
|
||||||
|
<link rel="stylesheet" href="style.css">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<h1>Hola mundo 11</h1>
|
<canvas id="particles"></canvas>
|
||||||
|
<div class="scanline"></div>
|
||||||
|
|
||||||
|
<div class="container">
|
||||||
|
<h1 class="glitch" data-text="Hola Mundo">Hola Mundo</h1>
|
||||||
|
<p class="subtitle"><span id="typed"></span></p>
|
||||||
|
<p class="counter" id="counter"></p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script src="main.js"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
112
main.js
Normal file
112
main.js
Normal file
@@ -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();
|
||||||
125
style.css
Normal file
125
style.css
Normal file
@@ -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;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user