Меню
Поиск

Интерфейс и визуал · Анимация и микровзаимодействия

Как связать движением состояния и не помешать тем, кому движение неприятно

Движение в интерфейсе связывает состояния: показывает, откуда появился элемент и куда исчез. В уроке разберём, как это делать, почему длительности и кривые берут из общего набора, что показывать вместо движения при системной настройке «уменьшить движение» и почему анимация не должна ждать ответа сервера и дёргаться на недорогом телефоне. Для практики — корзина и заказы магазина кофе, сравнение «вразнобой и по набору», переключатель «Уменьшить движение» и тренажёр «что при уменьшенном движении».

Пример для всего урока. То же приложение магазина кофе, что и в уроке Junior. Переходы в нём уже есть, но каждый собран отдельно: у шторки своя длительность, у диалога своя, у меню третья. Приведём их к одному набору, свяжем движением состояния и проверим, как приложение ведёт себя, когда движение выключено.
У вас включена настройка «уменьшить движение». Образцы в этом уроке всё равно двигаются, когда вы нажимаете «Проиграть»: без этого урок про движение не показать. Сами они не стартуют, и самый длинный идёт меньше секунды. В разделе 3 есть переключатель, который показывает те же образцы без движения.

1Движение показывает, откуда и куда

Когда новое состояние вырастает из старого, человек не теряет нить: видно, что откуда взялось и куда делось.

В корзине три товара, кофе из Кении удаляют.

Слева строка пропадает, и список прыгает вверх. Справа строка уезжает, а список смыкается следом.

Нажмите «Проиграть» под каждым экраном.

Удаление из корзины · прыжок и смыкание

Строка пропадает

←Корзина
Эфиопия, 250 г890 ₽
Кения, 250 г960 ₽
Колумбия, 250 г820 ₽
Итого1 710 ₽

Строка уезжает, список смыкается

←Корзина
Эфиопия, 250 г890 ₽
Кения, 250 г960 ₽
Колумбия, 250 г820 ₽
Итого1 710 ₽
Слева сумма и строка «Колумбия» оказываются на новом месте, и надо перечитать список, чтобы понять, что пропало. Справа видно, какая строка ушла и что остальные просто сдвинулись.

Смена экрана

Здесь то же самое. Когда подробности заказа раскрываются из его карточки, понятно, куда вернёт кнопка «Назад». Когда экран просто подменяется, связь между карточкой и подробностями человек восстанавливает сам.

Подробности заказа · подмена и раскрытие из карточки

Экран подменяется

Мои заказы
№ 10472 290 ₽
В пути
№ 10423 480 ₽
Доставлен
№ 10381 190 ₽
Отменён
←Заказ № 1042
Доставлен 14 сентября3 480 ₽
Lavazza Crema e Aroma, 1 кг
Кения АА, 250 г
Френч-пресс
Повторить заказ

Экран раскрывается из карточки

Мои заказы
№ 10472 290 ₽
В пути
№ 10423 480 ₽
Доставлен
№ 10381 190 ₽
Отменён
←Заказ № 1042
Доставлен 14 сентября3 480 ₽
Lavazza Crema e Aroma, 1 кг
Кения АА, 250 г
Френч-пресс
Повторить заказ
Справа экран растёт из второй карточки и в неё же возвращается по «Назад». Уход короче появления: 200 мс против 300.
Движение идёт от причины к следствию. Новое появляется из того места, где нажали, а уходит туда, где его можно найти снова. Если элемент исчез, соседи показывают это, занимая его место.

2Общий набор длительностей и кривых

Когда длительность подбирают для каждого перехода заново, приложение двигается вразнобой: шторка вяло, диалог резко, меню как-то ещё.

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

Каждый по отдельности выбран разумно, а вместе они дают пять длительностей и пять кривых, и ни одну нельзя повторить, не открыв исходник.

ПереходБылоСтало по набору
Шторка фильтров появляется270 мс, cubic-bezier(0.3, 0.1, 0.4, 0.9)средне, появление
Диалог «Удалить товар?» появляется180 мс, easeсредне, появление
Меню профиля появляется350 мс, linearкоротко, появление
Плашка «Добавлено» уходит400 мс, ease-outкоротко, уход
Строки корзины смыкаются220 мс, ease-in-outкоротко, перемещение

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

