~/wiki / motion-i-animatsiya / perekhody-mezhdu-ekranami-patterny

Переходы которые не тошнят: паттерны смены экранов для мобайла и веба

Основной чат

Чат для вайбкодеров: новости, гайды, поиск исполнителей, маркетплейс и разбор реальных кейсов.

$ cd раздел/ $ join vibe dev
Переходы которые не тошнят: паттерны смены экранов для мобайла и веба - обложка

Переход между экранами — самый часто встречающийся motion-паттерн и самый часто ломающий UX. Плохой переход дезориентирует: пользователь не понимает где он был, где он теперь, что произошло.

Хороший переход невидим. Ты замечаешь только что стало лучше понятно.


Почему переходы важны для ориентации

Без анимации переход — телепортация. Пользователь был на списке, нажал, оказался на детальной странице. Мозг не успел построить пространственную связь между двумя состояниями.

С правильной анимацией — движение. Пользователь видит что «пошёл вглубь» (slide left), что «вернулся назад» (slide right), что «что-то всплыло поверх» (slide up). Ментальная карта продукта строится через эти переходы.

Неправильный переход ломает ментальную карту. Slide влево когда ожидался вправо. Fade там где нужен scale. Пользователь чувствует «что-то не так» — даже если не может назвать причину.


Семь паттернов с логикой выбора

Fade (crossfade)

Текущий экран растворяется, новый появляется. Самый нейтральный — не передаёт направление.

Когда: экраны не иерархически связаны (смена разделов через боковое меню), модальные диалоги, смена вкладок таба.

Не когда: drill-down навигация где нужно показать «я пошёл глубже».

css
@keyframes fadeIn {
  from { opacity: 0; }
  to   { opacity: 1; }
}
.page-enter { animation: fadeIn 0.25s ease-out; }

Slide horizontal

Следующий экран приходит справа, текущий уходит влево. Назад — обратное.

Когда: drill-down навигация (список → деталь), iOS navigation stack, любая иерархическая структура.

Почему работает: имитирует физическое перелистывание. Пользователь понимает что «пошёл вглубь» и знает в какую сторону возвращаться.

css
.page-enter  {
  transform: translateX(100%);
  animation: slideIn 0.3s ease-out forwards;
}
.page-exit {
  animation: slideOut 0.3s ease-in forwards;
}
@keyframes slideIn  { to { transform: translateX(0); } }
@keyframes slideOut { to { transform: translateX(-30%); opacity: 0.3; } }

Slide vertical (bottom sheet)

Появление снизу — стандарт для modal bottom sheets на мобайле. Исчезновение вниз при закрытии.

Когда: bottom sheets, action sheets, drawer-панели на мобайле. Создаёт чёткое разделение между «основным контентом» и «временным контентом».

Scale (zoom)

Элемент увеличивается до нового экрана. Самый сильный способ показать что новый экран «вышел» из конкретного элемента.

Когда: нажатие на карточку открывает детальный экран, thumbnail → full screen, аватар → профиль пользователя.

Реализация: transform: scale(0.9)transform: scale(1) с transform-origin в точке нажатия.

Shared Element Transition (morph)

Элемент на одном экране плавно трансформируется в элемент на другом. Карточка в списке становится хедером детального экрана.

Когда: список → деталь, thumbnail → полноэкранное фото.

В CSS: View Transitions API. В React: layoutId в Framer Motion.

javascript
// Framer Motion — один layoutId на обоих экранах
// На экране списка:
<motion.div layoutId={`card-${item.id}`}>
// На экране детали:
<motion.div layoutId={`card-${selectedId}`}>

Reveal

Новый экран появляется из-под старого. Для sidebar-навигации: контент «сдвигается» открывая панель.

Cross-fade с blur

Текущий экран размывается и исчезает, новый появляется чётким. Создаёт ощущение «другого уровня» — более глубокого погружения.


Правило консистентности

Один продукт — одна система переходов. Mixing slide и scale в зависимости от настроения ломает ментальную модель.

Минимальная система:

  • Drill-down (вглубь) → slide left
  • Назад → slide right
  • Modal / overlay → fade или slide up
  • Смена разделов → fade

Запиши это в DESIGN.md и дай AI в контекст — иначе каждый экран будет с другим переходом.


View Transitions API — переходы без JavaScript

Для многостраничных сайтов (MPA) — автоматические переходы без роутер-библиотек:

