Что разберём
1Движение показывает, откуда и куда
Когда новое состояние вырастает из старого, человек не теряет нить: видно, что откуда взялось и куда делось.
В корзине три товара, кофе из Кении удаляют.
Слева строка пропадает, и список прыгает вверх. Справа строка уезжает, а список смыкается следом.
Нажмите «Проиграть» под каждым экраном.
Строка пропадает
Строка уезжает, список смыкается
Смена экрана
Здесь то же самое. Когда подробности заказа раскрываются из его карточки, понятно, куда вернёт кнопка «Назад». Когда экран просто подменяется, связь между карточкой и подробностями человек восстанавливает сам.
Экран подменяется
Экран раскрывается из карточки
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 перешли на пружинную анимацию и эту таблицу больше не развивают. Но как точка отсчёта для своего набора она по-прежнему годится.
До и после набора
Ниже три перехода магазина — шторка, меню и плашка — слева с прежними числами, справа по набору. Нажмите «Проиграть» под каждым экраном.
Вразнобой
По набору
3Настройка «уменьшить движение»
У части людей движение на экране вызывает головокружение и тошноту. Для них в системе есть настройка, и интерфейс должен её слушать.
В первую очередь она нужна людям с вестибулярными нарушениями.
Крупный сдвиг, приближение и параллакс вызывают у них головокружение, тошноту и головную боль — так эти реакции описывает WCAG.
Таких людей немало. По данным американской ассоциации вестибулярных расстройств VeDA, с нарушениями вестибулярной системы сталкивались до 35 % взрослых старше 40 лет в США.
Бывает и так, что движение отвлекает и мешает читать.
Настройка есть во всех основных системах:
| Система | Где включается |
|---|---|
| iOS и iPadOS | Настройки → Универсальный доступ → Движение → Уменьшение движения |
| Android | Настройки → Спец. возможности → Цвет и движение → Убрать анимации |
| macOS | Системные настройки → Универсальный доступ → Движение → Уменьшить движение. В macOS 15 и раньше — в разделе «Дисплей» |
| Windows 11 | Параметры → Специальные возможности → Визуальные эффекты → Эффекты анимации |
Сайт узнаёт о настройке через медиазапрос prefers-reduced-motion, приложение — через системный флаг.
Решение, что показать вместо движения, принимает дизайнер.
Включите «Уменьшить движение» в коробке ниже и проиграйте те же переходы.
Что обычно делают с разными видами движения, когда настройка включена:
| Движение | При «уменьшить движение» |
|---|---|
| Шторка, диалог, меню выезжают или вырастают | Проявляются на месте, без сдвига и масштаба |
| Экран раскрывается из карточки | Старый экран гаснет, новый проявляется |
| Фон едет медленнее текста при прокрутке | Фон стоит на месте |
| Баннеры листаются сами, видео крутится по кругу | Только по нажатию |
| Индикатор загрузки крутится | Остаётся: он говорит, что работа идёт |
| Кнопка меняет цвет при нажатии | Остаётся: смена цвета — не движение |
Для разработки это одно условие в стилях. Вот как оно выглядит для шторки:
/* Обычно: шторка выезжает снизу */
.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 мс анимации.
- Запрос уходит в момент нажатия, а не после анимации.
- Анимация не блокирует ввод. Пока шторка едет, по ней уже можно нажимать.
- Анимация не прячет загрузку. Растянуть переход, «чтобы не было видно ожидания», значит заставить ждать и тех, у кого сеть быстрая.
- Долгое ожидание — каркасом экрана, а не длинной анимацией: каркас сразу показывает, что будет на экране.
5Слабый телефон: проверка не в редакторе
В Figma и на ноутбуке любая анимация плавная. На недорогом телефоне та же анимация может дёргаться.
Плавным движение выглядит, когда экран обновляется 60 раз в секунду.
На каждый кадр у браузера остаётся около 16 мс, и за это время он должен успеть всё: посчитать, нарисовать, ответить на нажатие. Не успел — кадр пропадает, и глаз видит рывок.
Одни свойства браузер меняет почти бесплатно, другие заставляют его пересчитывать страницу на каждом кадре:
| Что меняется | Цена для телефона | Как быть |
|---|---|---|
| Сдвиг, масштаб, поворот | Дёшево: браузер двигает уже нарисованную картинку | Основной способ двигать |
| Прозрачность | Дёшево | Появление и растворение |
| Ширина, высота, положение от края | Дорого: пересчитывается раскладка страницы | Заменить сдвигом и масштабом |
| Тень, размытие, цвет фона | Дорого: элемент перерисовывается на каждом кадре | Не анимировать или проверять на слабом телефоне |
Все образцы в этом уроке двигаются только сдвигом, масштабом и прозрачностью. Строки корзины из раздела 1 смыкаются не потому, что пересчитывается высота списка, а потому, что нижние строки сдвигаются вверх ровно на одну строку.
Как проверить
- Откройте прототип на самом дешёвом телефоне, до которого можно дотянуться: свой старый, у разработки, в тестовом парке.
- В браузере Chrome включите замедление процессора: инструменты разработчика → Performance → настройки записи → CPU: 4x slowdown.
- Смотрите на две вещи: дёргается ли движение и успевает ли интерфейс ответить на нажатие, пока анимация идёт.
- Если дёргается, спросите разработку, какие свойства меняются. Обычно помогает заменить высоту или положение сдвигом.
6Чем заменить движение
Для каждого движения в продукте нужен ответ на вопрос: что будет, если настройка «уменьшить движение» включена.
Шторка фильтров выезжает снизу за 300 мс.
ПроявлениеСдвиг уходит, а открытие шторки остаётся заметным: она проявляется на месте.
Баннеры на главной листаются сами каждые пять секунд.
Только по нажатиюДвижение без участия человека. Листать баннеры он будет сам, когда захочет.
Фон кнопки «Оформить» темнеет при нажатии за 100 мс.
ОставитьСмена цвета — не движение: ничего не едет и не растёт.
Фон главного экрана едет медленнее текста при прокрутке.
Убрать движениеWCAG приводит параллакс как пример движения, без которого обычно можно обойтись, а людям с вестибулярными нарушениями от него бывает плохо. Фон просто стоит.
Экран заказа вырастает из карточки в списке за 300 мс.
ПроявлениеМасштаб уходит, остаётся смена экранов растворением: понятно, что открылось новое.
Число в счётчике корзины подпрыгивает, когда добавили товар.
Убрать движениеЧисло сменяется сразу. Что товар добавлен, говорит ещё и плашка.
Индикатор загрузки крутится, пока приходит список заказов.
ОставитьИндикатор маленький и сообщает, что работа идёт. Без него человек решит, что приложение зависло.
Видео на обложке кофе проигрывается само по кругу.
Только по нажатиюВместо видео — кадр из него и кнопка «Смотреть».
7Задание
Заведите набор длительностей и кривых для продукта, пересоберите на нём три анимации и опишите поведение при «уменьшить движение».
Часть 1 · Собрать набор, 20 минут
- Выпишите все длительности и кривые, которые есть в продукте: из прототипов, из кода, у разработки.
- Сгруппируйте по размеру изменения: мелкое на месте, небольшой элемент, крупный элемент, смена экрана.
- Сведите к трём-пяти длительностям и трём кривым: появление, уход, перемещение. Можно взять кривые Material Design 3.
- Дайте имена по роли: «коротко», «появление», а не «200» и «кривая 2».
Часть 2 · Пересобрать три анимации, 40 минут
- Выберите три: одно появление, один уход и одно перемещение.
- Замените числа именами из набора. Если ничего не подходит, сначала решите, не лишний ли это переход.
- Проверьте направление: новое появляется оттуда, где нажали, и уходит туда, где его можно найти снова.
Часть 3 · Описать «уменьшить движение» и проверить, 30 минут
- Для каждой анимации решите, что будет вместо движения: оставить, проявление, убрать движение или только по нажатию.
- Включите настройку на своём телефоне и пройдите сценарий в прототипе или в продукте.
- Проверьте на медленной сети и на слабом телефоне: запрос не ждёт анимацию, рывков нет.
ПРОДУКТ: ______________________ Дата: __.__.____
НАБОР
длительности: быстро ___ мс · коротко ___ мс · средне ___ мс · долго ___ мс
кривые: появление 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 InteractionsКому мешает движение и чем оно опасно, почему смена цвета и прозрачности движением не считается и какую анимацию считать необходимой. Критерий уровня AAA.
- Motion и AccessibilityЧто Apple советует при «уменьшить движение»: меньше автоматического движения, масштаба и приближения, пружины без отскока, растворение вместо сдвига — это абзац в разделе Cognitive страницы Accessibility. Страница Motion — про движение вообще: не анимировать частые действия и не заставлять ждать конца анимации.
- prefers-reduced-motion: Sometimes less movement is moreКак сайт узнаёт о настройке: медиазапрос prefers-reduced-motion в CSS и JavaScript, анимированная картинка с неподвижной заменой, демо с появлением при прокрутке. Статья для разработки 2019 года; «вибрирующая» кнопка регистрации в примере — как раз то движение, которое урок убирает.
- How to create high-performance CSS animations и Why are some animations slow?Какими свойствами двигать — transform и opacity, — почему top и left тормозят и как проверить это в DevTools. Вторая статья объясняет, откуда 16 мс на кадр и что браузер успевает за это время. Справка для разработки 2020 года.
- Easing and duration: tokens and specsШестнадцать длительностей от 50 до 1000 мс и кривые появления, ухода и перемещения с именами токенов и значениями для CSS. Длительности здесь длиннее, чем в уроке: переключатель 200 мс, раскрытие на весь экран 500 мс; саму систему Google больше не развивает.