Css animations display none

How to animate an element in display none in two steps

Animate a hidden element is very simple now. We just need 2 CSS declaration and a bit of JavaScript to toggle the state open / close.

The solution

In this article:

  • CSS :not pseudo-selector
  • Single Source of truth
  • animationend event
  • Accessibility

Problem we try to solve

You have a hidden element with a hidden attribute. To provide a better UX, you want to animate this opening and closing state. But in CSS, display:none is like an interrupter; it can be «on» or «off» but animate between both state with a CSS transition is impossible.

Step 1: animate during opening

As you notice, I’m opting for a link instead of a button. Why ? Because my modal will still be accessible without JavaScript, thanks to the target CSS pseudo-class. I will focus on this point after.

Читайте также:  Несколько колонок на css

JS to show the modal

modalButton.addEventListener("click", function (e) < e.preventDefault(); modal.hidden = false; >); 

CSS

That’s it. ¯_(ツ)_/¯.
If you prefer relying on .hidden class (like in Tailwind), you can switch :not([hidden]) with :not(.hidden) . If you want both, the not pseudo-class accept multiple arguments separated by a comma : not([hidden], .hidden) . Anyway, our Modal appears with a shiny animation now :

Step 2 : animate during closing

The closing state is a little more tricky. If you set the hidden attribute to «true», you won’t be able to hide it smoothly. You need to add a temporary class like is-closing to play the closing animation and then, hide the element.

JS

