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

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

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

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

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

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

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

Часті запитання

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

  • image_website-b2b-advance_0.webp
    Розробка сайту компанії B2B ADVANCE
    1419
  • image_web-applications_feedme_466_0.webp
    Розробка веб-додатків для компанії FEEDME
    1287
  • image_websites_belfingroup_462_0.webp
    Розробка веб-сайту для компанії БЕЛФІНГРУП
    983
  • image_ecommerce_furnoro_435_0.webp
    Розробка інтернет магазину для компанії FURNORO
    1248
  • image_crm_enviok_479_0.webp
    Розробка веб-додатків для компанії Enviok
    984
  • image_bitrix-bitrix-24-1c_fixper_448_0.webp
    Розробка веб-сайту для компанії ФІКСПЕР
    998

Що таке 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 місяці) та безкоштовна підтримка протягом місяця після здачі.

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