Я разрабатывал компонент настроек для своего приложения 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 с результатом.