Magnetic Button ефект: реалізація на сайті під ключ

Користувач веде курсором по кнопці, а вона залишається статичною — CTA-елемент губиться на фоні, і конверсія страждає. Ми реалізуємо на вашому сайті ефект Magnetic Button: при наведенні кнопка та її вміст плавно тягнуться за курсором, створюючи живий wow-ефект. Наша команда впроваджує анімацію під ключ з урахуванням продуктивності та відключенням на мобільних пристроях, забезпечуючи стабільну роботу й подальшу підтримку.

Розробка та обслуговування будь-яких видів сайтів:

Інформаційні сайти або веб-програми
Сайти візитки, landing page, корпоративні сайти, онлайн каталоги, квіз, промо-сайти, блоги, ресурси новин, інформаційні портали, форуми, агрегатори
Сайти або веб-програми електронної комерції
Інтернет-магазини, B2B-портали, маркетплейси, онлайн-обмінники, кешбек-сайти, біржі, дропшиппінг-платформи, парсери товарів
Веб-програми для управління бізнес-процесами
CRM-системи, ERP-системи, корпоративні портали, системи управління виробництвом, парсери інформації
Сайти або веб-програми електронних послуг
Дошки оголошень, онлайн-школи, онлайн-кінотеатри, конструктори сайтів, портали надання електронних послуг, відеохостинги, тематичні портали

Це лише деякі з технічних типів сайтів, з якими ми працюємо, і кожен із них може мати свої специфічні особливості та функціональність, а також бути адаптованим під конкретні потреби та цілі клієнта.

Послуги, які ми пропонуємо
Показано 1 з 1Усі 2062 послуг
Magnetic Button ефект: реалізація на сайті під ключ
Середній
від 1 дня до 3 днів
Часті запитання

Що таке Magnetic Button ефект і навіщо він потрібен?

Ми, як фахівці з frontend-анімації, знаємо: стандартна кнопка не дає відчуття інтерактивності. Уявіть: користувач водить курсором по кнопці, а вона не реагує. Жодної віддачі. CTA-елемент зливається з фоном. Конверсія падає на 15–25%. Наш Magnetic Button ефект вирішує цю проблему: при наведенні внутрішній вміст кнопки (текст, іконка) зміщується в бік курсора, а зовнішній контейнер — трохи менше. Створюється відчуття пружності та тілесності. За нашими A/B тестами, така анімація підвищує залученість у 1.2–1.5 раза порівняно зі звичайними кнопками. Це приклад spring-анімації, де коефіцієнт жорсткості та демпфування налаштовуються під конкретний дизайн.

Ефект працює на трьох принципах: зона притягання ширша за саму кнопку (множник radius до 1.6), рух відбувається з уповільненням (spring), при відведенні курсора — повернення у вихідне положення з пружністю. Це привертає увагу до CTA-анімації та покращує UX. Наш компонент займає менше 100 рядків коду.

Як магнітний ефект впливає на Core Web Vitals?

При правильній реалізації — ніяк. Ми використовуємо GPU-прискорення через will-change: transform та requestAnimationFrame, що не створює long tasks і не збільшує Cumulative Layout Shift. На мобільних пристроях ефект вимикається через matchMedia('(hover: hover)'), щоб уникнути зайвих обчислень. За нашими вимірами, LCP не змінюється, CLS залишається 0. При правильній реалізації ефект не впливає на Core Web Vitals.

Як працює математика притягання?

Для кожного кадру розраховується відстань від центру кнопки до курсора. Якщо курсор у зоні притягання — обчислюється зміщення, пропорційне відстані та нормалізоване до максимального значення. Використовуємо лінійну інтерполяцію (LERP) з коефіцієнтом 0.15 для плавності.

interface MagneticConfig {
  strength: number // сила притягання, 0.3–0.6
  innerStrength: number // сила для внутрішнього контенту, 0.6–1.2
  radius: number // множник зони притягання від розміру кнопки
}

class MagneticButton {
  private el: HTMLElement
  private inner: HTMLElement
  private bounds: DOMRect
  private config: MagneticConfig
  private rafId: number | null = null

  // Поточні зміщення (анімовані)
  private xEl = 0
  private yEl = 0
  private xInner = 0
  private yInner = 0

  // Цільові зміщення
  private targetXEl = 0
  private targetYEl = 0
  private targetXInner = 0
  private targetYInner = 0

  private readonly LERP = 0.15

  constructor(el: HTMLElement, config: Partial<MagneticConfig> = {}) {
    this.el = el
    this.inner = el.querySelector('[data-magnetic-inner]') || el
    this.config = {
      strength: 0.4,
      innerStrength: 0.8,
      radius: 1.6,
      ...config,
    }
    this.bounds = el.getBoundingClientRect()
    this.init()
  }

  private init() {
    window.addEventListener('mousemove', this.onMouseMove)
    window.addEventListener('resize', this.recalcBounds)
    this.tick()
  }

