Жесты и свайпы

В современных веб-приложениях навигация становится более интерактивной, включая поддержку жестов и свайпов для улучшения пользовательского опыта на мобильных устройствах. В React Router (начиная с версии 6) можно интегрировать обработку жестов и свайпов с помощью комбинации маршрутизации и сторонних библиотек для управления событиями касания.


Обработка свайпов с использованием React Router

React Router сам по себе не предоставляет встроенного механизма для распознавания свайпов. Для реализации свайпов обычно используют библиотеки вроде react-swipeable или hammerjs. Основная идея заключается в том, чтобы отслеживать событие свайпа и на его основе инициировать навигацию по маршрутам.

Пример с использованием react-swipeable:

import { useSwipeable } from 'react-swipeable';
import { useNavigate } from 'react-router-dom';

function SwipeNavigation() {
  const navigate = useNavigate();

  const handlers = useSwipeable({
    onSwipedLeft: () => navigate('/next'),
    onSwipedRight: () => navigate('/previous'),
    preventDefaultTouchmoveEvent: true,
    trackMouse: true
  });

  return (
    <div {...handlers} style={{ width: '100%', height: '100%' }}>
      <h2>Свайпните влево или вправо для навигации</h2>
    </div>
  );
}

Ключевые моменты:

  • useNavigate из React Router позволяет программно менять маршруты без перезагрузки страницы.
  • useSwipeable создаёт объект обработчиков для всех касаний и свайпов.
  • Свойство trackMouse: true позволяет тестировать свайпы и на десктопе, эмулируя их мышью.

Интеграция с динамическими маршрутами

Свайпы часто используют для перехода между страницами, которые динамически подгружаются, например, статьи блога или карточки продуктов. В этом случае важно учитывать параметры маршрута:

import { useParams, useNavigate } from 'react-router-dom';
import { useSwipeable } from 'react-swipeable';

function ArticleViewer({ articles }) {
  const { id } = useParams();
  const navigate = useNavigate();
  const currentIndex = articles.findIndex(article => article.id === parseInt(id));

  const handlers = useSwipeable({
    onSwipedLeft: () => {
      if (currentIndex < articles.length - 1) {
        navigate(`/articles/${articles[currentIndex + 1].id}`);
      }
    },
    onSwipedRight: () => {
      if (currentIndex > 0) {
        navigate(`/articles/${articles[currentIndex - 1].id}`);
      }
    }
  });

  return (
    <div {...handlers}>
      <h1>{articles[currentIndex].title}</h1>
      <p>{articles[currentIndex].content}</p>
    </div>
  );
}

Особенности реализации:

  • useParams позволяет извлекать текущий идентификатор статьи из маршрута.
  • Проверка индекса предотвращает выход за границы массива.
  • Навигация осуществляется без перезагрузки страницы, сохраняя плавность взаимодействия.

Состояния переходов и анимации

Для создания эффекта перелистывания важно сочетать свайпы с CSS-переходами или библиотеками анимации, такими как framer-motion. Пример простой анимации при переходе:

import { motion, AnimatePresence } from 'framer-motion';
import { Routes, Route, useLocation } from 'react-router-dom';

function AnimatedRoutes() {
  const location = useLocation();

  return (
    <AnimatePresence exitBeforeEnter>
      <Routes location={location} key={location.pathname}>
        <Route
          path="/page/:id"
          element={
            <motion.div
              initial={{ x: 300, opacity: 0 }}
              animate={{ x: 0, opacity: 1 }}
              exit={{ x: -300, opacity: 0 }}
              transition={{ duration: 0.3 }}
            >
              <PageContent />
            </motion.div>
          }
        />
      </Routes>
    </AnimatePresence>
  );
}

Пояснения:

  • AnimatePresence отслеживает смену компонентов при маршрутизации.
  • motion.div задаёт начальное и конечное состояние анимации.
  • key={location.pathname} гарантирует, что анимация будет повторяться при каждом переходе.

Оптимизация и производительность

При реализации свайпов и жестов важно учитывать производительность:

  1. Дебаунс и троттлинг событий свайпа для уменьшения нагрузки на рендер.
  2. Lazy loading компонентов страниц с помощью React.lazy и Suspense.
  3. Использование React.memo для компонент, которые не должны перерисовываться при каждом свайпе.
  4. Минимизация количества DOM-операций при анимации — лучше использовать CSS-трансформации (translateX, translateY), так как они обрабатываются GPU.

Расширенные сценарии

  • Вертикальные свайпы: можно отслеживать ось Y и переключать вертикальные страницы или секции.
  • Комбинация с навигационными панелями: свайпы можно интегрировать с боковыми меню для открытия/закрытия панелей.
  • История браузера: использование navigate(-1) и navigate(1) для навигации по истории браузера при свайпах.

Эта методика позволяет создавать мобильные веб-приложения с естественной навигацией и высокой отзывчивостью, сочетая возможности React Router с современными инструментами обработки жестов и анимаций.