ИмяЗначениеДля чего
быстро100 мсПереключатель, флажок, число в счётчике
коротко200 мсМеню, подсказка, плашка; уход крупных элементов
средне300 мсПоявление шторки и диалога, раскрытие карточки
долго400 мсСмена экрана целиком
появлениеcubic-bezier(0, 0, 0, 1)Элемент приходит: быстрый старт, долгое торможение
уходcubic-bezier(0.3, 0, 1, 1)Элемент уходит насовсем: разгон к концу
перемещениеcubic-bezier(0.2, 0, 0, 1)Элемент меняет место на экране

Кривые взяты из Material Design 3, там они называются standard decelerate, standard accelerate и standard.

Готовый набор удобнее самодельного: он описан, и разработчикам он знаком.

В обновлении Material 3 Expressive компоненты Google перешли на пружинную анимацию и эту таблицу больше не развивают. Но как точка отсчёта для своего набора она по-прежнему годится.

До и после набора

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

Три перехода · вразнобой и по набору

Вразнобой

КаталогФильтры
фото
фото
Добавлено в корзину
Фильтры
Показать 24 товара

По набору

КаталогФильтры
фото
фото
Добавлено в корзину
Фильтры
Показать 24 товара
Сначала появляется плашка, через полсекунды — шторка. Слева плашка тянется, а шторка приходит с другим характером движения. Справа оба перехода двигаются одинаково, хотя длительности у них разные: так ощущается одно приложение.
Длительность и кривую не подбирают, а выбирают из набора. Если ни одно значение не подходит, это вопрос к набору, а не повод завести новое число.

3Настройка «уменьшить движение»

У части людей движение на экране вызывает головокружение и тошноту. Для них в системе есть настройка, и интерфейс должен её слушать.

В первую очередь она нужна людям с вестибулярными нарушениями.

Крупный сдвиг, приближение и параллакс вызывают у них головокружение, тошноту и головную боль — так эти реакции описывает WCAG.

Таких людей немало. По данным американской ассоциации вестибулярных расстройств VeDA, с нарушениями вестибулярной системы сталкивались до 35 % взрослых старше 40 лет в США.

Бывает и так, что движение отвлекает и мешает читать.

Настройка есть во всех основных системах:

СистемаГде включается
iOS и iPadOSНастройки → Универсальный доступ → Движение → Уменьшение движения
AndroidНастройки → Спец. возможности → Цвет и движение → Убрать анимации
macOSСистемные настройки → Универсальный доступ → Движение → Уменьшить движение. В macOS 15 и раньше — в разделе «Дисплей»
Windows 11Параметры → Специальные возможности → Визуальные эффекты → Эффекты анимации

Сайт узнаёт о настройке через медиазапрос prefers-reduced-motion, приложение — через системный флаг.

Решение, что показать вместо движения, принимает дизайнер.

Включите «Уменьшить движение» в коробке ниже и проиграйте те же переходы.

Шторка, плашка и счётчик · обычно и без движения
КаталогКорзина 23
фото
фото
Фильтры
ЗёрнаМолотыйКапсулы
Показать 24 товара
Добавлено в корзину
Без движения шторка и плашка проявляются на месте, а число в счётчике просто сменяется. Всё, что было понятно с движением, понятно и без него: шторка открылась, товар в корзине, число новое.

Что обычно делают с разными видами движения, когда настройка включена:

ДвижениеПри «уменьшить движение»
Шторка, диалог, меню выезжают или вырастаютПроявляются на месте, без сдвига и масштаба
Экран раскрывается из карточкиСтарый экран гаснет, новый проявляется
Фон едет медленнее текста при прокруткеФон стоит на месте
Баннеры листаются сами, видео крутится по кругуТолько по нажатию
Индикатор загрузки крутитсяОстаётся: он говорит, что работа идёт
Кнопка меняет цвет при нажатииОстаётся: смена цвета — не движение

Для разработки это одно условие в стилях. Вот как оно выглядит для шторки:

Шторка · с движением и без
/* Обычно: шторка выезжает снизу */
.sheet {
  transition: transform 300ms cubic-bezier(0, 0, 0, 1);
}
.sheet.is-closed {
  transform: translateY(100%);
}

