You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

咨询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:

Core Components of the Goo Ball Effect

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 mousemove events 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 (like source-atop or lighter) to "re-sharpen" the blended edges, creating that distinct rounded blob shape.
Simplified Code Example

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();
Real-World Optimizations

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.14 08:02:00