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:
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();
|
||||
Reference in New Issue
Block a user