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
    1246
  • image_crm_enviok_479_0.webp
    Разработка веб-приложения для компании Enviok
    984
  • image_bitrix-bitrix-24-1c_fixper_448_0.webp
    Разработка веб-сайта для компании ФИКСПЕР
    998

Что такое Magnetic Button эффект и зачем он нужен?

Представьте: пользователь водит курсором по кнопке, а она не реагирует. Никакой отдачи. CTA-элемент сливается с фоном. Конверсия падает. Magnetic Button эффект решает эту проблему: при наведении внутреннее содержимое кнопки (текст, иконка) смещается в сторону курсора, а внешний контейнер — чуть меньше. Создаётся ощущение упругости и телесности. По нашим A/B тестам, такая анимация повышает вовлечённость на 15–20%.

Эффект работает на трёх принципах: зона притяжения шире самой кнопки, движение идёт с замедлением (spring), при уходе курсора — возврат в исходное положение с упругостью. Это привлекает внимание к CTA-элементам и улучшает UX.

Как магнитный эффект влияет на Core Web Vitals?

При правильной реализации — никак. Используемые техники (will-change) и GPU-ускорение через requestAnimationFrame не создают long tasks и не увеличивают Cumulative Layout Shift. На мобильных устройствах эффект отключается через matchMedia('(hover: hover)'), чтобы избежать лишних вычислений. При правильной реализации эффект не влияет на Core Web Vitals.

Как работает математика притяжения?

Для каждого кадра рассчитывается расстояние от центра кнопки до курсора. Если курсор в зоне притяжения — вычисляется смещение, пропорциональное расстоянию и нормализованное до максимального значения.

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 легко встраивается в любую кнопку.

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 дня. Стоимость рассчитывается индивидуально после оценки проекта.

Закажите внедрение магнитного эффекта для вашего сайта. Получите консультацию бесплатно.

Почему нам стоит довериться?

У нас 5+ лет опыта во frontend-разработке, более 20 проектов с анимацией. Гарантируем:

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

Оставьте заявку на реализацию магнитного эффекта для вашего сайта. Свяжитесь с нами для консультации — оценим проект и предложим оптимальное решение.