Files
repodeprueba/index.html
T
2025-09-01 19:46:58 +00:00

524 lines
20 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!doctype html>
<html lang="es">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>NeonVerse — 1file Spectacle</title>
<meta name="description" content="Neon glass UI over a Three.js galaxy background. One single HTML file with embedded CSS & JS." />
<!-- Google Font -->
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Outfit:wght@300;400;600;800&display=swap" rel="stylesheet">
<style>
/* =============================
Modern, clean, and neon style
============================= */
:root {
/* Theme variables */
--bg: #0a0b10;
--text: #eaf3ff;
--muted: #9bb3ff;
--accent: #6cf9ff;
--accent-2: #9b5cff;
--glass: rgba(255, 255, 255, 0.08);
--glass-2: rgba(255, 255, 255, 0.14);
--blur: 18px;
--card-radius: 20px;
--shadow: 0 10px 40px rgba(0,0,0,.35);
}
* { box-sizing: border-box; }
html, body { height: 100%; }
body {
margin: 0;
font-family: 'Outfit', system-ui, -apple-system, Segoe UI, Roboto, Helvetica, Arial, 'Apple Color Emoji', 'Segoe UI Emoji';
color: var(--text);
background: radial-gradient(1200px 600px at 20% 20%, #10122b 0%, #090a12 60%), var(--bg);
overflow-x: hidden;
}
/* Fullscreen WebGL background */
#bg-canvas {
position: fixed;
inset: 0;
width: 100vw; height: 100vh;
z-index: -2; /* Behind everything */
display: block;
}
/* Subtle scanline overlay for a cyber feel */
.scanlines {
pointer-events: none;
position: fixed; inset: 0; z-index: -1;
background-image: repeating-linear-gradient(
to bottom,
rgba(255,255,255,0.04),
rgba(255,255,255,0.04) 1px,
transparent 1px,
transparent 3px
);
mix-blend-mode: soft-light;
}
header {
max-width: 1200px; margin: 0 auto; padding: 56px 24px 20px;
display: flex; align-items: center; justify-content: space-between; gap: 16px;
}
.brand {
display: flex; align-items: center; gap: 14px;
text-decoration: none; color: var(--text);
}
.brand .logo {
width: 44px; height: 44px; border-radius: 12px;
background: conic-gradient(from 210deg, var(--accent), var(--accent-2), #40f0b5, var(--accent));
box-shadow: 0 0 30px rgba(108, 249, 255, .45);
position: relative; isolation: isolate;
}
.brand .logo::after {
content: ""; position: absolute; inset: 2px; border-radius: 10px;
background: radial-gradient(120px 120px at 30% 30%, rgba(255,255,255,.35), transparent 60%);
filter: blur(8px);
z-index: -1;
}
.brand h1 { font-size: clamp(20px, 2.4vw, 28px); margin: 0; letter-spacing: 0.4px; }
.brand span { display: block; font-weight: 300; font-size: 12px; color: var(--muted); margin-top: -4px; }
.controls {
display: flex; gap: 12px; align-items: center;
}
.btn {
--glow: var(--accent);
border: 1px solid rgba(255,255,255,.15);
color: var(--text);
background: linear-gradient(180deg, rgba(255,255,255,.06), rgba(255,255,255,.02));
padding: 12px 16px; border-radius: 12px; cursor: pointer;
backdrop-filter: blur(var(--blur));
box-shadow: var(--shadow), 0 0 0 0 rgba(108,249,255,.45) inset;
transition: box-shadow .3s ease, transform .15s ease;
font-weight: 600; letter-spacing: .3px;
}
.btn:hover { transform: translateY(-1px); box-shadow: var(--shadow), 0 0 0 2px rgba(108,249,255,.45) inset; }
.btn:active { transform: translateY(0); }
.toggle {
display: inline-flex; align-items: center; gap: 8px; cursor: pointer;
user-select: none; padding: 10px 12px; border-radius: 12px;
border: 1px solid rgba(255,255,255,.12);
background: rgba(255,255,255,.04);
backdrop-filter: blur(12px);
}
.toggle input { display: none; }
.toggle .dot {
width: 24px; height: 24px; border-radius: 999px; position: relative;
background: radial-gradient(circle at 30% 30%, #fff, #cde 35%, #89f 70%, #48f);
box-shadow: 0 0 16px rgba(108,249,255,.45);
transition: transform .3s ease;
}
.toggle input:checked + .dot { transform: translateX(4px) rotate(16deg) scale(1.02); }
main { max-width: 1200px; margin: 0 auto; padding: 20px 24px 80px; }
.hero {
display: grid; grid-template-columns: 1.1fr 1fr; gap: clamp(18px, 4vw, 40px); align-items: center;
}
@media (max-width: 900px) { .hero { grid-template-columns: 1fr; } }
.hero h2 {
font-size: clamp(32px, 6vw, 68px);
line-height: 1.02; margin: 8px 0 10px; font-weight: 800;
text-shadow: 0 10px 50px rgba(108,249,255,.25);
}
.hero p { color: var(--muted); font-size: clamp(14px, 1.8vw, 18px); margin: 0 0 20px; }
.cta {
display: flex; gap: 12px; flex-wrap: wrap;
}
.btn-primary {
--glow: var(--accent-2);
border-color: rgba(155,92,255,.35);
box-shadow: var(--shadow), 0 0 0 0 rgba(155,92,255,.45) inset;
}
.btn-primary:hover { box-shadow: var(--shadow), 0 0 0 2px rgba(155,92,255,.45) inset; }
.card {
border: 1px solid rgba(255,255,255,.12);
background: linear-gradient(180deg, var(--glass), rgba(255,255,255,0.05));
backdrop-filter: blur(var(--blur));
border-radius: var(--card-radius);
box-shadow: var(--shadow);
padding: clamp(14px, 2.6vw, 24px);
transform-style: preserve-3d; perspective: 1200px;
transition: transform .2s ease, box-shadow .35s ease;
}
.card:hover { transform: translateY(-2px) rotateX(1deg) rotateY(-1deg); box-shadow: 0 20px 60px rgba(0,0,0,.45); }
.card h3 { margin: 0 0 8px; font-size: clamp(18px, 2.4vw, 22px); }
.card p { margin: 0; color: var(--muted); }
.grid { margin-top: 28px; display: grid; grid-template-columns: repeat(3, 1fr); gap: 16px; }
@media (max-width: 1000px) { .grid { grid-template-columns: repeat(2,1fr); } }
@media (max-width: 640px) { .grid { grid-template-columns: 1fr; } }
.icon {
width: 42px; height: 42px; border-radius: 12px;
display: grid; place-items: center; margin-bottom: 10px;
background: radial-gradient(120px 60px at 30% 20%, rgba(255,255,255,.35), transparent 60%),
linear-gradient(180deg, rgba(108,249,255,.25), rgba(155,92,255,.25));
border: 1px solid rgba(255,255,255,.18);
box-shadow: 0 6px 22px rgba(108,249,255,.3);
}
footer { opacity: .75; text-align: center; padding: 30px 0 40px; font-size: 13px; color: var(--muted); }
/* Decorative blurred blobs */
.blob {
position: fixed; filter: blur(60px); opacity: .35; z-index: -1; pointer-events: none;
width: 45vmax; height: 45vmax; border-radius: 999px;
background: radial-gradient(circle at 30% 30%, rgba(108,249,255,.6), transparent 60%),
radial-gradient(circle at 70% 70%, rgba(155,92,255,.6), transparent 60%);
mix-blend-mode: screen;
}
.blob.one { top: -10vmax; left: -10vmax; }
.blob.two { bottom: -15vmax; right: -10vmax; }
/* "Hyperdrive" visual feedback */
.hyper-outline { outline: 2px solid rgba(108,249,255,.0); outline-offset: 0; transition: outline-color .25s ease, outline-offset .25s ease; }
.hyper-outline.active { outline-color: rgba(108,249,255,.6); outline-offset: 8px; }
/* Hide the noscript hint unless JS is disabled */
noscript { position: fixed; inset: 0; background: #02030a; color: #fff; display: grid; place-items: center; z-index: 999; padding: 32px; text-align: center; }
</style>
</head>
<body>
<canvas id="bg-canvas"></canvas>
<div class="scanlines"></div>
<div class="blob one"></div>
<div class="blob two"></div>
<header>
<a class="brand" href="#">
<div class="logo" aria-hidden="true"></div>
<div>
<h1>NeonVerse</h1>
<span>Un archivo · Todo el espectáculo</span>
</div>
</a>
<div class="controls">
<label class="toggle" title="Modo Calma / Hiper">
<input id="modeToggle" type="checkbox" />
<div class="dot" aria-hidden="true"></div>
<span>Calma</span>
</label>
<button id="btnHyper" class="btn btn-primary">Lanzar Hiperimpulso ⚡️</button>
<button id="btnReset" class="btn">Reiniciar</button>
</div>
</header>
<main>
<section class="hero card hyper-outline" id="hero">
<div>
<h2>Futuro en una sola página</h2>
<p>Fondo 3D reactivo con <strong>Three.js</strong>, animaciones fluidas con <strong>GSAP</strong> y una UI de cristal. Todo en este único archivo HTML.</p>
<div class="cta">
<button class="btn btn-primary" id="btnPulse">Pulso Neón</button>
<button class="btn" id="btnParticles">Explosión de Estrellas</button>
</div>
</div>
<div class="card" style="min-height: 220px; display:flex; align-items:center; justify-content:center;">
<svg width="180" height="160" viewBox="0 0 180 160" fill="none" xmlns="http://www.w3.org/2000/svg" aria-hidden="true">
<defs>
<linearGradient id="grad" x1="0" y1="0" x2="1" y2="1">
<stop offset="0%" stop-color="#6cf9ff"/>
<stop offset="100%" stop-color="#9b5cff"/>
</linearGradient>
</defs>
<g opacity="0.95" stroke="url(#grad)" stroke-width="2">
<path d="M20 120 C 60 40, 120 40, 160 120"/>
<circle cx="90" cy="80" r="36"/>
<circle cx="90" cy="80" r="56" opacity=".45"/>
<circle cx="90" cy="80" r="70" opacity=".2"/>
</g>
</svg>
</div>
</section>
<section class="grid">
<article class="card">
<div class="icon">⚙️</div>
<h3>Interacción en tiempo real</h3>
<p>El campo de estrellas responde al ratón y a la velocidad del modo <em>Hiper</em>.</p>
</article>
<article class="card">
<div class="icon">🧪</div>
<h3>Una sola dependencia</h3>
<p>Three.js para el 3D y GSAP para animar. Nada más. Ligero y espectacular.</p>
</article>
<article class="card">
<div class="icon">🎛️</div>
<h3>Controles divertidos</h3>
<p>Activa el pulso, explota partículas o enciende el hiperimpulso y siente la aceleración.</p>
</article>
</section>
<footer>
Hecho con ♥︎ · Three.js · GSAP · HTML/CSS · En un solo archivo
</footer>
</main>
<noscript>
<div>
<h2>Necesitas JavaScript</h2>
<p>Esta demo usa WebGL y animaciones. Activa JavaScript para verla en acción ✨</p>
</div>
</noscript>
<!-- External JS libraries (CDN). Still a single HTML file; no separate JS/CSS files used. -->
<script src="https://unpkg.com/three@0.160.0/build/three.min.js"></script>
<script src="https://unpkg.com/gsap@3.12.5/dist/gsap.min.js"></script>
<script>
// =============================
// NeonVerse — Single-file demo
// (c) You — Free to tweak
// Comments in EN as requested
// =============================
// ---- Three.js setup ----
const canvas = document.getElementById('bg-canvas');
const scene = new THREE.Scene();
const renderer = new THREE.WebGLRenderer({ canvas, antialias: true, alpha: true });
renderer.setPixelRatio(Math.min(devicePixelRatio, 2));
renderer.setSize(window.innerWidth, window.innerHeight);
const camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 0.1, 2000);
camera.position.set(0, 0, 140);
// Starfield parameters
const STAR_COUNT = 3000; // adjust for performance
const galaxy = new THREE.Group();
// Create a spiral galaxy-like distribution
const positions = new Float32Array(STAR_COUNT * 3);
const colors = new Float32Array(STAR_COUNT * 3);
// Helper to set HSL into rgb Float32 array
const color = new THREE.Color();
for (let i = 0; i < STAR_COUNT; i++) {
// Spiral params
const radius = Math.pow(Math.random(), 0.6) * 380 + 20; // denser near center
const arms = 3;
const branchAngle = ((i % arms) / arms) * Math.PI * 2;
const spin = radius * 0.035;
const randomness = (Math.random() - 0.5) * 28;
const angle = branchAngle + spin;
const x = Math.cos(angle) * radius + randomness;
const y = (Math.random() - 0.5) * 24 + Math.sin(angle * 3) * 3; // slight vertical waves
const z = Math.sin(angle) * radius + randomness;
const idx = i * 3;
positions[idx] = x; positions[idx + 1] = y; positions[idx + 2] = z;
// Color gradient across radius (neon cyan -> magenta)
const t = radius / 420;
color.setHSL(0.55 + 0.3 * (1 - t), 0.9, 0.6); // 0.55≈cyan to 0.85≈magenta
colors[idx] = color.r; colors[idx + 1] = color.g; colors[idx + 2] = color.b;
}
const geometry = new THREE.BufferGeometry();
geometry.setAttribute('position', new THREE.Float32BufferAttribute(positions, 3));
geometry.setAttribute('color', new THREE.Float32BufferAttribute(colors, 3));
const material = new THREE.PointsMaterial({
size: 1.7,
vertexColors: true,
transparent: true,
opacity: 0.95,
depthWrite: false,
blending: THREE.AdditiveBlending
});
const points = new THREE.Points(geometry, material);
galaxy.add(points);
scene.add(galaxy);
// Subtle core glow (sprite)
const coreTexture = new THREE.CanvasTexture(generateRadialGlow(256));
const coreMaterial = new THREE.SpriteMaterial({ map: coreTexture, transparent: true, depthWrite: false, blending: THREE.AdditiveBlending, opacity: 0.8 });
const core = new THREE.Sprite(coreMaterial);
core.scale.set(120, 120, 1);
scene.add(core);
function generateRadialGlow(size) {
// Create a radial gradient canvas for the galaxy core
const c = document.createElement('canvas');
c.width = c.height = size;
const ctx = c.getContext('2d');
const g = ctx.createRadialGradient(size/2, size/2, 0, size/2, size/2, size/2);
g.addColorStop(0, 'rgba(255,255,255,0.95)');
g.addColorStop(0.25, 'rgba(108,249,255,0.85)');
g.addColorStop(0.55, 'rgba(155,92,255,0.55)');
g.addColorStop(1, 'rgba(0,0,0,0)');
ctx.fillStyle = g; ctx.fillRect(0,0,size,size);
return c;
}
// Interaction state
const state = {
rotSpeed: 0.0026, // base rotation speed
zoom: 0,
hyper: false,
parallaxX: 0,
parallaxY: 0
};
// Parallax by mouse/touch
const lerp = (a, b, t) => a + (b - a) * t;
let targetX = 0, targetY = 0;
window.addEventListener('pointermove', (e) => {
const nx = (e.clientX / window.innerWidth) * 2 - 1;
const ny = (e.clientY / window.innerHeight) * 2 - 1;
targetX = nx * 0.6;
targetY = ny * 0.4;
}, { passive: true });
// Animation loop
const clock = new THREE.Clock();
function animate() {
const dt = clock.getDelta();
// Smooth parallax
state.parallaxX = lerp(state.parallaxX, targetX, 0.05);
state.parallaxY = lerp(state.parallaxY, targetY, 0.05);
camera.position.x = state.parallaxX * 20;
camera.position.y = -state.parallaxY * 12;
camera.lookAt(0,0,0);
// Galaxy rotation
galaxy.rotation.y += state.rotSpeed;
galaxy.rotation.x = lerp(galaxy.rotation.x, state.parallaxY * 0.25, 0.02);
// Subtle material twinkle
const time = performance.now() * 0.001;
material.size = 1.5 + Math.sin(time * 1.7) * 0.2 + (state.hyper ? 0.6 : 0);
material.opacity = 0.9 + Math.sin(time * 0.9) * 0.08 + (state.hyper ? 0.05 : 0);
renderer.render(scene, camera);
requestAnimationFrame(animate);
}
animate();
// Handle resize
addEventListener('resize', () => {
camera.aspect = innerWidth / innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(innerWidth, innerHeight);
});
// ---- GSAP UI animations ----
const hero = document.getElementById('hero');
const btnHyper = document.getElementById('btnHyper');
const btnReset = document.getElementById('btnReset');
const btnPulse = document.getElementById('btnPulse');
const btnParticles = document.getElementById('btnParticles');
const modeToggle = document.getElementById('modeToggle');
// Intro animation
gsap.from(['.brand', '.controls'], { y: -16, opacity: 0, duration: 0.9, ease: 'power2.out', stagger: 0.08 });
gsap.from('.hero .card', { y: 20, opacity: 0, duration: 1, ease: 'power3.out', stagger: 0.14, delay: 0.1 });
gsap.from('.grid .card', { y: 24, opacity: 0, duration: 0.9, ease: 'power3.out', stagger: 0.08, delay: 0.25 });
// Helper: pulse outline on hero card
function pulseOutline() {
hero.classList.add('active');
gsap.fromTo(hero, { outlineOffset: 0 }, { outlineOffset: 8, duration: 0.3, yoyo: true, repeat: 1, onComplete: () => hero.classList.remove('active') });
}
// Hyperdrive action — accelerate, recolor subtly, add camera shake
btnHyper.addEventListener('click', () => {
state.hyper = true;
pulseOutline();
gsap.to(state, { rotSpeed: 0.020, duration: 0.8, ease: 'power3.out' });
// Quick camera kick
gsap.fromTo(camera.position, { z: 160 }, { z: 120, duration: 0.8, ease: 'power2.out', yoyo: true, repeat: 1 });
// Screen pulse via blobs
gsap.to('.blob', { opacity: 0.55, scale: 1.08, duration: 0.6, yoyo: true, repeat: 1, ease: 'sine.inOut' });
});
// Reset to calm
btnReset.addEventListener('click', () => {
state.hyper = false;
gsap.to(state, { rotSpeed: 0.0026, duration: 1.2, ease: 'power3.inOut' });
gsap.to(camera.position, { x: 0, y: 0, z: 140, duration: 0.9, ease: 'power2.inOut' });
gsap.to('.blob', { opacity: 0.35, duration: 0.8 });
});
// Neon pulse — quick UI and core glow amp
btnPulse.addEventListener('click', () => {
pulseOutline();
gsap.fromTo(core.material, { opacity: 0.6 }, { opacity: 1, duration: 0.35, yoyo: true, repeat: 1, ease: 'sine.inOut' });
gsap.to('.brand .logo', { boxShadow: '0 0 60px rgba(108,249,255,.8)', duration: 0.35, yoyo: true, repeat: 1, ease: 'sine.inOut' });
});
// Particles burst — radial push on positions for a brief spark
btnParticles.addEventListener('click', () => {
const pos = geometry.attributes.position;
const burst = [];
for (let i = 0; i < STAR_COUNT; i += Math.floor(Math.random()*30)+15) {
const idx = i * 3;
const x = pos.array[idx], y = pos.array[idx+1], z = pos.array[idx+2];
const len = Math.max(60, Math.hypot(x, y, z));
const nx = x/len, ny = y/len, nz = z/len;
burst.push({ idx, dx: nx * 20, dy: ny * 20, dz: nz * 20 });
}
// Animate selected vertices
gsap.to(burst, {
duration: 0.5,
ease: 'power3.out',
onUpdate: () => {
burst.forEach(b => {
pos.array[b.idx] += b.dx * 0.35;
pos.array[b.idx+1] += b.dy * 0.35;
pos.array[b.idx+2] += b.dz * 0.35;
});
pos.needsUpdate = true;
}
});
// spring back
gsap.to({}, {
duration: 0.7,
delay: 0.35,
ease: 'power2.in',
onUpdate: () => { galaxy.rotation.y += 0.01; }
});
pulseOutline();
});
// Mode toggle: calm vs hyper bias
modeToggle.addEventListener('change', (e) => {
const on = e.target.checked;
state.hyper = on;
gsap.to(state, { rotSpeed: on ? 0.010 : 0.003, duration: 0.8, ease: 'power2.inOut' });
gsap.to('.blob', { opacity: on ? 0.5 : 0.35, duration: 0.6 });
});
// Accessibility: key shortcuts
addEventListener('keydown', (e) => {
if (e.key.toLowerCase() === 'h') btnHyper.click();
if (e.key.toLowerCase() === 'r') btnReset.click();
if (e.key.toLowerCase() === 'p') btnPulse.click();
});
// Optional: reduce motion respect
const mq = matchMedia('(prefers-reduced-motion: reduce)');
if (mq.matches) { state.rotSpeed = 0.001; }
</script>
</body>
</html>