Анімуючи небуття
Ми усі звикли до анімацій в CSS. Зокрема я зараз говоритиму про transition: воно змінює значення від початкового до кінцевого в залежності від часової функції на заданому часовому проміжку.
І тут важливо згадати про дві умови: анімація не може відбутися, якщо немає так званого before-change style — попереднього відрендереного стану, від якого анімація може стартувати, а також якщо значення, яке ми намагаємося "оживити", не може мати проміжних станів, тобто є дискретним.
В CSS обидві умови можна зламати, просто використавши
display: none. Тривалий час задача "показати/сховати елемент і додати анімацію появи/зникнення" покладалась на JavaScript та його ненадійні таймери. Згодом зʼявилася дещо надійніші події на кшталт transitionend, але вони вирішували лише частину проблеми.Але сьогодні ця задача більше не є проблемою. CSS надає нам дуже цікаві інструменти, що дозволяють дещо більше, ніж просто "анімувати `display:none`".
Мова про
@starting-style та allow-discrete.Коли елемент переходить від
display: none до "видимого" значення, за замовчуванням усі кінцеві значення властивостей застосовуються одразу, навіть попри transition-duration. Чому? Бо у елемента немає *початкового стану*, на основі якого можна розрахувати перий кадр анімації. Чому? Бо він на момент початку переходу просто ніколи не був *відмальований*. Це якщо ми говоримо про видиму зміну стану.Примітка
Елемент може не мати відмальованого стану з кількох причин. Наприклад,display: noneозначає, що елемент взагалі не бере участі в рендерингу, а отvisibility: hiddenзначить, що елемент невидимий, але займає місце в макеті.
Так от, тут в нагоді нам стає
@starting-style. Це @-правило дозволяє "допомогти" бравзеру створити той самий перший кадр, від якого він уже буде спроможний розрахувати анімацію. Тобто сама суть не в тому, що бравзер не може інтерполювати певні значення, а в тому, що він просто не розуміє, як має виглядати перший кадр.
element {
transition: 1s;
display: none;
&.visible {
display: block;
opacity: 1;
@starting-style {
& { opacity: 0; }
}
}
}
Таким чином можна додати анімацію не лише до
display: none -> block, а й при вставці нового елементу в DOM. Тож, якщо коротко: @starting-style це шпаргалка, яку ми надаємо бравзеру, аби він зміг зрозуміти, як намалювати перший кадр анімації для елементу, у якого не було відмальованого (rendered) стану до того.Наступна проблема — анімація від видимого стану до "невідмальованого", і полягає вона в абсолютно іншому механізмі: оскільки дискретні значення анімувати неможливо, бравзер за замовчуванням просто "перемикає" їх на *початку переходу*. І саме тому намагаючись "анімувати" елемент в небуття від
display: block до display: none ми не бачимо переходу, а лише миттєве зникнення.Це можна було б вирішити, якби можна було "відкласти" перемикання на потрібний нам час замість миттєвої зміни. І от якраз це раніше можна було вирішити виключно за допомогою JavaScript: або ставити таймери, або слухати події завершення анімації, і вже тоді "клацати перемикач" вручну.
І, нарешті, цей механізм є і в CSS. Це значення
allow-discrete для властивості transition-behavior. Він робить рівно те, що необхідно: дозволяє відкласти перемикання дискретної властивості на потрібний час.
element {
transition:
opacity 1s,
display 1s allow-discrete;
display: none;
opacity: 0;
}
element.visible {
display: block;
opacity: 1;
}
Якщо ми приберемо
.visible з елементу, то спочатку побачимо, як той поступово "зникає", і лише в кінці, коли opacity досягне нуля, display набуде значення none.Якщо коротко, то
allow-discrete дозволяє визначити для перемикання значення дискретної властивості певну точку на часовій шкалі анімації, але з купою виключень, звісно. Які я вам пропоную дослідити самим.MDN: @starting-style
MDN: allow-discrete
***
@babichdev