Fondo de Partículas Animado
Avanzado
Fondo interactivo con partículas que reaccionan al movimiento del mouse. Perfecto para hero sections y landing pages modernas.
Vista Previa en Vivo
Implementación del Código
HTML
<div class="particles-demo">
<div class="particles-container">
<canvas id="particles-canvas"></canvas>
<div class="particles-content">
<h1 class="hero-title">Bienvenido al Futuro</h1>
<p class="hero-subtitle">Experiencia interactiva con partículas</p>
<button class="hero-cta">Explorar Más</button>
</div>
</div>
</div>
CSS
.particles-demo {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
margin: 0;
padding: 0;
height: 400px;
overflow: hidden;
position: relative;
}
.particles-container {
position: relative;
width: 100%;
height: 100%;
background: linear-gradient(135deg, #0f0c29 0%, #302b63 50%, #24243e 100%);
overflow: hidden;
}
#particles-canvas {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 1;
cursor: crosshair;
}
.particles-content {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
text-align: center;
color: white;
z-index: 2;
pointer-events: none;
}
.hero-title {
font-size: 3rem;
font-weight: 700;
margin-bottom: 1rem;
background: linear-gradient(45deg, #667eea, #764ba2, #f093fb);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
animation: titleGlow 3s ease-in-out infinite alternate;
}
.hero-subtitle {
font-size: 1.2rem;
opacity: 0.8;
margin-bottom: 2rem;
animation: fadeInUp 1s ease-out 0.5s both;
}
.hero-cta {
padding: 0.8rem 2rem;
font-size: 1rem;
background: linear-gradient(135deg, #667eea, #764ba2);
color: white;
border: none;
border-radius: 50px;
cursor: pointer;
transition: all 0.3s ease;
pointer-events: auto;
animation: fadeInUp 1s ease-out 1s both;
position: relative;
overflow: hidden;
}
.hero-cta:hover {
transform: translateY(-3px);
box-shadow: 0 10px 25px rgba(102, 126, 234, 0.4);
}
.hero-cta::before {
content: '';
position: absolute;
top: 0;
left: -100%;
width: 100%;
height: 100%;
background: linear-gradient(90deg, transparent, rgba(255,255,255,0.2), transparent);
transition: left 0.5s;
}
.hero-cta:hover::before {
left: 100%;
}
@keyframes titleGlow {
0% {
text-shadow: 0 0 20px rgba(102, 126, 234, 0.5);
}
100% {
text-shadow: 0 0 30px rgba(118, 75, 162, 0.8), 0 0 40px rgba(240, 147, 251, 0.6);
}
}
@keyframes fadeInUp {
from {
opacity: 0;
transform: translateY(30px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
@media (max-width: 768px) {
.hero-title {
font-size: 2rem;
}
.hero-subtitle {
font-size: 1rem;
}
.hero-cta {
padding: 0.6rem 1.5rem;
font-size: 0.9rem;
}
}
JavaScript
class ParticleSystem {
constructor(canvas) {
this.canvas = canvas;
this.ctx = canvas.getContext('2d');
this.particles = [];
this.mouse = { x: 0, y: 0 };
this.animationId = null;
this.resize();
this.init();
this.bindEvents();
this.animate();
}
resize() {
this.canvas.width = this.canvas.offsetWidth;
this.canvas.height = this.canvas.offsetHeight;
}
init() {
this.particles = [];
const particleCount = Math.floor((this.canvas.width * this.canvas.height) / 8000);
for (let i = 0; i < particleCount; i++) {
this.particles.push({
x: Math.random() * this.canvas.width,
y: Math.random() * this.canvas.height,
vx: (Math.random() - 0.5) * 0.5,
vy: (Math.random() - 0.5) * 0.5,
size: Math.random() * 2 + 1,
opacity: Math.random() * 0.5 + 0.2,
color: this.getRandomColor()
});
}
}
getRandomColor() {
const colors = [
'rgba(102, 126, 234, ',
'rgba(118, 75, 162, ',
'rgba(240, 147, 251, ',
'rgba(255, 255, 255, '
];
return colors[Math.floor(Math.random() * colors.length)];
}
bindEvents() {
window.addEventListener('resize', () => {
this.resize();
this.init();
});
this.canvas.addEventListener('mousemove', (e) => {
const rect = this.canvas.getBoundingClientRect();
this.mouse.x = e.clientX - rect.left;
this.mouse.y = e.clientY - rect.top;
});
this.canvas.addEventListener('mouseleave', () => {
this.mouse.x = -1000;
this.mouse.y = -1000;
});
}
updateParticles() {
this.particles.forEach(particle => {
// Mouse interaction
const dx = this.mouse.x - particle.x;
const dy = this.mouse.y - particle.y;
const distance = Math.sqrt(dx * dx + dy * dy);
if (distance < 100) {
const force = (100 - distance) / 100;
particle.vx -= (dx / distance) * force * 0.01;
particle.vy -= (dy / distance) * force * 0.01;
}
// Update position
particle.x += particle.vx;
particle.y += particle.vy;
// Boundary check
if (particle.x < 0 || particle.x > this.canvas.width) {
particle.vx *= -1;
particle.x = Math.max(0, Math.min(this.canvas.width, particle.x));
}
if (particle.y < 0 || particle.y > this.canvas.height) {
particle.vy *= -1;
particle.y = Math.max(0, Math.min(this.canvas.height, particle.y));
}
// Damping
particle.vx *= 0.99;
particle.vy *= 0.99;
});
}
drawParticles() {
this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
this.particles.forEach(particle => {
this.ctx.beginPath();
this.ctx.arc(particle.x, particle.y, particle.size, 0, Math.PI * 2);
this.ctx.fillStyle = particle.color + particle.opacity + ')';
this.ctx.fill();
// Draw connections
this.particles.forEach(otherParticle => {
const dx = particle.x - otherParticle.x;
const dy = particle.y - otherParticle.y;
const distance = Math.sqrt(dx * dx + dy * dy);
if (distance < 80) {
this.ctx.beginPath();
this.ctx.moveTo(particle.x, particle.y);
this.ctx.lineTo(otherParticle.x, otherParticle.y);
this.ctx.strokeStyle = `rgba(102, 126, 234, ${0.1 * (1 - distance / 80)})`;
this.ctx.lineWidth = 0.5;
this.ctx.stroke();
}
});
});
}
animate() {
this.updateParticles();
this.drawParticles();
this.animationId = requestAnimationFrame(() => this.animate());
}
destroy() {
if (this.animationId) {
cancelAnimationFrame(this.animationId);
}
window.removeEventListener('resize', this.resize);
}
}
// Initialize when DOM is loaded
document.addEventListener('DOMContentLoaded', () => {
const canvas = document.getElementById('particles-canvas');
if (canvas) {
new ParticleSystem(canvas);
}
});