В современных веб-приложениях навигация становится более интерактивной, включая поддержку жестов и свайпов для улучшения пользовательского опыта на мобильных устройствах. В React Router (начиная с версии 6) можно интегрировать обработку жестов и свайпов с помощью комбинации маршрутизации и сторонних библиотек для управления событиями касания.
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} гарантирует, что анимация будет
повторяться при каждом переходе.При реализации свайпов и жестов важно учитывать производительность:
React.lazy и Suspense.React.memo для компонент, которые не
должны перерисовываться при каждом свайпе.translateX,
translateY), так как они обрабатываются GPU.navigate(-1) и navigate(1) для навигации по
истории браузера при свайпах.Эта методика позволяет создавать мобильные веб-приложения с естественной навигацией и высокой отзывчивостью, сочетая возможности React Router с современными инструментами обработки жестов и анимаций.