/* Уменьшить движение: без сдвига, только проявление */
@media (prefers-reduced-motion: reduce) {
  .sheet {
    transition: opacity 200ms linear;
  }
  .sheet.is-closed {
    transform: none;
    opacity: 0;
  }
}
Дизайнеру не нужно писать этот код. Нужно решить, что будет вместо движения, и записать это рядом с анимацией.
«Уменьшить» не значит «убрать всё». Смысл перехода остаётся: шторка всё так же открывается, строка всё так же пропадает. Уходят сдвиг, масштаб и всё, что двигается само.

В WCAG 2.2 этому посвящён критерий 2.3.3 «Анимация от взаимодействия» уровня AAA.

А критерий 2.2.2 уровня A касается того, что начинает двигаться само. Если оно идёт дольше пяти секунд и стоит рядом с другим содержимым, его должно быть можно остановить.

4Медленная сеть: анимация не ждёт

Переход, который прекрасно выглядит на быстрой сети, на медленной прибавляет своё время к ожиданию.

Экран заказа в магазине открывается так: сначала проигрывается переход 400 мс, и только потом приложение просит данные у сервера.

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

Открытие заказа на медленной сети · шкала в миллисекундах
Сначала переход, потом запрос · данные через 1600 мс
переходзапросданные
Запрос вместе с переходом · данные через 1200 мс
запросданные
переходкаркас экрана
Во втором варианте запрос уходит в момент нажатия, и тот же переход идёт одновременно с ним. Пока данных нет, на экране каркас: заголовок, серые строки, кнопка. Данные приходят на 400 мс раньше, хотя ни сеть, ни переход не стали быстрее.
  • Запрос уходит в момент нажатия, а не после анимации.
  • Анимация не блокирует ввод. Пока шторка едет, по ней уже можно нажимать.
  • Анимация не прячет загрузку. Растянуть переход, «чтобы не было видно ожидания», значит заставить ждать и тех, у кого сеть быстрая.
  • Долгое ожидание — каркасом экрана, а не длинной анимацией: каркас сразу показывает, что будет на экране.
Анимация идёт вместе с работой, а не перед ней. Проверяйте переходы на медленной сети: в браузере для этого есть замедление сети в инструментах разработчика.

5Слабый телефон: проверка не в редакторе

В Figma и на ноутбуке любая анимация плавная. На недорогом телефоне та же анимация может дёргаться.

Плавным движение выглядит, когда экран обновляется 60 раз в секунду.

На каждый кадр у браузера остаётся около 16 мс, и за это время он должен успеть всё: посчитать, нарисовать, ответить на нажатие. Не успел — кадр пропадает, и глаз видит рывок.

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

Что меняетсяЦена для телефонаКак быть
Сдвиг, масштаб, поворотДёшево: браузер двигает уже нарисованную картинкуОсновной способ двигать
ПрозрачностьДёшевоПоявление и растворение
Ширина, высота, положение от краяДорого: пересчитывается раскладка страницыЗаменить сдвигом и масштабом
Тень, размытие, цвет фонаДорого: элемент перерисовывается на каждом кадреНе анимировать или проверять на слабом телефоне

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

Как проверить

  1. Откройте прототип на самом дешёвом телефоне, до которого можно дотянуться: свой старый, у разработки, в тестовом парке.
  2. В браузере Chrome включите замедление процессора: инструменты разработчика → Performance → настройки записи → CPU: 4x slowdown.
  3. Смотрите на две вещи: дёргается ли движение и успевает ли интерфейс ответить на нажатие, пока анимация идёт.
  4. Если дёргается, спросите разработку, какие свойства меняются. Обычно помогает заменить высоту или положение сдвигом.
Двигайте сдвигом, масштабом и прозрачностью. Остальное — только после проверки на слабом телефоне.

6Чем заменить движение

Для каждого движения в продукте нужен ответ на вопрос: что будет, если настройка «уменьшить движение» включена.