css
/* Включить для всего сайта */
@view-transition {
  navigation: auto;
}

/* Кастомизация */
::view-transition-old(root) {
  animation: slide-out 0.3s ease-in;
}
::view-transition-new(root) {
  animation: slide-in 0.3s ease-out;
}

@keyframes slide-out { to { transform: translateX(-100%); } }
@keyframes slide-in  { from { transform: translateX(100%); } }

Поддержка: Chrome, Edge, Safari 18+. Для Firefox — пока полифилл.


Промпт для Codex / Claude Code

markdown
Добавь систему переходов между страницами в Next.js проект.

Правила:
- Навигация вглубь (список → деталь): slide left
- Навигация назад: slide right
- Модальные окна: fade + scale(0.95→1)
- Смена разделов через nav: fade

Реализация: View Transitions API для нативных переходов.
Fallback для неподдерживаемых браузеров — мгновенный переход без анимации.
Duration: 300ms, easing: cubic-bezier(0.16, 1, 0.3, 1).
prefers-reduced-motion: мгновенные переходы.

Не использовать Framer Motion только для переходов — View Transitions API достаточно.

Почему переходы между экранами — самое сложное в motion

Hover-анимация затрагивает один элемент. Переход между экранами затрагивает всё — старый экран исчезает, новый появляется, иногда отдельные элементы «переезжают» между ними. Ошибка здесь видна сразу и на весь экран.

Три самые частые ошибки:

Слишком длинный transition. 600мс для перехода между страницами — это почти секунда ожидания при каждом клике. На сайте с 20 страницами пользователь потратит минуты только на анимации.

Неправильное направление. Пользователь нажимает кнопку «назад» — экран уходит вправо (правильно). Нажимает ссылку вглубь — экран тоже уходит вправо (неправильно, должен влево). Ментальная модель нарушается.

Transition прерывается. Пользователь нажимает быстро, следующий переход начинается до завершения предыдущего. Если не обработано правильно — визуальный мусор.


Shared Element Transition: когда делать, когда не делать

Shared Element Transition (карточка превращается в детальный экран) — один из самых впечатляющих паттернов. Но у него есть цена.

Работает хорошо когда: один элемент «трансформируется» в другой, пользователь видит визуальную связь, экраны физически близки по контенту (список товаров → страница товара).

Не работает когда: элементов много и непонятно какой «трансформируется», контент экранов сильно отличается, переход работает медленно на слабых устройствах.

Техническая реализация через View Transitions API или Framer Motion layoutId — относительно простая. Дизайнерская реализация — сложная: нужно точно спроектировать начальное и конечное состояние обоих экранов чтобы переход выглядел естественно.


Мобайл vs десктоп: разные паттерны

На мобайле пользователь взаимодействует пальцем и ожидает физическое ощущение. Slide transition который следует за свайпом — естественно. Fade который появляется независимо от жеста — диссонанс.

На десктопе мышь не оставляет «следа движения». Fade или subtle slide — нормально. Агрессивный slide как на мобайле — обычно избыточно.

При адаптивном дизайне: определи разные motion-системы для мобайла и десктопа. @media (pointer: coarse) для устройств с тач — более физические переходы. @media (pointer: fine) для мыши — более сдержанные.


Когда переход вообще не нужен

Не каждая навигация требует анимированного перехода. Три случая когда переход лучше убрать:

Быстрые повторяющиеся навигации. Пользователь листает список нажимая «назад→вперёд» быстро. Каждый переход 300мс — за 10 нажатий он потратил 3 секунды только на анимации. Решение: после третьего быстрого нажатия за секунду — отключать переход.

Тяжёлый контент. Страница с большим количеством изображений и данных — переход начинается до того как новая страница загружена. Пользователь видит пустой экран с анимацией. Решение: показывать переход только после того как базовый контент загружен.

Действия в той же области. Фильтр изменился — карточки перестроились. Нужен ли переход на уровне страницы? Скорее всего нет — достаточно анимировать сами карточки.

plaintext
☐ Определена система: какой паттерн для какой навигации
☐ Drill-down: slide направление совпадает с Back
☐ Нет mixing разных паттернов без логики
☐ Система записана в DESIGN.md
☐ View Transitions API или Framer Motion AnimatePresence реализованы
☐ prefers-reduced-motion: мгновенные переходы
☐ Duration 250–350мс (не 500мс+ для базовых переходов)
$ cd ../ ← назад к Motion и анимация