modal.addEventListener("click", function (e) < // Omitted… if (hasClickedOutside || hasClickedCloseButton) < modal.classList.add("is-closing"); // Omitted… 

CSS


Now our modal is closing smoothly, but it is not back to hidden state. You have to wait to the end of the animation to remove the .is-closing class and back to hidden="true" . With setTimeout ? You could, but you have a better option.

Animationend event

With a timeout , we have to declare a value at least equal to the animation duration, which can change.
If you can, you have to have a single source of truth : here, the animation duration declared in the CSS.
The animationend will wait to the end of the animation, then execute the function inside the listener.

modal.addEventListener("click", function (e) < const hasClickedOutside = !e.target.closest(".modal-main"); const hasClickedCloseButton = e.target.closest(".modal-close"); if (hasClickedOutside || hasClickedCloseButton) < modal.classList.add("is-closing"); modal.addEventListener( "animationend", function () < modal.hidden = true; modal.classList.remove("is-closing"); >, < once: true >); > >); 

Once the event is completely done, you have to destroy it with the once: true option, as you don't need it anymore.
And voilà, you have the knowledge to animate any element hidden in the DOM.

Bonus : A little accessibility enhancement

Without JS, the modal can still be open via its hash, and you can style the opened state with the :target pseudo class.
To close it, the user needs to back in your history. This is why I hide the .modal-close . It's not pertinent to show it if it can't do anything.

Don't play animation if user don't want animation.

For personal taste, medical reason or to solve a performance issue on their device, your users may not want any animation, and you have to respect their preferences. It would be a good idea to embed the following the rule as part of your CSS reset, if it's not already done.

@media (prefers-reduced-motion: reduce) < *, *::before, *::after < animation-duration: 0.01ms !important; animation-iteration-count: 1 !important; transition-duration: 0.01ms !important; scroll-behavior: auto !important; >> 

Источник

Анимация перехода display из none в block

Есть некий объект со свойством display: none . Нужно анимировать его появление, при изменении параметра display , например, на block .

Пример

Невидимый прямоугольник делаем видимым при нажатии на кнопку:

document.addEventListener("DOMContentLoaded", () =>  const button = document.getElementById("button"); const rect = document.getElementById("rect"); button.addEventListener("click", () =>  rect.classList.toggle("is-visible"); >); >); 
#rect  width: 100px; height: 100px; background-color: red; display: none; > #rect.is-visible  display: block; > 

Проблема

Стандартный способ анимации через transition не работает. Но это и не удивительно:

#rect  width: 100px; height: 100px; background-color: red; transition: all 0.5s ease-in-out; display: none; > 

Но даже через введение параметра opacity ничего не работает:

#rect  width: 100px; height: 100px; background-color: red; transition: all 0.5s ease-in-out; display: none; opacity: 0; > #rect.is-visible  display: block; opacity: 1; > 

Решение

Будем использовать @keyframes :

#rect  width: 100px; height: 100px; background-color: red; display: none; opacity: 0; > #rect.is-visible  display: block; opacity: 1; animation: fadeInFromNone 3s ease-in-out; > @keyframes fadeInFromNone  0%  display: none; opacity: 0; > 1%  display: block; opacity: 0; > 100%  display: block; opacity: 1; > > 

Решение с обратной анимацией

Сейчас анимирован только переход из невидимого состояния в видимое. Сделаем так, чтобы работало и в обратную сторону. Как оказалось, это гораздо сложнее.

#rect  width: 100px; height: 100px; background-color: red; display: none; opacity: 0; > #rect.is-visible  display: block; animation: fadeInFromNone 0.5s ease-in-out; animation-fill-mode: forwards; > #rect.is-hidden  animation: fadeOutFromBlock 0.5s ease-in-out; > @keyframes fadeInFromNone  0%  opacity: 0; > 100%  opacity: 1; > > @keyframes fadeOutFromBlock  0%  opacity: 1; > 100%  opacity: 0; > > 
document.addEventListener("DOMContentLoaded", () =>  const button = document.getElementById("button"); const rect = document.getElementById("rect"); button.addEventListener("click", () =>  toggleTwoClasses(rect, "is-visible", "is-hidden", 500); >); >); function toggleTwoClasses(element, first, second, timeOfAnimation)  if (!element.classList.contains(first))  element.classList.add(first); element.classList.remove(second); > else  element.classList.add(second); window.setTimeout(function()  element.classList.remove(first); >, timeOfAnimation); > > 

Если добавим в HTML первый класс is visible , то вначале элемент будет видимым, а потом переключаться с видимого на невидимый:

 id="button">Show  id="rect" class="is-visible">

Источник

doctor Brain

Анимация элементов с display:none и callback функции

Учимся делать корректную анимацию появления элемента на странице

Photo by Sam Schooler on Unsplash

Когда появляется необходимость сделать анимацию для появляющегося элемента, обычно мы не задумываемся и прибегаем к помощи jQuery.

Почему мы так поступаем? Потому что это не требует никаких усилий. jQuery позаботился даже о том, чтобы у нас была возможность вызвать callback-функцию после анимации:

$('.foo').fadeIn(300, doSomthingAfterIt); 

Это очень быстро и эффективно. Но, к сожалению, появляется проблема: вся анимация, которую предлагает jQuery, в конечном счете является чистым JavaScript кодом, без малейшей примеси CSS.

Хороший frontend-разработчик должен знать, что переходы и анимации должны находиться в зоне ответственности CSS. Только так можно добиться лучшей производительности. Функции jQuery: .hide() , .show() , .toggle() , .fadeIn() , .slideUp() не рекомендуются к использованию.

CSS переходы (transition)

Использование универсального CSS-свойства transition является верным подходом. Все что нужно сделать - это добавить класс с нужным свойством элементу:

Тем не менее использование свойства transition связано с двумя проблемами:

Проблема 1

Как использовать callback-функции после transition?

Тут есть маленькая хитрость, а именно: событие transitionend (подробно спецификацию можно прочитать тут и тут:

$('.foo') .addClass('fade-in') .one(transitionend, doSomethingAfterIt); 

Теперь callback-функции доступны после любой анимации.

Проблема 2

Как сделать анимацию для элемента с исходным свойством display:none

Анимация для скрытого объекта - насущная проблема. Например, если мы добавим класс с нужными свойствами к такому элементу и используем transition, ничего не произойдет.

Такой код не будет работать:

/* исходное состояние элемента */ .foo < display: none; opacity: 0; transition: opacity 300ms; >/* желаемый результат */ .foo.fade-in

Это происходит потому, что элемент не отрисован и не занимает места на экране, а это является обязательным условием для его анимации. То есть transition для элемента с display:none не работает.

Метод Reflow

Для начала, нужно разобраться с терминами repaint и reflow. Если коротко:

  • repaint вызывается при изменениях, связанных с внешним видом элементов. Эти изменения не затрагивают размеры объекта и его положение на экране. Например, это могут быть CSS-свойства opacity , outline , background-color .
  • reflow вызывается при изменении размеров элемента. В свою очередь это приводит к перерасчету макета страницы и дальнейшему обновлению экрана уже с учетом изменений.

По этой причине, для того, чтобы добавить анимацию элементу с исходным свойством display:none , нужно форсировать reflow. Как это сделать? Добавим элементу класс со свойством display:block и вызовем метод, возвращающий размеры элемента. Для определения размеров браузер будет вынужден обновить элемент и форсировать запуск reflow. После запуска reflow элемент уже занимает место на экране и выполнение анимации с помощью transition становится возможным.

var $foo = $('.foo'); $foo .addClass('block') .outerWidth(); // Reflow $foo .addClass('fade-in') .one(transitionEnd, function() < alert('Animated'); >); 

Новые публикации

Photo by CHUTTERSNAP on Unsplash

JavaScript: сохраняем страницу в pdf

HTML: Полезные примеры

CSS: Ускоряем загрузку страницы

JavaScript: 5 странностей

JavaScript: конструктор сортировщиков

Категории

О нас

Frontend & Backend. Статьи, обзоры, заметки, код, уроки.

© 2021 dr.Brain .
мир глазами веб-разработчика

Источник

So, you’d like to animate the display property

The CSS Working Group gave that a thumbs-up a couple weeks ago. The super-duper conceptual proposal being that we can animate or transition from, say, display: block to display: none . It’s a bit of a brain-twister to reason about because setting display: none on an element cancels animations. And adding it restarts animations. Per the spec:

Setting the display property to none will terminate any running animation applied to the element and its descendants. If an element has a display of none, updating display to a value other than none will start all animations applied to the element by the animation-name property, as well as all animations applied to descendants with display other than none .

That circular behavior is what makes the concept seemingly dead on arrival. But if @keyframes supported any display value other than none , then there’s no way for none to cancel or restart things. That gives non- none values priority, allowing none to do its thing only after the animation or transition has completed. Miriam’s toot (this is what we’re really calling these, right?) explains how this might work: We’re not exactly interpolating between, say, block and none , but allowing block to stay intact until the time things stop moving and it’s safe to apply none . These are keywords, so there are no explicit values between the two. As such, this remains a discrete animation. We’re toggling between two values once that animation is complete. This is the Robert Flack’s example pulled straight from the discussion:

@keyframes slideaway < from < display: block; >to < transform: translateY(40px); opacity: 0;>> .hide

This is a helpful example because it shows how the first frame sets the element to display: block , which is given priority over the underlying display: none as a non- none value. That allows the animation to run and finish without none cancelling or resetting it in the process since it only resolves after the animation. This is the example Miriam referenced on Mastodon:

We’re dealing with a transition this time. The underlying display value is set to none before anything happens, so it’s completely out of the document flow. Now, if we were to transition this on hover, maybe like this:

…then the element should theoretically fade in at 200ms . Again, we’re toggling between display values, but block is given priority so the transition isn’t cancelled up front and is actually applied after opacity finishes its transition. At least that’s how my mind is reading into it. I’m glad there are super smart people thinking these things through because I imagine there’s a ton to sort out. Like, what happens if multiple animations are assigned to an element — will none reset or cancel any of those? I’m sure everything from infinite animations, reversed directions, and all sorts of other things will be addressed in time. But what a super cool first step!

Источник

Оцените статью