Тренажёр · что при «уменьшить движение»
  1. Шторка фильтров выезжает снизу за 300 мс.

  2. Баннеры на главной листаются сами каждые пять секунд.

  3. Фон кнопки «Оформить» темнеет при нажатии за 100 мс.

  4. Фон главного экрана едет медленнее текста при прокрутке.

  5. Экран заказа вырастает из карточки в списке за 300 мс.

  6. Число в счётчике корзины подпрыгивает, когда добавили товар.

  7. Индикатор загрузки крутится, пока приходит список заказов.

  8. Видео на обложке кофе проигрывается само по кругу.

«Проявление» — появление на месте за счёт прозрачности. «Убрать движение» — изменение происходит сразу. «Только по нажатию» — то, что двигалось само, ждёт человека.

7Задание

Заведите набор длительностей и кривых для продукта, пересоберите на нём три анимации и опишите поведение при «уменьшить движение».

Часть 1 · Собрать набор, 20 минут

  1. Выпишите все длительности и кривые, которые есть в продукте: из прототипов, из кода, у разработки.
  2. Сгруппируйте по размеру изменения: мелкое на месте, небольшой элемент, крупный элемент, смена экрана.
  3. Сведите к трём-пяти длительностям и трём кривым: появление, уход, перемещение. Можно взять кривые Material Design 3.
  4. Дайте имена по роли: «коротко», «появление», а не «200» и «кривая 2».

Часть 2 · Пересобрать три анимации, 40 минут

  1. Выберите три: одно появление, один уход и одно перемещение.
  2. Замените числа именами из набора. Если ничего не подходит, сначала решите, не лишний ли это переход.
  3. Проверьте направление: новое появляется оттуда, где нажали, и уходит туда, где его можно найти снова.

Часть 3 · Описать «уменьшить движение» и проверить, 30 минут

  1. Для каждой анимации решите, что будет вместо движения: оставить, проявление, убрать движение или только по нажатию.
  2. Включите настройку на своём телефоне и пройдите сценарий в прототипе или в продукте.
  3. Проверьте на медленной сети и на слабом телефоне: запрос не ждёт анимацию, рывков нет.
Набор и три анимации · заполните
ПРОДУКТ: ______________________        Дата: __.__.____

НАБОР
длительности: быстро ___ мс · коротко ___ мс · средне ___ мс · долго ___ мс
кривые: появление    cubic-bezier(__, __, __, __)
        уход         cubic-bezier(__, __, __, __)
        перемещение  cubic-bezier(__, __, __, __)

АНИМАЦИЯ 1: ______________________
было: ___ мс, кривая ______________   стало: ________ / ________
откуда → куда: ______________________
при «уменьшить движение»: оставить / проявление / убрать движение / только по нажатию
как выглядит без движения: ______________________

АНИМАЦИЯ 2: ______________________
было: ___ мс, кривая ______________   стало: ________ / ________
откуда → куда: ______________________
при «уменьшить движение»: ______________________
как выглядит без движения: ______________________

АНИМАЦИЯ 3: ______________________
было: ___ мс, кривая ______________   стало: ________ / ________
откуда → куда: ______________________
при «уменьшить движение»: ______________________
как выглядит без движения: ______________________

ПРОВЕРКА
настройка включена на телефоне, сценарий пройден: да / нет
медленная сеть: запрос уходит сразу, ввод не блокируется: да / нет
слабый телефон: рывков нет: да / нет
Имена в наборе — по роли. Число может поменяться, имя останется.
Как понять, что задание выполнено. При включённой системной настройке интерфейс остаётся полностью работающим и понятным без движения.

Что должно получиться

  • Набор длительностей и кривых с именами.
  • Три анимации на этом наборе: было и стало.
  • Описание поведения при «уменьшить движение» для каждой.
  • Результат проверки на телефоне с включённой настройкой, медленной сети и слабом устройстве.

План работы: 20 минут на набор, 40 на пересборку, 30 на «уменьшить движение» и проверку. Всего — около полутора часов.

Частые сложности

  • «У нас сложный продукт, четырёх длительностей мало». Проверьте, что делают лишние. Чаще всего это два почти одинаковых числа, между которыми разницу никто не видит.
  • «Разработка говорит, что учесть настройку сложно». На сайте это одно условие в стилях, в приложениях — системный флаг. Сложно становится, когда решение «что вместо движения» не принято, и его приходится придумывать в коде.
  • «Без движения непонятно, откуда взялась шторка». Проявление на месте и затемнение фона сохраняют связь. Если и так непонятно, дело не в движении, а в том, где шторка стоит.
  • «Нет слабого телефона». Замедление процессора в Chrome — не замена, но первые рывки покажет. Попросите у разработки или у тестировщиков самый старый телефон из тех, что поддерживает продукт.

