- How To Do CSS Transitions With Height: Auto
- Method 1: Use transform
- Method 2: Animate max-height / max-width
- Method 3: Calculate height with JavaScript
- Method 4: Nuke from orbit with FLIP + Inverse Scaling
- CSS-переход свойства height от 0px до auto
- Анимация CSS width и height без неприятного эффекта
- Не анимируйте свойства width и height
- Вместо этого используйте Transform
How To Do CSS Transitions With Height: Auto
Creating a smooth expand/collapse animation seems easy. Surely you can set a transition on height: auto , and it’ll just work?
Sadly, the CSS gods aren’t so kind.
Instead of the smooth open and close you wanted, your element flashes to its new height. No smooth animation.
As it turns out, animating on CSS auto values doesn’t work, and it’s honestly a real shame. We need to use specific values, and that’s not a luxury we always have.
Fortunately, there are several approaches we can take to animating auto dimensions in CSS.
Method 1: Use transform
Despite its rampant use, you should avoid CSS transitions on the height or width properties (among some others).
These properties affect the page layout. To see how other elements on the page are being affected during your animation, the browser completely recalculates your page’s layout.
This is where the CSS transform property comes in. transform causes your element to be animated like an image, and skip the layout recalculations. If you can use this approach, you should.
Unfortunately, there are two deal-breakers in using transform to collapse your content:
Method 2: Animate max-height / max-width
A different approach to collapsing height is to collapse max-height in its place.
The upside of this approach is that it’s straightforward. It also manages to avoid the weird warping effect of transform: scale .
There are some downsides, though:
- We’re animating on height , with all its layout-thrashing goodness
- You need a non-auto value for max-height
- Your transition-timing applies to the max-height range, not height . This makes it nearly impossible to get a specific visual effect from your transitions.
Method 3: Calculate height with JavaScript
A lengthier (but more precise) approach is to use JavaScript to calculate the animation start and end using the element’s rendered height .
Once we determine the element’s actual height, we can inject it as inline CSS so the browser has something to transition between.
In terms of effect, this is probably the one you were going for, but with tradeoffs.
- We’re still animating on height
- We’ve introduced a fair chunk of code to address a very narrow problem
- There’s tight coupling between your JavaScript and CSS
Method 4: Nuke from orbit with FLIP + Inverse Scaling
Remember Method 1 and all the problems that using transform caused?
Well, it turns out you can fix them.
Content warping can be fixed by applying counter-scaling on your content. The FLIP technique also lets us move surrounding content in a performant way.
This looks much better than our initial transform attempt, and solves the problems we were having. However:
- This solution is big and complex. We’ve introduced a lot of code now, and it’s still a very narrow problem.
- Several moving parts need to stay synchronised
- We need a custom easing function. I’ve used linear for simplicity, but anything else gets gnarly.
- Even more coupling than the height example.
CSS-переход свойства height от 0px до auto
Хочу поделиться ещё одним способом создания css-перехода ( transition ) свойства height от 0px до auto .
Столкнулся с данной проблемой при разработке веб-компонентов TreeView и DataGrid. В TreeView решил сделать плавное развёртывание/свёртывание узлов, а в DataGrid — строки с дополнительным контентом. Почитав интернет, нашёл несколько способов реализации, основные — через свойство max-height и на javascript. Реализация на javascript была исключена — есть же css с поддержкой переходов и анимаций. Остался max-height , тем более в примерах с выпадающими меню всё работает.
В TreeView каждый узел имеет неограниченную вложенность, поэтому сразу не получится определить максимальную высоту его содержимого, да и если max-height задать очень большим, будут проблемы с анимацией перехода. Также, если развернуть дочерние узлы, высота родительского увеличится и может перекрыть max-height . Как ни крути, max-height не подходит. С DataGrid та же проблема — дополнительный контент в строке может быть любой. Нужен height:auto !
Итак, приступим к реализации перехода по свойству height от 0px до auto . Рассмотрим простой пример.
- elBlock: HTMLDivElement — блок, который нужно развёртывать/свёртывать;
- elToggle: HTMLButtonElement — кнопка-переключатель состояния.
Опишем обработчик события onClick для elToggle :
elToggle.addEventListener("click", () => < if (elBlock.style.height === "0px") < elBlock.style.height = `$< elBlock.scrollHeight >px` > else < elBlock.style.height = `$< elBlock.scrollHeight >px`; window.getComputedStyle(elBlock, null).getPropertyValue("height"); elBlock.style.height = "0"; > >);
Осталось добавить возврат height:auto после перехода:
elBlock.addEventListener("transitionend", () => < if (elBlock.style.height !== "0px") < elBlock.style.height = "auto" >>);
Ну вот и всё, теперь развёртывание/свёртывание блока работает как надо и не зависит от размера контента.
Рисунок 1 — Пример развёртывания/свёртывания узлов в TreeView
Стоит отметить минусы данного подхода:
- использование javascript, хотелось бы только css;
- во время перехода может измениться контент (его высота, scrollHeight ) и после его завершения, в случае возврата auto , высота блока резко поменяется в ту или иную сторону. Для избежания данного эффекта, необходимо отслеживать изменение scrollHeight и менять height . Как показывает практика, обычно переходы развёртывания/свёртывания занимают по 0.5 с, а за это время пользователь вряд ли успеет изменить что-то внутри, например, в случае TreeView, развернуть дочерний узел.
Анимация CSS width и height без неприятного эффекта
От автора: возможность анимировать свойства CSS width и height была бы очень полезна. К сожалению, на данный момент это верный способ заставить ваш браузер биться в агонии. В этом 5-минутном руководстве мы рассмотрим использование свойства transform для имитации анимации ширины элемента.
Не анимируйте свойства width и height
Браузерам не нравится, когда им приходится вычислять позиции и размеры элементов на веб-странице. Большинство элементов каким-то образом влияют на отображение других элементов. Изменение размеров одного элемента может иметь множество непредвиденных последствий.
Изменение width и / или height элемента потребует, чтобы браузер вычислил, какие другие элементы (дочерние элементы, смежные элементы или родительский элемент) будут затронуты этим изменением и как эти элементы должны быть обновлены. Этот процесс называется перекомпоновкой и сопровождается перерисовкой.
Это затратные операции, и мы должны максимально избегать их запуска.
Онлайн курс по JavaScript
Научитесь создавать приложения со сложными интерфейсами
Это основной язык для современной веб-разработки — почти 100% сайтов работает на JavaScript. Освойте его с нуля всего за 4 месяца, и вы сможете зарабатывать от 70 000 рублей.
Вместо этого используйте Transform
Для создания анимации стоит использовать свойства CSS transform или opacity. В этой статье мы сосредоточимся на transform.
Свойство transform указывает графическому процессору выполнить последние обновления текстуры элемента перед его отображением на экране. Эти последние обновления могут, например, вращать, перемещать и масштабировать элемент.
Вы можете сравнить текстуру графического элемента с изображением, сделанным вами, с помощью мобильного телефона. Картинка состоит только из пикселей, дочерних элементов, и другой информации об элементе больше нет.
Графический процессор имеет дело только с пикселями, которые представляют элемент. И так как графические процессоры очень хорошо справляются с пикселями, эти операции преобразования являются супер производительными.
Это также имеет обратную сторону. Мы можем манипулировать только пикселями, а не содержимым элемента (у нас есть только изображение элемента). Ниже вы можете увидеть, как эта разница влияет на свойство border-radius. В примере с transform радиус не перерисовывается (как в примере с width), он просто масштабируется. Перекомпоновка требует перерисовки, а графический процессор не может ее выполнить, он работает только с пикселями.