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

Пользователь проводит курсором по кнопке, а она остаётся статичной — CTA-элемент теряется на фоне, и конверсия страдает. Мы реализуем на вашем сайте эффект Magnetic Button: при наведении кнопка и её содержимое плавно тянутся за курсором, создавая живой wow-эффект. Наша команда внедряет анимацию под ключ с учётом производительности и отключением на мобильных устройствах, обеспечивая стабильную работу и последующую поддержку.

Разработка и обслуживание любых видов сайтов:

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

Это лишь некоторые из технических типов сайтов, с которыми мы работаем, и каждый из них может иметь свои специфические особенности и функциональность, а также быть адаптированным под конкретные потребности и цели клиента

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

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

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

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

Последние работы

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