8Чек-лист и ошибки

Движение в продукте связно, если

  • Новое появляется оттуда, где нажали, и уходит туда, где его можно найти снова
  • Длительности и кривые взяты из набора с именами по роли
  • Для каждой анимации решено, что будет при «уменьшить движение»
  • С включённой настройкой сценарий проходится и понятен
  • То, что двигается само дольше пяти секунд, можно остановить
  • Запрос не ждёт окончания анимации, ввод не блокируется
  • Движение сделано сдвигом, масштабом и прозрачностью
  • Анимация проверена на слабом телефоне

Что мешает

  • Число на каждый случай. Пять переходов — пять длительностей, и приложение двигается вразнобой.
  • Появление из ниоткуда. Экран подменяется, и связь с тем, что нажали, теряется.
  • «Уменьшить» значит «убрать всё». Шторка открывается без следа, и человек не понимает, что произошло.
  • Анимация перед запросом. На медленной сети к ожиданию прибавляется переход.
  • Проверка только в редакторе. На слабом телефоне анимация дёргается, а увидят это пользователи.

9Три ступени темы

В этом уроке учимся связывать движением состояния, работать с общим набором и с настройкой «уменьшить движение». На следующей ступени — описывать правила движения для всей команды.

СтупеньЧто нужно уметьКак проверить результат
Junior Отличать переход, который объясняет, от украшения; подбирать длительность под размер; выбирать кривую; не анимировать частое; описывать анимацию для разработки Для каждого перехода можно назвать, что он помогает понять, переходов «для красоты» не осталось
Middle — этот урок Показывать движением, откуда появилось и куда исчезло; брать длительности и кривые из общего набора; учитывать «уменьшить движение»; помнить про медленную сеть и слабые телефоны При включённой системной настройке интерфейс остаётся полностью работающим и понятным без движения
Senior Описать правила движения продукта: что анимируем и зачем, токены длительностей и кривых, что при «уменьшить движение», как проверяем производительность Спор об анимации в команде закрыт ссылкой на правило
Ориентир по этой теме: с выключенным движением ваш интерфейс так же понятен, а с включённым — двигается как одно приложение, а не как пять разных.

Что почитать дальше

  • Understanding Success Criterion 2.3.3: Animation from InteractionsW3C WAI · на английском · бесплатноКому мешает движение и чем оно опасно, почему смена цвета и прозрачности движением не считается и какую анимацию считать необходимой. Критерий уровня AAA.
  • Motion и AccessibilityApple Human Interface Guidelines · на английском · бесплатноЧто Apple советует при «уменьшить движение»: меньше автоматического движения, масштаба и приближения, пружины без отскока, растворение вместо сдвига — это абзац в разделе Cognitive страницы Accessibility. Страница Motion — про движение вообще: не анимировать частые действия и не заставлять ждать конца анимации.
  • prefers-reduced-motion: Sometimes less movement is moreweb.dev, Томас Штайнер, 2019 · на английском · бесплатноКак сайт узнаёт о настройке: медиазапрос prefers-reduced-motion в CSS и JavaScript, анимированная картинка с неподвижной заменой, демо с появлением при прокрутке. Статья для разработки 2019 года; «вибрирующая» кнопка регистрации в примере — как раз то движение, которое урок убирает.
  • How to create high-performance CSS animations и Why are some animations slow?web.dev · на английском · бесплатноКакими свойствами двигать — transform и opacity, — почему top и left тормозят и как проверить это в DevTools. Вторая статья объясняет, откуда 16 мс на кадр и что браузер успевает за это время. Справка для разработки 2020 года.
  • Easing and duration: tokens and specsMaterial Design 3 · на английском · бесплатноШестнадцать длительностей от 50 до 1000 мс и кривые появления, ухода и перемещения с именами токенов и значениями для CSS. Длительности здесь длиннее, чем в уроке: переключатель 200 мс, раскрытие на весь экран 500 мс; саму систему Google больше не развивает.