咨询pygar.co/en网站中Goo ball鼠标跟随效果的实现方式
Great question! I’ve poked around similar gooey mouse-follower effects before, and the one you’re referencing relies on a mix of canvas rendering and lightweight physics simulation to get that satisfying squishy, cohesive behavior. Let me break down how it works:
1. Particle System with Mouse Tracking
First, the effect uses a cluster of small, interconnected particles. Each particle tracks the mouse’s position:
- We listen for
mousemoveevents to get the current mouse coordinates. - Each particle is given a gentle "pull" toward the mouse position every frame, using basic velocity updates.
- The particles start clustered together, so they move as a group but retain individual movement.
2. Spring & Damping Physics for Cohesion
The key to the "gooey" stickiness is simulating spring-like forces between particles:
- For every pair of particles, we calculate the distance between them. If they drift too far from a predefined ideal distance, we apply a pulling force (using Hooke’s Law for spring physics) to bring them back together.
- We add damping (a velocity multiplier < 1) to slow particles over time, preventing infinite bouncing and making the movement feel smooth and natural.
3. Canvas Filter Magic for the Gooey Look
Raw particles would just look like a bunch of circles—here’s how we turn them into a single blob:
- Draw all particles to an off-screen canvas (or the main canvas first).
- Apply a Gaussian blur filter to soften the edges of the particles, making them blend together.
- Use a brightness/threshold filter and
globalCompositeOperation(likesource-atoporlighter) to "re-sharpen" the blended edges, creating that distinct rounded blob shape.
Here’s a stripped-down Canvas 2D implementation that captures the core behavior:
// Set up canvas const canvas = document.getElementById('goo-canvas'); const ctx = canvas.getContext('2d'); canvas.width = window.innerWidth; canvas.height = window.innerHeight; // Particle class with physics class Particle { constructor(x, y) { this.x = x; this.y = y; this.vx = 0; this.vy = 0; this.radius = 8; this.damping = 0.95; // Reduces velocity over time this.springStrength = 0.1; // How strongly particles pull each other } update(mouseX, mouseY, allParticles) { // Pull particle toward mouse const mousePull = 0.02; this.vx += (mouseX - this.x) * mousePull; this.vy += (mouseY - this.y) * mousePull; // Apply spring forces between particles allParticles.forEach(particle => { if (particle === this) return; const dx = particle.x - this.x; const dy = particle.y - this.y; const distance = Math.sqrt(dx ** 2 + dy ** 2); const idealDistance = 12; // Target distance between particles if (distance > 0 && distance < idealDistance * 2) { const force = (distance - idealDistance) * this.springStrength; this.vx += (dx / distance) * force; this.vy += (dy / distance) * force; } }); // Apply damping this.vx *= this.damping; this.vy *= this.damping; // Update position this.x += this.vx; this.y += this.vy; } draw(ctx) { ctx.beginPath(); ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2); ctx.fillStyle = '#2ecc71'; ctx.fill(); } } // Initialize particle cluster const particles = []; const particleCount = 20; for (let i = 0; i < particleCount; i++) { particles.push(new Particle(window.innerWidth / 2, window.innerHeight / 2)); } // Track mouse position let mouseX = window.innerWidth / 2; let mouseY = window.innerHeight / 2; window.addEventListener('mousemove', e => { mouseX = e.clientX; mouseY = e.clientY; }); // Animation loop function animate() { ctx.clearRect(0, 0, canvas.width, canvas.height); // Update and draw particles particles.forEach(p => p.update(mouseX, mouseY, particles)); particles.forEach(p => p.draw(ctx)); // Apply gooey filters ctx.filter = 'blur(8px) brightness(1.2)'; ctx.drawImage(canvas, 0, 0); ctx.filter = 'none'; ctx.globalCompositeOperation = 'source-atop'; ctx.fillStyle = '#2ecc71'; ctx.fillRect(0, 0, canvas.width, canvas.height); requestAnimationFrame(animate); } animate();
The actual implementation on that site might include:
- WebGL rendering instead of Canvas 2D for better performance with more particles.
- Fine-tuned physics parameters (like variable particle mass or different spring strengths) for a more organic feel.
- Boundary collision to prevent the blob from moving off-screen.
- Color gradients or texture overlays to match the site’s design.
内容的提问来源于stack exchange,提问作者zanarduz