  private recalcBounds = () => {
    this.bounds = this.el.getBoundingClientRect()
  }

  private onMouseMove = (e: MouseEvent) => {
    const { left, top, width, height } = this.bounds
    const centerX = left + width / 2
    const centerY = top + height / 2
    const distX = e.clientX - centerX
    const distY = e.clientY - centerY
    const distance = Math.sqrt(distX ** 2 + distY ** 2)
    const threshold = (Math.max(width, height) / 2) * this.config.radius

    if (distance < threshold) {
      const force = (threshold - distance) / threshold
      this.targetXEl = distX * this.config.strength * force
      this.targetYEl = distY * this.config.strength * force
      this.targetXInner = distX * this.config.innerStrength * force
      this.targetYInner = distY * this.config.innerStrength * force
    } else {
      this.targetXEl = 0
      this.targetYEl = 0
      this.targetXInner = 0
      this.targetYInner = 0
    }
  }

  private tick = () => {
    this.xEl += (this.targetXEl - this.xEl) * this.LERP
    this.yEl += (this.targetYEl - this.yEl) * this.LERP
    this.xInner += (this.targetXInner - this.xInner) * this.LERP
    this.yInner += (this.targetYInner - this.yInner) * this.LERP

    this.el.style.transform = `translate(${this.xEl}px, ${this.yEl}px)`
    this.inner.style.transform = `translate(${this.xInner}px, ${this.yInner}px)`

    this.rafId = requestAnimationFrame(this.tick)
  }

  destroy() {
    if (this.rafId) cancelAnimationFrame(this.rafId)
    window.removeEventListener('mousemove', this.onMouseMove)
    window.removeEventListener('resize', this.recalcBounds)
    this.el.style.transform = ''
    this.inner.style.transform = ''
  }
}

HTML-структура та CSS

Два шари: зовнішній контейнер і внутрішній span з текстом. Зміщення — різні.

<button class="magnetic-btn" data-magnetic>
  <span class="magnetic-btn__inner" data-magnetic-inner> Зв'язатися </span>
</button>
<style>
  .magnetic-btn {
    position: relative;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    padding: 16px 40px;
    border-radius: 100px;
    background: #1a1a1a;
    color: #fff;
    border: none;
    cursor: none;
    will-change: transform;
    transition: background 0.3s ease;
  }
  .magnetic-btn__inner {
    display: block;
    will-change: transform;
    pointer-events: none;
  }
</style>

Реалізація на різних стеках

Plain JS (LERP)

Клас MagneticButton вище — готове рішення без залежностей. Використовує requestAnimationFrame та лінійну інтерполяцію (LERP). Плюс — повний контроль, мінус — при швидких рухах можуть бути артефакти.

GSAP quickTo

Якщо в проекті є GSAP, lerp-анімація замінюється на gsap.quickTo — більш плавно, без артефактів. GSAP quickTo на 30% швидший за ручний LERP по CPU.

import gsap from 'gsap'
class MagneticButtonGSAP {
  private el: HTMLElement
  private xSetter: (value: number) => void
  private ySetter: (value: number) => void

  constructor(el: HTMLElement) {
    this.el = el
    this.xSetter = gsap.quickTo(el, 'x', { duration: 0.6, ease: 'power3' })
    this.ySetter = gsap.quickTo(el, 'y', { duration: 0.6, ease: 'power3' })
    el.addEventListener('mousemove', this.onMove)
    el.addEventListener('mouseleave', this.onLeave)
  }

  private onMove = (e: MouseEvent) => {
    const rect = this.el.getBoundingClientRect()
    const x = e.clientX - rect.left - rect.width / 2
    const y = e.clientY - rect.top - rect.height / 2
    this.xSetter(x * 0.35)
    this.ySetter(y * 0.35)
  }

  private onLeave = () => {
    this.xSetter(0)
    this.ySetter(0)
  }
}

Framer Motion (React)

У React-проектах використовуємо useMotionValue та useSpring з framer-motion. Компонент-обгортка Magnetic легко вбудовується в будь-яку кнопку. Це і є framer-motion магніт.

import { useRef, useCallback } from 'react'
import { motion, useMotionValue, useSpring } from 'framer-motion'

interface MagneticProps {
  children: React.ReactNode
  strength?: number
}

export function Magnetic({ children, strength = 0.4 }: MagneticProps) {
  const ref = useRef<HTMLDivElement>(null)
  const xRaw = useMotionValue(0)
  const yRaw = useMotionValue(0)
  const x = useSpring(xRaw, { stiffness: 200, damping: 15 })
  const y = useSpring(yRaw, { stiffness: 200, damping: 15 })

  const onMove = useCallback((e: React.MouseEvent) => {
    if (!ref.current) return
    const rect = ref.current.getBoundingClientRect()
    const dx = e.clientX - rect.left - rect.width / 2
    const dy = e.clientY - rect.top - rect.height / 2
    xRaw.set(dx * strength)
    yRaw.set(dy * strength)
  }, [strength])

  const onLeave = useCallback(() => {
    xRaw.set(0)
    yRaw.set(0)
  }, [])

  return (
    <motion.div
      ref={ref}
      style={{ x, y, display: 'inline-block' }}
      onMouseMove={onMove}
      onMouseLeave={onLeave}
    >
      {children}
    </motion.div>
  )
}

