WEBLEB
Inicio
Editora de código
Iniciar sesión
Pro
Español
English
Français
Español
Português
Deutsch
Italiano
हिंदी
Lienzo: Flujo de partículas del corazón
1005
zegarkidawida
Abrir en el editor
Publica tu código
Recomendado
30 December 2024
Un código de cleancloud199
11 December 2024
Un código de slowapp370
16 September 2025
Plantilla HTML del formulario de envío de monedas de TikTok
HTML
Copy
Controls
Full Screen
Colors:
Rainbow
Red
Green
Blue
Monochrome
Size:
10
Particles:
32
Speed:
1
Mouse Follow:
50
CSS
Copy
body { margin: 0; padding: 0; overflow: hidden; background-color: #000; font-family: "Archivo Black", sans-serif; color: white; } #c { position: fixed; top: 0; left: 0; width: 100%; height: 100%; z-index: 1; } #controls-container { position: fixed; top: 20px; left: 20px; z-index: 100; } #toggle-controls { background: rgba(0, 0, 0, 0.7); color: white; border: none; padding: 10px; border-radius: 5px; cursor: pointer; margin-bottom: 5px; backdrop-filter: blur(5px); } #controls { background: rgba(100, 100, 100, 0.2); padding: 15px; border-radius: 10px; max-width: 300px; backdrop-filter: blur(5px); display: block; } .control-group { margin: 10px 0; display: flex; align-items: center; } .control-group label { width: 100px; font-size: 14px; margin-right: 10px; } .control-group input[type="range"] { flex-grow: 1; margin-right: 10px; } .control-group span { width: 30px; text-align: right; font-size: 14px; } .control-group select { flex-grow: 1; padding: 5px; border-radius: 4px; background: #333; color: white; border: none; } .heart-outline { position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); width: 360px; height: 320px; fill: none; stroke: rgba(255, 255, 255, 0.3); stroke-width: 2; pointer-events: none; z-index: 10; } #intro-controls { margin-top: 20px; padding: 20px; border-radius: 10px; background-color: rgba(0, 0, 0, 0.5); } #intro-controls h2 { font-size: 1.5em; margin-bottom: 10px; } #intro-controls ul { list-style-type: none; padding: 0; } #intro-controls li { margin-bottom: 5px; font-size: 1em; } #toggle-fullscreen { position: fixed; top: 20px; right: 20px; background: rgba(0, 0, 0, 0.7); color: white; border: none; padding: 10px; border-radius: 5px; cursor: pointer; z-index: 100; backdrop-filter: blur(5px); } @media (max-width: 600px) { #controls-container { top: 10px; left: 10px; right: 10px; max-width: none; } .control-group label { width: 80px; font-size: 12px; } }
JS
Copy
// Configuration const config = { particleCount: 32, speed: 1, colorScheme: 'rainbow', mouseInfluence: 50, showHeartOutline: true, particleSize: 10 }; // Canvas setup const canvas = document.getElementById('c'); const ctx = canvas.getContext('2d'); let canvasWidth = canvas.width = window.innerWidth; let canvasHeight = canvas.height = window.innerHeight; // Animation state let trails = []; let heartPath = []; let mouseX = canvasWidth / 2; let mouseY = canvasHeight / 2; let mouseActive = false; let animationRunning = false; // Initialize heart path points (fixed formula) function initHeartPath() { heartPath = []; const PI2 = 6.28318; // 2*PI approximation const steps = Math.max(32, config.particleCount); for (let i = 0; i < steps; i++) { const t = (i / steps) * PI2; heartPath.push([ canvasWidth/2 + 180 * Math.pow(Math.sin(t), 3), canvasHeight/2 + 10 * (-(15 * Math.cos(t) - 5 * Math.cos(2*t) - 2 * Math.cos(3*t) - Math.cos(4*t))) ]); } } // Initialize particles with proper bounds checking function initParticles() { trails = []; if (heartPath.length === 0) initHeartPath(); for (let i = 0; i < config.particleCount; i++) { const particles = []; const x = Math.random() * canvasWidth; const y = Math.random() * canvasHeight; for (let k = 0; k < config.particleCount; k++) { // Color generation let hue, saturation = Math.random() * 40 + 60; let brightness = Math.random() * 60 + 20; switch(config.colorScheme) { case 'red': hue = Math.random() * 20 + 350; break; case 'blue': hue = Math.random() * 20 + 200; break; case 'green': hue = Math.random() * 20 + 100; break; case 'monochrome': hue = 0; saturation = 0; break; default: hue = i/config.particleCount * 360; // rainbow } particles.push({ x, y, velX: 0, velY: 0, radius: ((1 - k/config.particleCount) + 1) * config.particleSize/2, speed: Math.random() + 1, targetIndex: Math.floor(Math.random() * heartPath.length), direction: i % 2 * 2 - 1, friction: Math.random() * 0.2 + 0.7, color: `hsla(${hue},${saturation}%,${brightness}%,0.1)` }); } trails.push(particles); } } // Render a single particle function renderParticle(particle) { ctx.fillStyle = particle.color; ctx.beginPath(); ctx.arc(particle.x, particle.y, particle.radius, 0, Math.PI * 2); ctx.fill(); } // Main animation loop with robust error handling function animationLoop() { if (!animationRunning) return; // Stop the animation if not running try { // Clear with trail effect ctx.fillStyle = "rgba(0, 0, 0, 0.2)"; ctx.fillRect(0, 0, canvasWidth, canvasHeight); trails.forEach(trail => { if (!trail || !trail.length) return; const leader = trail[0]; const target = heartPath[leader.targetIndex % heartPath.length]; if (!target) return; // Mouse influence if (mouseActive && config.mouseInfluence > 0) { const dx = mouseX - leader.x; const dy = mouseY - leader.y; const dist = Math.sqrt(dx*dx + dy*dy); if (dist < 300) { const force = (1 - dist/300) * (config.mouseInfluence/20); leader.velX += dx/dist * force; leader.velY += dy/dist * force; } } // Move toward target const dx = leader.x - target[0]; const dy = leader.y - target[1]; const dist = Math.sqrt(dx*dx + dy*dy); if (dist < 10) { if (Math.random() > 0.95) { leader.targetIndex = Math.floor(Math.random() * heartPath.length); } else { if (Math.random() > 0.99) leader.direction *= -1; leader.targetIndex += leader.direction; leader.targetIndex = (leader.targetIndex + heartPath.length) % heartPath.length; } } // Update physics leader.velX += -dx/dist * leader.speed * config.speed; leader.velY += -dy/dist * leader.speed * config.speed; leader.x += leader.velX; leader.y += leader.velY; leader.velX *= leader.friction; leader.velY *= leader.friction; // Render trail renderParticle(leader); for (let k = 1; k < trail.length; k++) { trail[k].x -= (trail[k].x - trail[k-1].x) * 0.7; trail[k].y -= (trail[k].y - trail[k-1].y) * 0.7; renderParticle(trail[k]); } }); } catch (error) { console.error("Animation error:", error); } requestAnimationFrame(animationLoop); } // Control handlers function setupControls() { const controls = document.getElementById('controls'); let controlsVisible = true; document.getElementById('toggle-controls').addEventListener('click', () => { controlsVisible = !controlsVisible; controls.style.display = controlsVisible ? 'block' : 'none'; }); document.getElementById('toggle-fullscreen').addEventListener('click', () => { if (!document.fullscreenElement) { document.documentElement.requestFullscreen(); } else { if (document.exitFullscreen) { document.exitFullscreen(); } } }); const updateParticles = () => { config.particleCount = Math.min(100, Math.max(10, parseInt(document.getElementById('particleCount').value))); document.getElementById('particleCountValue').textContent = config.particleCount; initHeartPath(); initParticles(); }; document.getElementById('particleCount').addEventListener('input', updateParticles); document.getElementById('speed').addEventListener('input', (e) => { config.speed = parseFloat(e.target.value); document.getElementById('speedValue').textContent = config.speed; }); document.getElementById('colorScheme').addEventListener('change', (e) => { config.colorScheme = e.target.value; initParticles(); }); document.getElementById('mouseInfluence').addEventListener('input', (e) => { config.mouseInfluence = parseInt(e.target.value); document.getElementById('mouseInfluenceValue').textContent = config.mouseInfluence; }); document.getElementById('particleSize').addEventListener('input', (e) => { config.particleSize = parseInt(e.target.value); document.getElementById('particleSizeValue').textContent = config.particleSize; initParticles(); }); // Mouse tracking document.addEventListener('mousemove', (e) => { mouseX = e.clientX; mouseY = e.clientY; mouseActive = true; }); document.addEventListener('mouseleave', () => mouseActive = false); // Window resize window.addEventListener('resize', () => { canvasWidth = canvas.width = window.innerWidth; canvasHeight = canvas.height = window.innerHeight; initHeartPath(); }); } // Initialize everything function init() { const controls = document.getElementById('controls'); const canvas = document.getElementById('c'); controls.style.display = 'block'; canvas.style.display = 'block'; animationRunning = true; setupControls(); animationLoop(); // Start the animation loop // Initialize everything initHeartPath(); initParticles(); } // Start the animation init();