← Blurr Motion cta-magnetic-button-brutalist
Categorie cta Tier 1 Techniek #12 Deps
[ MAGNETIC / 002 ]

PULL
ME
IN.

// CURSOR ATTRACTS — AUTO-DEMO LOOPT IN ∞

1. Mechanisme — kopieer 1-op-1, geen styling-keuzes
// Mechanisme: cta-magnetic-button-brutalist
// Magnetic-tilt naar cursor (0.3 * delta, capped). Auto-demo: programmatic
// lemniscaat-pad (∞-curve) zodat de knop bij viewport-enter automatisch zwiept.
const root = document.querySelector('.mag-block');
if (root && !window.matchMedia('(prefers-reduced-motion: reduce)').matches) {
  const btn = root.querySelector('.mag-btn');
  const stage = root.querySelector('.mag-stage');
  const CAP = 36;
  const STRENGTH = 0.3;
  let cx = 0, cy = 0, userActive = false, userTimer;

  const apply = (dx, dy) => {
    const mag = Math.hypot(dx, dy) || 1;
    const k = Math.min(1, CAP / (mag * STRENGTH));
    btn.style.transform = 'translate(' + (dx * STRENGTH * k) + 'px,' + (dy * STRENGTH * k) + 'px)';
  };

  stage.addEventListener('mousemove', (e) => {
    userActive = true;
    clearTimeout(userTimer);
    userTimer = setTimeout(() => userActive = false, 1500);
    const r = btn.getBoundingClientRect();
    cx = r.left + r.width / 2;
    cy = r.top + r.height / 2;
    apply(e.clientX - cx, e.clientY - cy);
  });
  stage.addEventListener('mouseleave', () => { btn.style.transform = ''; });

  // Auto-demo: lemniscaat van Bernoulli
  const io = new IntersectionObserver((entries) => {
    entries.forEach(en => {
      if (en.isIntersecting && !btn._auto) {
        btn._auto = true;
        let t0 = performance.now();
        const loop = (now) => {
          if (userActive) { btn._auto && requestAnimationFrame(loop); return; }
          const t = (now - t0) / 1000;
          const a = 140;
          const s = Math.sin(t * 0.9);
          const c = Math.cos(t * 0.9);
          const dx = (a * c) / (1 + s * s);
          const dy = (a * s * c) / (1 + s * s);
          apply(dx, dy);
          if (btn._auto) requestAnimationFrame(loop);
        };
        requestAnimationFrame(loop);
      }
    });
  }, { threshold: 0.2 });
  io.observe(stage);
}
2. Skeleton — DOM + class-namen, mag herschikken
<!-- Skeleton: cta-magnetic-button-brutalist -->
<section class="mag-block">
  <span class="mag-tag">[ MAGNETIC / 002 ]</span>
  <h2 class="mag-headline">PULL<br/>ME<br/>IN.</h2>
  <div class="mag-stage">
    <button class="mag-btn" type="button">
      <span class="mag-arrow">→</span>
      <span class="mag-label">START PROJECT</span>
    </button>
  </div>
  <p class="mag-foot">// CURSOR ATTRACTS — RELEASE TO RESET</p>
</section>
3. Styling-template — verplicht eigen invulling per merk
/* Styling: cta-magnetic-button-brutalist — 2px borders, no-radius, JetBrains Mono UPPERCASE, hot-spot */
.mag-block {
  --paper: #FFFFFF;
  --ink: #0A0A0A;
  --hot: #FF4A1C;
  background: var(--paper);
  color: var(--ink);
  font-family: 'JetBrains Mono', monospace;
  text-transform: uppercase;
  display: grid;
  gap: 2rem;
  padding: clamp(2rem, 5vw, 4rem);
  border: 2px solid var(--ink);
  box-shadow: 12px 12px 0 0 var(--ink);
}
.mag-headline { font-size: clamp(3rem, 9vw, 6rem); line-height: .88; font-weight: 700; margin: 0; }
.mag-stage { min-height: 240px; display: grid; place-items: center; border: 2px dashed var(--ink); }
.mag-btn {
  background: var(--hot);
  color: var(--paper);
  border: 2px solid var(--ink);
  box-shadow: 8px 8px 0 0 var(--ink);
  padding: 1.6rem 2.4rem;
  font-family: inherit;
  font-weight: 700;
  cursor: pointer;
  will-change: transform;
}