Зависимая анимация по наведению: Тильда умеет, но по одной картинке
Эффект «наводишь на пункт списка — рядом меняется картинка» в Тильде есть: пошаговая анимация умеет событие hover и список элементов-триггеров. Начинать надо с неё. Упирается она в другом месте: каждая картинка включается сама по себе, общего состояния у них нет, и на списке из шести пунктов дизайнер выставляет руками шесть независимых анимаций, которые спокойно показываются все вместе.
Задача из тех, что приносят раз в месяц: список услуг, справа картинка, курсор на пункт — картинка меняется. В Тильде это делается настройками, и начинать надо с них. Но есть черта, за которой встроенный инструмент перестаёт справляться, — и проходит она не там, где ожидается.
Короткий ответ: Тильда это умеет
Пошаговая анимация Zero Block принимает событие hover и, главное, список
других элементов, наведение на которые её запускает. То есть «наводишь на пункт
— анимируется картинка рядом» — родная возможность, а не задача для скрипта:
справка Тильды. Предел один:
у картинок нет общего состояния, каждая включается сама по себе. На двух-трёх
это незаметно, на шести — уже нет.
Как это устроено внутри
Элемент, который анимируется пошагово, помечен в разметке атрибутами:
<div class="tn-elem"
data-animate-sbs-event="hover"
data-animate-sbs-opts="…"
data-animate-sbs-trgels="…">
Три вещи, которые из этого следует знать:
data-animate-sbs-eventпринимает иscroll, иhover. Именно здесь выбирается, чем анимация запускается.data-animate-sbs-trgels— тот самый список элементов-триггеров. Рантайм читает его при разборе анимации и собирает из него набор узлов, на которые вешает слушатели.- Содержимое элемента заворачивается в
.tn-atom__sbs-anim-wrapper. Это важнее, чем кажется: если вы соберётесь трогать такой элемент своим кодом, вы найдёте внутри лишний узел, которого не было в макете.
Любой атрибут с префиксом data-animate- означает, что элементом владеет
анимация Тильды. Ваш код на такой элемент лезть не должен вовсе — двух эффектов
на одном элементе посетитель не переживёт.
Почему на телефоне тоже срабатывает
Слушателей на элементы-триггеры вешается три: mouseenter, mouseleave и
click. Последний и есть ответ на вопрос «а как это работает на телефоне, там же
нет наведения»: тап приходит как клик, и анимация запускается.
Это хорошая новость и одновременно источник путаницы. На телефоне у эффекта нет
«ухода курсора»: mouseleave не наступит, потому что мыши нет. Картинка,
включённая тапом, остаётся включённой, пока не тапнут по другому пункту — а
поскольку общего состояния нет, второй тап включит вторую картинку, не выключив
первую.
Где встроенный инструмент упирается
Теперь по существу. У дизайнера список из шести пунктов и шесть картинок. Во
встроенном инструменте это шесть отдельных анимаций: шесть раз выбрать
элемент, шесть раз поставить событие hover, шесть раз указать список триггеров.
Дальше начинаются последствия:
| Что нужно | Во встроенном инструменте |
|---|---|
| Поменять длительность | шесть правок, по одной на анимацию |
| Добавить седьмой пункт | ещё одна анимация целиком |
| «Видна ровно одна картинка» | не выражается: состояния нет |
| «На старте видна первая» | не выражается: начального кадра нет |
| Переставить пункты местами | триггеры сбиваются, править руками |
Первые две строки — про труд, и с ними можно жить. Третья и четвёртая — про то, что средствами настройки не выражается вообще.
Что значит «нет начальной картинки»
Пошаговая анимация описывает переход, а не состояние. Она отвечает на вопрос «что сделать, когда навели», и не отвечает на вопрос «а что видно до этого».
Практически это выглядит так: страница открылась, курсора на списке ещё не было — и на месте картинки либо пусто, либо видны все шесть, наложенные друг на друга, смотря как дизайнер настроил их прозрачность в макете. Первое наведение приводит картинки в порядок, но первое впечатление уже испорчено.
И то же самое между наведениями: увели курсор быстро, тапнули по второму пункту, не убрав палец с первого, вернулись на страницу из другой вкладки — состояний, которых никто не описывал, оказывается больше, чем описанных.
Переключатель против набора анимаций
Разница в одном предложении: набор анимаций отвечает на события, переключатель держит состояние.
Из «держит состояние» следует всё остальное. Видна ровно одна картинка. На старте — та, что назначена. Переход между любыми двумя — один и тот же. Добавить пункт — значит добавить пару, а не анимацию. Порядок элементов в артборде не имеет значения, потому что связывает пару номер, а не соседство.
В моде это выглядит как разметка классами: пункт получает mk-item-1, его
картинка — mk-pic-1, следующая пара — mk-item-2 и mk-pic-2. Всё остальное
делает код: считает пары, показывает одну, прячет остальные и пересчитывает это
на каждом событии заново — между наведениями Тильда могла переставить узлы.
Прозрачность при этом пишется инлайном, а не правилом:
pr.atom.style.opacity = on ? String(pr.base) : '0';
Причина — не вкус: Zero Block держит прозрачность в правиле с id в селекторе, и правило по классу секции его не перебивает. Это отдельный разбор — почему ваш CSS проигрывает Zero Block.
Обратите внимание на pr.base: показываем до прозрачности, которую задал
дизайнер, а не до единицы. Если картинка в макете стоит на 80 %, она такой и
остаётся.
И одна подробность разметки, на которой спотыкаются все, кто связывает элементы
классами. Класс, вписанный в поле «CSS класс» элемента Zero Block, приезжает в
разметку с хвостом _elem: вы написали mk-pic-1, а в сохранённой странице
стоит mk-pic-1_elem. Поэтому селектор надо готовить к обеим формам сразу:
// Обе формы годятся, и дизайнеру не нужно знать, какую выберет Тильда.
return new RegExp('^' + safe + '-(\\d+)(?:_elem)?$');
Ищете вы своё же имя класса и не находите — проверьте это первым делом.
Спрятанная картинка обязана пропускать клики
Одно правило, без которого эффект выглядит работающим и при этом сломан:
/* Спрятанная картинка остаётся в раскладке на своём месте, поэтому она обязана
пропускать клики: иначе посетитель попадает по невидимой картинке вместо
видимой, и если это ссылка — уходит не туда. */
.mk-hover [data-mk-pic='out'] {
pointer-events: none;
}
Прозрачность не убирает элемент из раскладки. Шесть картинок лежат друг на друге,
пять из них невидимы — и клик достаётся верхней, то есть случайной. В Zero Block
картинка часто и есть ссылка (<a class="tn-atom">), так что цена ошибки —
посетитель уходит не на ту страницу.
Мышь эта строка закрывает целиком, а вот программу чтения экрана — нет:
pointer-events на дерево доступности не влияет, и спрятанной картинке нужен ещё
aria-hidden="true". Что при этом слышит незрячий посетитель и почему атрибут
снимается вместе с прозрачностью, а не отдельным проходом, — в разборе
доступности анимации.
С клавиатуры и без анимации
Два требования, которые к «наведению» не относятся, но без них эффект работает только для части посетителей.
Клавиатура. Текстовый элемент Zero Block сам по себе фокус не принимает —
значит, до пункта нельзя добраться табом, и переключатель для клавиатуры не
существует. Ссылка внутри пункта фокус принимает; если её нет, пункту нужен
tabindex="0". Слушателей поэтому три:
/* Слушатели — на секции, а не на пунктах: mouseover, focusin и touchstart
всплывают, поэтому делегирование работает, и замена внутренних узлов
обработчики не уносит. mouseenter не всплывает — он здесь не годится. */
section.addEventListener('mouseover', onPoint);
section.addEventListener('focusin', onPoint);
section.addEventListener('touchstart', onPoint, { passive: true });
focusin в этом списке — весь клавиатурный доступ. И заметьте, почему не
mouseenter, который для наведения кажется точнее: он не всплывает, до секции не
доходит, а вешать слушатели на сами пункты нельзя — Zero Block переставляет узлы.
Отключённая анимация. Если в системе включено «уменьшить движение», переключение должно быть мгновенным, а не быстрым: длительность обнуляется, а не делится на два. Картинка при этом всё равно меняется — отказ от анимации не отказ от эффекта.
Чего не должен делать любой такой эффект
Правило, из которого стоит проверять любую свою реализацию: прячем только то, что умеем вернуть.
Из него следуют три отказа, каждый из которых лучше молчаливой поломки:
- Картинка без пункта-триггера не прячется. Показать её потом нечем — значит, спрятать её один раз это убрать её со страницы навсегда.
- На одной паре эффект не включается. Переключать нечего, а прятать нельзя.
- Картинка, которой уже владеет анимация Тильды, не трогается. Два эффекта потянут её каждый в свою сторону, и это увидит посетитель.
И последнее, про направление поломки. Начальное состояние ставит код, а не правило
CSS, поэтому до первого прохода видны все картинки. Это выбрано осознанно: если
скрипт не отработает, посетитель увидит лишнее, а не пустое место. Правило CSS с
opacity: 0 при той же поломке оставило бы дырку в макете.
Что проверить у себя
- Сначала настройки. Пошаговая анимация, событие
hover, список элементов-триггеров. На двух-трёх картинках дальше идти незачем. - Посмотрите на страницу до наведения. Видно всё сразу или ничего — значит, начального состояния нет.
- Проверьте с телефона.
mouseleaveтам не наступит; тап включает и не выключает. - Тапните по двум пунктам подряд. Остались обе картинки — общего состояния нет.
- Кликните по месту, где лежит невидимая картинка. Ушли не туда — не хватает
pointer-events: none. - Посчитайте пункты. Больше четырёх — настройка руками начнёт стоить дороже готового переключателя.