Nahorniak Templates
База референсів шаблонів і компонентів
Назад до шаблону · Ashley
c29

custom-magnetic-cursor

Курсор·Шаблон: Ashley·Складність анімації: heavy·Адаптивний: Так
custom-magnetic-cursor

Файли-джерела

  • home-1.htmldiv.mil-ball

Бібліотеки

gsapjquery

Summary

A single 20×20 black ball that follows the pointer with a 0.6s sine ease and morphs based on what's hovered: a 90×90 circle with embedded SVG arrow over .mil-drag zones, a More text label over .mil-more, a Choose label over .mil-choose, an accent-orange ball over .mil-accent-cursor, and a near-invisible state over plain links and inputs. Hidden below 1200px viewport.

HTML structure (minimal)

<div class="mil-ball">
  <span class="mil-icon-1">
    <svg viewBox="0 0 128 128">
      <path d="M106.1,41.9c-1.2-1.2-3.1-1.2-4.2,0c-1.2,1.2-1.2,3.1,0,4.2L116.8,61H11.2…" />
    </svg>
  </span>
  <div class="mil-more-text">More</div>
  <div class="mil-choose-text">Сhoose</div>
</div>

Key SCSS tokens

.mil-ball {
  width: 20px; height: 20px;
  position: fixed; z-index: 10;
  background-color: $dark; border-radius: 50%;
  pointer-events: none; opacity: .1;
  display: flex; justify-content: center; align-items: center;

  & .mil-icon-1 {
    position: absolute;
    width: 40px; height: 40px;
    transform: scale(0);
    & svg { fill: $lt-90; }
  }
  & .mil-more-text, & .mil-choose-text {
    position: absolute; width: 100%; text-align: center;
    color: $lt-90; font-size: 12px; font-weight: 500;
    text-transform: uppercase; letter-spacing: 2px;
    transform: scale(0);
  }
  &.mil-accent {
    & .mil-icon-1 svg { fill: $dark; }
    & .mil-more-text, & .mil-choose-text { color: $dark; }
  }
  @media screen and (max-width: 1200px) { display: none; }
}

Animation logic

const cursor = document.querySelector('.mil-ball');
gsap.set(cursor, { xPercent: -50, yPercent: -50 });

document.addEventListener('pointermove', (e) => {
  gsap.to(cursor, { duration: 0.6, ease: 'sine', x: e.clientX, y: e.clientY });
});

// growth on draggable / call-to-action zones
$('.mil-drag, .mil-more, .mil-choose').mouseover(() => {
  gsap.to(cursor, .2, { width: 90, height: 90, opacity: 1, ease: 'sine' });
});
$('.mil-drag, .mil-more, .mil-choose').mouseleave(() => {
  gsap.to(cursor, .2, { width: 20, height: 20, opacity: .1, ease: 'sine' });
});

// accent flip on `.mil-accent-cursor`
$('.mil-accent-cursor').mouseover(() => {
  gsap.to(cursor, .2, { background: accent, ease: 'sine' });
  $(cursor).addClass('mil-accent');
});

// per-target inner reveal
$('.mil-drag').mouseover(() => gsap.to('.mil-ball .mil-icon-1', .2, { scale: 1, ease: 'sine' }));
$('.mil-more').mouseover(() => gsap.to('.mil-ball .mil-more-text', .2, { scale: 1, ease: 'sine' }));
$('.mil-choose').mouseover(() => gsap.to('.mil-ball .mil-choose-text', .2, { scale: 1, ease: 'sine' }));

// shrink near regular links and inputs
$('a:not(".mil-choose , .mil-more , .mil-drag , .mil-accent-cursor"), input, textarea, .mil-accordion-menu')
  .mouseover(() => gsap.to(cursor, .2, { scale: 0, ease: 'sine' }));

// click feedback
$('body').mousedown(() => gsap.to(cursor, .2, { scale: .1, ease: 'sine' }));
$('body').mouseup(() => gsap.to(cursor, .2, { scale: 1, ease: 'sine' }));

Notable details

  • One DOM element handles five behaviours via class targets (.mil-drag → arrow, .mil-more → "More", .mil-choose → "Choose", .mil-accent-cursor → orange, generic link → hide).
  • The cursor lags the pointer at 0.6s — long enough to feel "physical" without becoming sluggish; the inner-icon transitions are 0.2s for snappy reads.
  • Color invert on dark sections is handled by parent mi-invert-fix plus mil-accent class — no per-section overrides on the cursor itself.
  • pointer-events: none keeps the cursor non-interactive — clicks always reach the underlying element.

Use when

  • Portfolios or agencies that need cursor states tied to content semantics ("drag me", "choose this", "view more") rather than just hover-on-link.
  • When you want one shared cursor across multiple page types without per-section bespoke logic.
  • Sites that already use GSAP elsewhere — this adds little weight on top.

Caveats

  • Hidden entirely below 1200px (mobile/tablet still see the system cursor) — design without relying on the cursor labels for affordance.
  • Heavy use of jQuery .mouseover delegation; on dynamically-loaded content (Swup transitions) the script re-binds through the swup:contentReplaced handler.
  • pointer-events: none ball is positioned via transform: translate3d — on Firefox, fast pointer movements can leave a brief trail. Acceptable for the target audience.