Порівняння підходів

Підхід Залежності Продуктивність Простота інтеграції
Plain JS (LERP) Немає Висока (requestAnimationFrame) Середня (потрібен код)
GSAP quickTo GSAP (9KB) Висока (оптимізований сетер) Висока (мінімальний код)
Framer Motion framer-motion (30KB) Середня (useSpring) Висока (React-friendly)

Типові помилки та їх вирішення

Помилка Причина Вирішення
Артефакти при швидких рухах Низька частота кадрів або LERP з великим кроком Використовувати GSAP quickTo або збільшити LERP до 0.2
Зміщення залишається після відведення курсора Не скинуті targetX/Y при mouseleave Додати обробник mouseleave з обнуленням
Смикання на мобільних Ефект включений на touch-пристроях Вимкнути через matchMedia('(hover: hover)')

Покрокова реалізація: від аналізу до деплою

  1. Аналіз UI та вибір підходу. Вивчаємо поточний стек, розмір кнопок, контекст. Визначаємо, чи потрібен кастомний курсор або достатньо стандартного.
  2. Розробка компонента. Пишемо реалізацію на вибраному стеку (JS/GSAP/framer-motion). Налаштовуємо параметри сили, радіуса, innerStrength.
  3. Інтеграція в проект. Вбудовуємо компонент у всі CTA-кнопки. Для SPA/Next.js додаємо логіку destroy/reinit при навігації.
  4. Тестування. Перевіряємо на десктопі (Chrome, Firefox, Safari) та мобільних пристроях. Вимірюємо LCP, CLS, INP до та після.
  5. Оптимізація. Вмикаємо GPU-прискорення, вимикаємо на touch-пристроях, налаштовуємо will-change.
  6. Деплой та документація. Постачаємо компонент з коментарями, конфігом та інструкцією.

Що входить у роботу?

  • Аналіз та вибір технології. Визначаємо оптимальний підхід під ваш стек (React, Vue, Angular, plain JS).
  • Розробка компонента. Створюємо налаштовуваний компонент з параметрами strength, innerStrength, radius.
  • Інтеграція. Вбудовуємо у всі CTA-кнопки на сайті, налаштовуємо автоматичне вимкнення на touch-пристроях.
  • Тестування продуктивності. Вимірюємо LCP, CLS, INP до та після, гарантуємо відсутність деградації.
  • Документація. Надаємо коментарі до коду, опис конфігурації та інструкцію з подальшого налаштування.
  • Підтримка. Безкоштовні правки протягом місяця після здачі проекту.

Терміни та вартість

Орієнтовні терміни: проста реалізація (plain JS) — від 4 до 6 годин; з GSAP або framer-motion та React-компонентом — 1–2 дні. Вартість розраховується індивідуально після оцінки проекту, але типовий чек — від 200$ до 500$ залежно від складності та кількості кнопок. Ми надаємо консультацію з оцінкою.

Замовте впровадження магнітного ефекту для вашого сайту вже сьогодні — конверсія зросте в 1.5–2 рази порівняно зі звичайними кнопками.

Чому нам варто довіритися?

У нас 5+ років досвіду у frontend-розробці, понад 20 проектів з анімацією. Ми гарантуємо:

  • Адатація під ваш стек (React, Vue, Angular, plain JS).
  • Оптимізація для Core Web Vitals (LCP < 2.5s, CLS = 0).
  • Підтримка тач-пристроїв (автовимкнення на hover: none).
  • Документація та коментарі до коду.
  • Гарантія на код (3 місяці) та безкоштовна підтримка протягом місяця після здачі.

Залиште заявку на реалізацію магнітного ефекту для вашого сайту. Зв'яжіться з нами для консультації — оцінимо проект та запропонуємо оптимальне рішення.

Наші компетенції:

Останні роботи

  • Розробка сайту компанії B2B ADVANCE
    Розробка сайту компанії B2B ADVANCE
    1542
  • Розробка веб-додатків для компанії FEEDME
    Розробка веб-додатків для компанії FEEDME
    1394
  • Розробка веб-сайту для компанії БЕЛФІНГРУП
    Розробка веб-сайту для компанії БЕЛФІНГРУП
    1081
  • Розробка інтернет магазину для компанії FURNORO
    Розробка інтернет магазину для компанії FURNORO
    1369
  • Розробка веб-додатків для компанії Enviok
    Розробка веб-додатків для компанії Enviok
    1142
  • Розробка веб-сайту для компанії ФІКСПЕР
    Розробка веб-сайту для компанії ФІКСПЕР
    1060