Я разрабатывал компонент настроек для своего приложения Angular и использовал тему на основе Bootstrap для приложения, поэтому, естественно, я взял большую часть своего макета из темы. В образце темы была приятная небольшая анимация для переключения между вкладками в интерфейсе с вкладками — вкладка, которую вы покидали, быстро исчезала, а на ее месте появлялась новая вкладка. Мне понравился эффект, но он не работал в моем скопированном макете, так как я использовал только SCSS из темы и ничего из JavaScript.

Прежде всего, мне нужно было выяснить, как перекрестное затухание реализовано в Bootstrap. Элементам панели вкладок назначен класс CSS fade, и этот класс определяет переход для свойства непрозрачности, а также устанавливает opacity в 0 для всех элементов, которые имеют класс fade и не имеют класса show. По умолчанию элементы панели вкладок имеют display: none; и скрыты, применение к ним класса active превращает свойство отображения в блок. Все выглядело просто и понятно, когда вы нажимаете на ссылку вкладки, непрозрачность анимируется, и значение отображения изменяется на обеих вкладках, и все выглядит красиво и плавно. Однако я заметил, что когда я щелкнул ссылку на вкладку, вкладка, от которой я удалялся, начала исчезать, но свойство display на новой вкладке не было установлено на block до тех пор, пока не завершилась анимация исчезновения. Очевидно, был задействован какой-то JavaScript. И я был прав — изучение реализации компонента вкладки в Bootstrap показало, что они запускают анимацию, а затем регистрируют обратный вызов, который считывает продолжительность анимации из задействованных элементов HTML и добавляет активный класс (который устанавливает display в block). Хорошо, значит, это не просто CSS-решение, нет проблем, в Angular есть очень обширная система анимации, я должен быть в состоянии воспроизвести это быстро, верно? Неправильный! Посмотрим, в чем проблема.

Когда вы начинаете исчезать в новой статье, для свойства display необходимо установить значение block, иначе вы вообще не увидите никакой анимации. И это вызывает еще одну проблему в типичной компоновке элементов вкладок — затухание в области вкладок может прыгать сверху (или появляться под нижней частью) отображаемой в данный момент панели вкладок, и все начинает прыгать.

Я создал простой триггер анимации и определил предположительно правильную анимацию:

Согласно Документации Angular по анимации CSS, функция sequence() должна запускать свои директивы по порядку, одну за другой. style() шагов применяются сразу, а animate() — с течением времени, как указано их временными данными. И необязательный параметр задержки должен задерживать всю последовательность на указанную величину. Моя идея была проста — мне нужно было отложить настройку display: block; на выбранной вкладке до того момента, пока предыдущая вкладка не закончит свою анимацию затухания, поэтому я добавил задержку 0,15 с к моему затуханию sequence(). К сожалению, это не имело никакого значения, и display был настроен на немедленную блокировку, что приводило к такому же скачкообразному поведению, как показано на гифке выше. Ничего не помогло, я пробовал разные способы задержки style({ display: ‘block’ }) шага, но все, что не запускалось, сразу заставляло Angular запускать всю анимацию на невидимом пока элементе и применять только display: block; после окончания анимации.

Итак, мне пришлось найти другой способ предотвратить прыжки элементов друг перед другом. Конечно, я мог бы пойти по пути Bootstrap и исправить некоторый код, чтобы установить интервал и запустить некоторый обратный вызов, чтобы установить свойство отображения всякий раз, когда мне это лучше всего подходит, но я не хотел этого делать, потому что, как я уже упоминал, Angular имеет очень всеобъемлющий система анимации, разве она не должна справиться с такой простой задачей?

Каким может быть другой способ сохранить исчезающий элемент в той же позиции, что и новый элемент, пытающийся заменить его? Ответ прост: CSS-позиционирование! Установка абсолютного положения элемента делает его позиционированным относительно его ближайшего позиционированного предка, и для элемента в макете не резервируется место, что идеально подходит для нашего случая. Вот фрагмент финальной версии анимации и GIF с результатом.