От автора: Вы, возможно, уже знаете, что переходы и анимация CSS3 позволяют анимировать специальный набор свойств CSS. Одно из тех свойств, которые нельзя анимировать – display.
Отлично было бы это сделать, но на данный момент это невозможно и, думаю, никогда не станет реальностью (например, как анимировать до “display: table”?). Но есть возможность совершить обходной маневр, и один из них я здесь и опишу.
Зачем анимировать “display”?
Анимировать свойство display требуется для решения следующей проблемы:
Нужно, чтобы элемент постепенно зрительно исчезал со страницы.
Вам нехочется, чтобы этот элемент занимал место после своего исчезновения (например, требуется, чтобы его исчезновение вызывало перезаливку).
Нужно применять для анимации CSS, а не библиотеку.
По этой причине недостаточно просто анимировать opacity до нуля, так как элемент с нулевой непрозрачностью занимает то же самое место на странице. Давайте посмотрим, как шаг за шагом решить эту проблему.
Используйте Opacity и Display
Первое, о чем вы можете подумать – воспользоваться и свойством opacity, и display. Наш HTML будет выглядеть так:
1 2 3 |
<div id="box" class="box"> </div> <button>TOGGLE VISIBILITY</button> |
А CSS – так:
1 2 3 4 5 6 7 8 9 10 11 12 |
.box { background: goldenrod; width: 300px; height: 300px; margin: 30px auto; transition: all 2s linear; display: block; } .hidden { display: none; opacity: 0; } |
Обратите внимание на то, что display: none и opacity: 0 присвоен класс “hidden”. Если переключать класс “hidden” с помощью jQuery, получится примерно такой код:
1 2 3 4 |
var box = $('#box'); $('button').on('click', function () { box.toggleClass('hidden'); }); |
Но если так сделать, то мы не увидим эффекта перехода (определенного в блоке описаний .box). Вместо него мы увидим это (слегка отличающееся в Firefox по сравнению с Chrome/IE10):
Многократно нажмите кнопку ‘togglevisibility’ и увидите, как блок безо всяких переходов будет неожиданно исчезать и появляться. Чтобы это исправить, можно попробовать отделить в CSS свойство display от opacity:
1 2 3 4 5 6 |
.hidden { display: none; } .visuallyhidden { opacity: 0; } |
Затем можно переключить оба класса, один за другим:
1 2 3 4 5 |
var box = $('#box'); $('button').on('click', function () { box.toggleClass('visuallyhidden'); box.toggleClass('hidden'); }); |
Помимо того, что требуется реверсировать эти две строки при появлении блока, это еще и не сработает, как видно здесь:
Нам нужно, чтобы элемент визуально исчезал, а затем удалялся со страницы по окончанию исчезновения идентично функции обратного вызова. (Кроме того, даже если мы скомбинируем opacity: 0 с visibility: hidden, то анимация получится хорошо, но элемент после своего исчезновения продолжит занимать место на странице, так что все это не сработает.)
Почему не работает?
До перехода к своему методу я просто объясню, почему невозможно ничего сделать, просто изменяя классы один за другим. Во-первых, если вы добавляете классы как в вышеприведенных примерах, и если переход даже сработал, то вам придется установить отдельный раздел для удаления классов и реверсирования (т.е. если блок сначала скрыт, вам придется установить его на display: block, а затем сменить непрозрачность).
Но это несущественно, потому что все равно не работает. JavaScript выполняет одну строку за другой, но не ждет, пока закончат выполняться все связанные с ней процессы, а переходит к выполнению следующей строки. (Другими словами, если в строке 1 вы выполняете анимацию или другое несинхронное событие, строка 2 будет исполняться, даже если анимация не выполнена).
Это происходит от того, что непрозрачность ‘opacity’ пытается анимироваться сразу же, и даже если на долю миллисекунды она действительно анимируется, вы этого не увидите, потому что display: none начнет действовать столь же быстро.
Можно резюмировать проблему, которую нам нужно решить, следующим образом:
Когда элемент виден, сначала анимируйте непрозрачность, а затем, когда это выполнено, сделайте его display: none.
Когда элемент невидим, сначала сделайте его display: block, затем (пока он еще визуально скрыт, но уже существует на странице), анимируйте непрозрачность.
Возможное решение
Вероятно, существуют другие способы это сделать, и я буду рад узнать, как вы бы решили эту проблему. Но вот мое мнение.
CSS тот же самый (с двумя все еще разделенными классами ‘hidden’), но jQuery будет выглядеть так:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
var box = $('#box'); $('button').on('click', function () { if (box.hasClass('hidden')) { box.removeClass('hidden'); setTimeout(function () { box.removeClass('visuallyhidden'); }, 20); } else { box.addClass('visuallyhidden'); box.one('transitionend', function(e) { box.addClass('hidden'); }); } }); |
А это он в действии:
Вот краткое изложение действий кода, когда блок видимый:
Добавьте класс visuallyhidden, который анимирует его до его исчезновения.
Одновременно с добавлением класса с помощью метода jQuery.one() добавляется один обработчик события, ожидающий события transitionend.
Событие transitionend запускается, когда непрозрачность анимирована, и когда это происходит, элемент устанавливается на display: block. Так как мы не можем отслеживать событие transitionend свойства display, то в ситуации, когда блок невидим, придется применить другой метод:
Сначала удаляем класс hidden, делая его display: block, пока блок визуально еще скрыт.
За это время скрипт выполнил задержку с помощью setTimeout(), после чего непрозрачность начинает анимироваться.
Примите во внимание, что некоторые браузеры требуют префикса для transitionend. Modernizr.prefixed() поможет вам в этом. Задержка очень маленькая (20 миллисекунд), но так как display: block происходит мгновенно, нам вообще не требуется большой задержки, лишь достаточной для того, чтобы позволить элементу полностью занять свое место на странице перед анимацией непрозрачности. (Замечание: внутри JSBinвChrome’е я смог применить задержку всего в одну сотую миллисекунды (0,1). Но в Firefox’е и IE10 потребовалось минимум 20мс. Может быть, это касается только JSBin, я на самом деле не знаю.)
А как бы сделали вы?
Как уже говорилось, возможно, существуют другие способы это сделать, или есть возможность внести улучшения в мой код.
Лично я считаю применение setTimeout неудовлетворительным и неизящным решением. Но ура! — оно работает, и я не считаю его способным вызывать какие-то проблемы, если только вы не собираетесь сделать на странице тонну одинаковой анимации. Но, в любом случае ,это уже другой вопрос.
На StackOverflow имеются несколько возможных решений этой проблемы, но я нахожу, что их большая часть, если не все, на самом деле не решают ее.
Автор: Louis Lazaris
Источник: //www.impressivewebs.com/
Редакция: Команда webformyself.
Комментарии (8)