От автора: возможность анимировать свойства CSS width и height была бы очень полезна. К сожалению, на данный момент это верный способ заставить ваш браузер биться в агонии. В этом 5-минутном руководстве мы рассмотрим использование свойства transform для имитации анимации ширины элемента.
Не анимируйте свойства width и height
Браузерам не нравится, когда им приходится вычислять позиции и размеры элементов на веб-странице. Большинство элементов каким-то образом влияют на отображение других элементов. Изменение размеров одного элемента может иметь множество непредвиденных последствий.
Изменение width и / или height элемента потребует, чтобы браузер вычислил, какие другие элементы (дочерние элементы, смежные элементы или родительский элемент) будут затронуты этим изменением и как эти элементы должны быть обновлены. Этот процесс называется перекомпоновкой и сопровождается перерисовкой.
Это затратные операции, и мы должны максимально избегать их запуска.
Вместо этого используйте Transform
Для создания анимации стоит использовать свойства CSS transform или opacity. В этой статье мы сосредоточимся на transform.
Свойство transform указывает графическому процессору выполнить последние обновления текстуры элемента перед его отображением на экране. Эти последние обновления могут, например, вращать, перемещать и масштабировать элемент.
Вы можете сравнить текстуру графического элемента с изображением, сделанным вами, с помощью мобильного телефона. Картинка состоит только из пикселей, дочерних элементов, и другой информации об элементе больше нет.
Графический процессор имеет дело только с пикселями, которые представляют элемент. И так как графические процессоры очень хорошо справляются с пикселями, эти операции преобразования являются супер производительными.
Это также имеет обратную сторону. Мы можем манипулировать только пикселями, а не содержимым элемента (у нас есть только изображение элемента). Ниже вы можете увидеть, как эта разница влияет на свойство border-radius. В примере с transform радиус не перерисовывается (как в примере с width), он просто масштабируется. Перекомпоновка требует перерисовки, а графический процессор не может ее выполнить, он работает только с пикселями.
1 2 3 4 5 6 7 8 9 |
.square { width: 200px; height: 200px; border-radius: 40px; } .square-resized { width: 100px } .square-transformed { transform: scaleX(.5) } |
Анимация свойства transform выполняется в миллион раз быстрее, чем анимация width, height или любого другого свойства, которое влияет на макет и вызывает перекомпоновку.
Но если мы анимируем это, результатом будет странно растянутая фигура. Это может быть быстро, но выглядеть это будет не очень хорошо. Это всегда напоминает мне о тех инопланетянах в Duke Nukem 3D, которых раздавило дверью.
Метод 9-фрагментного масштабирования
До появления border-radius нам приходилось создавать изображение для каждого угла (а иногда и края) элемента, чтобы «имитировать» радиус округления. В зависимости от дизайна, который может дать до 8 изображений для одного элемента, эти изображения будут расположены следующим образом.
Это называется методом 9-фрагментного масштабирования.
Этот метод позволяет масштабировать элемент и растягивать изображения 2, 4, 6 и 8, в то же время связывая 1, 3, 7 и 9 с соответствующими им углами, используя абсолютное позиционирование. Это дает углы, которые не растягиваются при масштабировании.
К счастью, мы живем в эпоху, когда браузеры поддерживают border-radius, поэтому нам больше не нужно применять эту технику для рисования закругленных углов.
Однако мы можем применить ее, чтобы изменить размеры элементов, используя свойство transform. Мы начнем с такой структуры:
Давайте применим это к нашему квадрату. Это будет означать, что нам нужен один контейнер и три дочерних элемента для представления левой (1), центральной (2) и правой (3) частей квадрата.
1 2 3 4 5 |
<div class="square"> <div class="left"></div> <div class="center"></div> <div class="right"></div> </div> |
Давайте посмотрим на CSS:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 |
/* children will be positioned relativly to this element */ .square { position: relative; height: 100px; } .left, .center, .right { position: absolute; top: 0; bottom: 0; } .left { background: red } .center { background: yellow } .right { background: blue } /* we need room for a 20 pixel border radius (on one side) */ .left, .right { width: 20px } .left { border-radius: 20px 0 0 20px } .right { border-radius: 0 20px 20px 0 } /* child layout definitions */ .center { /* center needs to be 20 pixels from the left, so it doesn't overlap with the .left element */ left: 20px; /* needs a width of 1 pixel, this causes scaleX(60) is equal to 60 pixels */ width: 1px; transform: scaleX(60); /* we need to scale the texture from the left side for it to align correctly, default is center */ transform-origin: left; } .right { /* we need to move the right element to align with the right side of the .center element 20px + 60px = 80px */ transform: translateX(80px); } |
В результате получается красиво окрашенный квадрат.
Я раскрасил отдельные элементы, чтобы сделать их индивидуально идентифицируемыми. Теперь для следующего трюка, мы можем анимировать элементы квадрата .center и .right.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
.center { animation: center-animate 1s ease infinite alternate; } .right { animation: right-animate 1s ease infinite alternate; } @keyframes right-animate { 0% { transform: translateX(140px) } 100% { transform: translateX(20px) } } @keyframes center-animate { 0% { transform: scaleX(120) } 100% { transform: scaleX(0) } } |
Если вы используете Firefox, это должно выглядеть великолепно. Если вы используете Safari или Chrome, то можете заметить легкое мерцание края между центральной и правой частью.
Я не уверен, почему это происходит. Это либо проблема с альфа-смешиванием, либо с округлением пикселей. Тем не менее. Немного перекрывая центральный элемент с соответствующим элементом, мы можем решить эту проблему рендеринга. Мы позаботимся о том, чтобы элемент имел ширину не менее 1 пикселя и масштабировался до 121 пикселей.
1 2 3 4 |
@keyframes center-animate { 0% { transform: scaleX(121) } 100% { transform: scaleX(1) } } |
Мы могли бы пойти еще дальше и сделать его более гибким, переместив анимацию в JavaScript, создав веб-компонент или используя переменные CSS, но для целей этой статьи этого примера должно быть достаточно.
Посмотреть демо конечного результата на CodePen
Придержите лошадей
Прежде чем вы начнете использовать эту технику, обратите внимание, что:
Фоновые изображения в квадрате будут растянуты, потому что мы масштабируем центральный квадрат по оси X.
Можно задать box-shadow, но это немного сложно.
Квадрат не может обрезать другие элементы, так как overflow: hidden не будет работать.
Тем не менее, применив небольшие хитрости, вы можете использовать это, чтобы создавать удивительные эффекты. Элементы файла и область добавления библиотеки загрузки файлов FilePond — все используют эту технику, чтобы плавно анимировать их высоту.
Предыдущая версия Doka Image Editor использовала эту технику как по ширине, так и по высоте, чтобы визуализировать и анимировать белый прямоугольник обрезки в верхней части изображения. Позже это было частично заменено на WebGL.
Заключение
По умолчанию свойства CSS width и height (наряду с большинством других свойств) не подходят для анимации. Это слишком сильно влияет на производительность рендеринга.
Мы переключились на свойство transform и анимировали квадрат, задействуя графический процессор, чтобы значительно улучшить производительность, но в итоге появились некоторые другие ограничения.
Умно применив 9-фрагментную технику, мы обошли некоторые из этих ограничений, чтобы создать элемент, который мы можем анимировать, без негативного влияния на производительность.
Автор: Rik Schennink
Источник: //pqina.nl
Редакция: Команда webformyself.