20

Переходы на свойстве display в CSS

9

Я разрабатываю меню "мега-дропдаун" с помощью CSS — фактически, это обычное выпадающее меню только на CSS, но содержащее различные типы контента.

На данный момент, выяснил, что переходы CSS3 не применяются к свойству 'display', т.е. невозможно сделать плавный переход от display: none к display: block (или любой другой комбинации).

Существует ли способ сделать так, чтобы подменю из приведенного выше примера "появлялось" при наведении на один из элементов верхнего уровня?

Я знаю, что можно использовать переходы со свойством visibility:, но не могу придумать, как это эффективно реализовать.

Я также пробовал использовать высоту, но это просто не сработало.

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

5 ответ(ов)

10

Чтобы добиться желаемого эффекта, вам нужно скрывать элемент другим способом. Я достиг этого эффекта, установив оба <div> абсолютно и задав скрытому элементу opacity: 0.

Если вы попытаетесь переключить свойство display с none на block, переходы для других элементов не сработают.

Чтобы обойти это ограничение, всегда позволяйте элементу оставаться со свойством display: block, но скрывайте его, используя один из следующих способов:

  1. Установите height в 0.
  2. Установите opacity в 0.
  3. Позиционируйте элемент за пределами видимости другого элемента с overflow: hidden.

Скорее всего, есть и другие решения, но вы не сможете выполнить анимацию, если переключите элемент на display: none. Например, вы можете попробовать что-то вроде этого:

div {
    display: none;
    transition: opacity 1s ease-out;
    opacity: 0;
}
div.active {
    opacity: 1;
    display: block;
}

Но это не сработает. Из моего опыта, это не приведет ни к чему.

Поэтому вам всегда нужно оставлять элемент с display: block — но вы можете обойти это, сделав что-то вроде этого:

div {
    transition: opacity 1s ease-out;
    opacity: 0;
    height: 0;
    overflow: hidden;
}
div.active {
    opacity: 1;
    height: auto;
}
1

Я подозреваю, что причина, по которой анимации отключены при изменении свойства display, заключается в том, что это свойство выполняет совершенно другую функцию. Оно не изменяет что-то, что можно было бы плавно анимировать.

display: none; и visibility: hidden; — это две совершенно разных вещи.

Обе инструкции делают элемент невидимым, но при использовании visibility: hidden; элемент всё равно отображается в раскладке, просто не видимо.

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

С другой стороны, display: none полностью предотвращает отображение элемента.

Он не занимает никакого места в раскладке.

Элементы, которые занимали бы часть или всё пространство, занимаемое этим элементом, теперь подстраиваются, как будто этот элемент просто не существует.

Свойство display — это не просто ещё один визуальный атрибут.

Оно задаёт весь режим отображения элемента, включая такие значения, как block, inline, inline-block, table, table-row, table-cell, list-item и так далее!

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

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

0

Я нашел более эффективный способ решения этой задачи. Вы можете использовать CSS-анимации, чтобы создать отличный эффект отображения элементов.

.item {
    display: none;
}

.item:hover {
    display: block;
    animation: fade_in_show 0.5s;
}

@keyframes fade_in_show {
    0% {
        opacity: 0;
        transform: scale(0);
    }

    100% {
        opacity: 1;
        transform: scale(1);
    }
}

Таким образом, при наведении на элемент он плавно появляется с анимацией. Это добавляет красивый визуальный эффект без необходимости использовать JavaScript.

0

Проблема, с которой вы столкнулись, связана с тем, как работает свойство display в CSS-анимациях. В вашем коде анимация hide корректно изменяет opacity, однако свойство display не может быть анимировано так, как вы того ожидаете.

Вот что происходит в вашем примере: в течение 99% времени анимации display устанавливается на block, а на 100% оно устанавливается на none. Но браузеры не применяют display: none в анимации, потому что это свойство не может быть интерполировано и не имеет промежуточных состояний.

Кроме того, важно помнить, что когда вы задаете animation-fill-mode: forwards, это означает, что после окончания анимации элемент сохранит стили последнего ключевого кадра. В данном случае, final state (display: none) не применяется, поскольку элемент в это время уже был отключен от потока документа (потому что display: none не сохраняет состояние после анимации).

Чтобы достичь нужного вам эффекта, попробуйте изменить подход. Например, вы можете комбинировать анимации opacity и динамически устанавливать display через JavaScript после завершения анимации. Вот пример:

@keyframes fade-out {
  0% {
    opacity: 1;
  }
  99% {
    opacity: 0;
  }
  100% {
    opacity: 0; /* Применяем opacity: 0, а display меняем через JS */
  }
}

.hide {
   animation: fade-out 1s linear forwards;
}

Затем в вашем JavaScript добавьте обработчик события, который устанавливает display: none после окончания анимации:

const element = document.querySelector('.hide');

element.addEventListener('animationend', () => {
    element.style.display = 'none'; // устанавливаем display: none
});

Таким образом, вы можете добиться желаемого эффекта скрытия элемента с плавным исчезанием.

0

Вот перевод вашего ответа в стиле StackOverflow:


Мой удобный трюк с JavaScript заключается в разделении всего сценария на две разные функции!

Для подготовки я объявляю одну глобальную переменную и определяю один обработчик события:

var tTimeout;
element.addEventListener("transitionend", afterTransition, true); // для Firefox
element.addEventListener("webkitTransitionEnd", afterTransition, true); // для Chrome

Затем, когда необходимо скрыть элемент, я использую что-то вроде этого:

function hide() {
  element.style.opacity = 0;
}

function afterTransition() {
  element.style.display = 'none';
}

Для того чтобы элемент снова появился, я делаю что-то похожее на это:

function show() {
  element.style.display = 'block';
  tTimeout = setTimeout(timeoutShow, 100);
}

function timeoutShow() {
  element.style.opacity = 1;
}

На данный момент всё работает прекрасно!

Чтобы ответить на вопрос, пожалуйста, войдите или зарегистрируйтесь