Skip to content

Instantly share code, notes, and snippets.

@pzarzycki
Last active June 26, 2025 06:59
Show Gist options
  • Save pzarzycki/dab0559196d46f6e0a303020a0ded9b2 to your computer and use it in GitHub Desktop.
Save pzarzycki/dab0559196d46f6e0a303020a0ded9b2 to your computer and use it in GitHub Desktop.
Nice HTML animation
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Network Simulator Intro</title>
<style>
html, body {
margin: 0;
padding: 0;
height: 100%;
overflow: hidden;
background: #001326;
opacity: 0;
animation: fadeInBody 2s ease forwards;
font-family: 'Rajdhani', sans-serif;
color: #ffffff;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
@keyframes fadeInBody { to { opacity: 1; } }
@keyframes gradientShift {
0% { background-position: 0% 50%; }
50% { background-position: 100% 50%; }
100% { background-position: 0% 50%; }
}
.logo {
font-size: clamp(2rem, 6vw, 6rem);
text-align: center;
line-height: 1.1;
}
.logo span {
opacity: 0;
display: inline-block;
transform: scale(0.95) translateY(-8px);
animation: bounceIn 0.4s ease-out forwards;
}
.logo span.heavy { font-weight: 800; }
@keyframes bounceIn {
0% { opacity: 0; transform: scale(0.95) translateY(-8px); }
50% { opacity: 1; transform: scale(1.02) translateY(2px); }
100% { opacity: 1; transform: scale(1) translateY(0); }
}
.subtitle {
font-size: 1.4rem;
margin-top: 1.3rem;
opacity: 0;
transition: opacity 1s ease;
background: linear-gradient(90deg, #555, #ccc, #555);
background-size: 200% auto;
background-clip: text;
-webkit-background-clip: text;
color: transparent;
-webkit-text-fill-color: transparent;
animation: shineText 2.5s linear infinite;
}
.subtitle.show { opacity: 1; }
.dots {
display: inline-flex;
width: 3ch;
justify-content: space-between;
}
.dot {
width: .5ch;
text-align: center;
animation: dotPulse 1.2s infinite ease-in-out;
opacity: 0;
}
.dot:nth-child(1) { animation-delay: 0s; }
.dot:nth-child(2) { animation-delay: 0.15s; }
.dot:nth-child(3) { animation-delay: 0.3s; }
@keyframes dotPulse {
0% { transform: translateY(0); opacity: 0; }
30% { transform: translateY(-4px);opacity: 1; }
60% { transform: translateY(0); opacity: 1; }
100% { transform: translateY(0); opacity: 0; }
}
@keyframes shineText {
0% { background-position: 200% 0; }
100% { background-position: -200% 0; }
}
canvas {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 0;
background: linear-gradient(135deg, #0f0c29, #302b63, #24243e);
background-size: 400% 400%;
animation: gradientShift 15s ease infinite;
}
.content {
position: relative;
z-index: 1;
text-align: center;
}
.corner-logo {
position: fixed;
bottom: 16px;
right: 16px;
width: 64px;
height: 64px;
opacity: 0;
transition: opacity 1s ease;
z-index: 2;
}
.corner-logo.show {
opacity: 1;
}
</style>
<link href="https://fonts.googleapis.com/css2?family=Rajdhani:wght@400;700&display=swap" rel="stylesheet">
</head>
<body>
<canvas id="bg"></canvas>
<div class="content">
<div class="logo" id="logo"></div>
<div class="subtitle" id="subtitle">
loading simulation environment
<span class="dots">
<span class="dot">.</span><span class="dot">.</span><span class="dot">.</span>
</span>
</div>
</div>
<img id="cornerLogo" class="corner-logo" src="https://via.placeholder.com/64" alt="Logo" />
<script>
const canvas = document.getElementById('bg');
const ctx = canvas.getContext('2d');
const RESIZE = () => { canvas.width = innerWidth; canvas.height = innerHeight; };
RESIZE();
addEventListener('resize', RESIZE);
const POINTS = 100;
const pts = Array.from({length: POINTS}, () => ({
x: Math.random() * canvas.width,
y: Math.random() * canvas.height,
vx: (Math.random() - 0.5) * 0.2,
vy: (Math.random() - 0.5) * 0.2
}));
function drawNetwork() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
for (let i = 0; i < POINTS; i++) {
const p = pts[i];
p.x += p.vx; p.y += p.vy;
if (p.x < 0 || p.x > canvas.width) p.vx *= -1;
if (p.y < 0 || p.y > canvas.height) p.vy *= -1;
ctx.beginPath();
ctx.arc(p.x, p.y, 2, 0, Math.PI * 2);
ctx.fillStyle = 'rgba(255,255,255,0.6)';
ctx.fill();
for (let j = i + 1; j < POINTS; j++) {
const q = pts[j];
const dx = p.x - q.x, dy = p.y - q.y;
const d = Math.hypot(dx, dy);
if (d < 100) {
ctx.beginPath();
ctx.moveTo(p.x, p.y);
ctx.lineTo(q.x, q.y);
ctx.strokeStyle = `rgba(255,255,255,${1 - d / 100})`;
ctx.stroke();
}
}
}
requestAnimationFrame(drawNetwork);
}
drawNetwork();
const LOGO_TEXT = 'Network Simulator';
const BOLD_COUNT = 7;
const CHAR_DELAY = 100;
const START_DELAY = 1000;
const SUBTITLE_DELAY = 600;
function animateText(targetId, text, delayStart, charDelay, boldCount) {
const el = document.getElementById(targetId);
el.innerHTML = '';
[...text].forEach((ch, i) => {
const span = document.createElement('span');
span.textContent = ch;
if (i < boldCount) span.classList.add('heavy');
span.style.animationDelay = `${delayStart + i * charDelay}ms`;
el.appendChild(span);
});
}
animateText('logo', LOGO_TEXT, START_DELAY, CHAR_DELAY, BOLD_COUNT);
const subtitleTotalDelay = START_DELAY + LOGO_TEXT.length * CHAR_DELAY + SUBTITLE_DELAY;
setTimeout(() => {
document.getElementById('subtitle').classList.add('show');
document.getElementById('cornerLogo').classList.add('show');
}, subtitleTotalDelay);
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment