От автора: как делается анимация border CSS по состоянию hover? Просто, так ведь? Вы удивитесь. Задача проста – создать кнопку с расширяющейся рамкой при наведении мыши. В сегодняшней статье обсудим реальные CSS советы, которые легко внедрить в любой проект без вмешательства в DOM и подключения JS. Описываемые методы следуют следующим правилам.
Один элемент (без вспомогательных div, псевдоэлементы использовать можно)
Только CSS (без JS)
Работа при любом размере (без ограничения по ширине, высоте и соотношению сторон)
Поддержка прозрачных фонов
Плавный и быстрый переход
Метод 1: анимация border
Самый простой способ анимировать рамку… это анимировать свойство border.
1 2 3 4 5 6 |
.border-button { border: solid 5px #FC5185; transition: border-width 0.6s linear; } .border-button:hover { border-width: 10px; } |
Красиво и просто, но производительность сильно хромает.
Так как border занимает место в макете документа, то изменение border-width вызовет изменение макета. Окружающие элементы сдвинутся от элемента из-за нового размера рамки, что заставляет браузер заново вычислять позиции элементов каждый кадр анимации, если у кнопки не заданы явные размеры.
Перестройка макета не самое плохое, ведь плавный переход «отрывистый». В следующем примере я покажу вам это.
Метод 2: улучшенный border с outline
Как изменить рамку без перестройки макета? С помощью outline! Скорее всего, вы знакомы с outline (удаляете outline по :focus (не стоит этого делать)). Однако outline это внешняя линия, не меняющая размеры и положение элементов в макете.
1 2 3 4 5 6 7 |
.border-button { outline: solid 5px #FC5185; transition: outline 0.6s linear; margin: 0.5em; /* Increased margin since the outline expands outside the element */ } .border-button:hover { outline-width: 10px; } |
Если заглянуть в панель разработчика в браузере на вкладку Performance, можно увидеть, что outline не вызывает перестройку макета. Тем не менее, движения все еще выглядят отрывистыми, так как браузеры округляют значения border-width и outline-width, чтобы не было рендера меньше пикселя (между 5 и 6) и плавных переходов между 5.4 и 5.5.
Странно, но Safari часто не рендерит переход outline и оставляет непонятные артифакты.
Метод 3: кадрирование через clip-path
Steve Gardner создал этот метод. Метод использует clip-path и calc для обрезания рамки вовнутрь. При наведении мыши мы можем показывать полную ширину рамки.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
.border-button { /* Full width border and a clip-path visually cutting it down to the starting size */ border: solid 10px #FC5185; clip-path: polygon( calc(0% + 5px) calc(0% + 5px), /* top left */ calc(100% - 5px) calc(0% + 5px), /* top right */ calc(100% - 5px) calc(100% - 5px), /* bottom right */ calc(0% + 5px) calc(100% - 5px) /* bottom left */ ); transition: clip-path 0.6s linear; } .border-button:hover { /* Clip-path spanning the entire box so it's no longer hiding the full-width border. */ clip-path: polygon(0 0, 100% 0, 100% 100%, 0 100%); } |
Техника с clip-path самая плавная и быстрая, но у нее есть несколько минусов. Ошибки округления могут вызвать небольшие неровности на определенных размерах. Изначально ширина рамки полная, из-за чего возникают сложности с позиционированием.
К сожалению, в IE/Edge до сих пор поддержки нет, однако она в разработке. Вы можете и обязаны поддержать команду Microsoft, проголосовав за добавление masks/clip-path.
Метод 4: фон linear-gradient
Рамку можно имитировать с помощью хитрой комбинации нескольких фонов linear-gradient с правильными размерами. Необходимо 4 отдельных градиента, по одному на сторону. Свойства background-position и background-size устанавливают все градиенты в правильное место и размеры, а их уже можно плавно менять и расширять рамку.
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 |
.border-button { background-repeat: no-repeat; /* background-size values will repeat so we only need to declare them once */ background-size: calc(100% - 10px) 5px, /* top & bottom */ 5px calc(100% - 10px); /* right & left */ background-position: 5px 5px, /* top */ calc(100% - 5px) 5px, /* right */ 5px calc(100% - 5px), /* bottom */ 5px 5px; /* left */ /* Since we're sizing and positioning with the above properties, we only need to set up a simple solid-color gradients for each side */ background-image: linear-gradient(0deg, #FC5185, #FC5185), linear-gradient(0deg, #FC5185, #FC5185), linear-gradient(0deg, #FC5185, #FC5185), linear-gradient(0deg, #FC5185, #FC5185); transition: all 0.6s linear; transition-property: background-size, background-position; } .border-button:hover { background-position: 0 0, 100% 0, 0 100%, 0 0; background-size: 100% 10px, 10px 100%, 100% 10px, 10px 100%; } |
Метод немного сложный и немного различается в браузерах. Firefox и Safari плавно анимируют faux-border, что нам и нужно. В Chrome анимация дерганая и еще более отрывистая чем в outline и border. IE и Edge вообще отказываются анимировать background, зато эти браузеры правильно задают размеры рамки.
Метод 5: имитация через box-shadow
В спецификации box-shadow скрыто четвертое значение spread-radius. Установите все остальные значения в 0px и создайте рамку через spread-radius. Свойство, как и outline, не влияет на макет.
1 2 3 4 5 6 7 |
.border-button { box-shadow: 0px 0px 0px 5px #FC5185; transition: box-shadow 0.6s linear; margin: 0.5em; /* Increased margin since the box-shado expands outside the element, like outline */ } .border-button:hover { box-shadow: 0px 0px 0px 10px #FC5185; } |
Переход box-shadow довольно быстрый и намного плавнее. Исключение Safari – браузер привязывается к целым значениям во время переходов свойств border и outline.
Псевдоэлементы
Несколько техник можно изменить под псевдо-элементы, однако псевдо-элементы вызывают проблемы с производительностью.
В случае с методом box-shadow плавный переход иногда вызывает перерисовку большей области, чем необходимо. Reinier Kaper заметил, что псевдоэлементы могут изолировать отрисовку на большую область. Дополнительные тесты выявили, что box-shadow больше не вызывает отрисовку на больших областях документа, а сложность псевдоэлементов снижает производительность. Изменения в отрисовке и производительности могли быть вызваны обновлением Chrome, можете сами проверить.
Я также не нашел способа, как с помощью псевдоэлементов использовать transform анимацию.
А почему не transform: scale?
Можете обратиться в Twitter и предложить transform: scale. Свойства transform и opacity лучше всего анимируются по скорости, так почему не использовать псевдоэлемент и менять размер рамки?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
.border-button { position: relative; margin: 0.5em; border: solid 5px transparent; background: #3E4377; } .border-button:after { content: ''; display: block; position: absolute; top: 0; right: 0; bottom: 0; left: 0; border: solid 10px #FC5185; margin: -15px; z-index: -1; transition: transform 0.6s linear; transform: scale(0.97, 0.93); } .border-button:hover::after { transform: scale(1,1); } |
Есть несколько проблем:
Рамка будет видна через прозрачную кнопку. Я специально убрал фон кнопки, чтобы показать, как рамка прячется под кнопкой. Если в вашем дизайне кнопки с фоном, то все будет нормально.
Можно масштабировать рамку до определенного размера. Размеры кнопки могут меняться в зависимости от текста, поэтому нет четкого способа анимировать рамку от 5px до 10px через CSS. В этом примере я добавил немного магии в scale, но пример не универсален.
Рамка анимируется неровно из-за того, что соотношение сторон у кнопки не 1:1. То есть left/right будут больше, чем top/bottom, пока анимация не завершится. Проблема может быть не заметна на большой скорости анимации, правильном соотношении сторон кнопки и размерах рамки.
Если у вашей кнопки заданы размеры, Cher нашла умный способ вычисления точного масштаба. Однако могут возникнуть некоторые ошибки при округлении.
Заключение
Рамка это не просто border. Если вы захотели анимировать рамку, у вас может возникнуть несколько проблем. Описанные здесь методы помогут сделать это, но они не идеальны. Ваш выбор зависит от требований проекта. Я составил таблицу сравнения, чтобы помочь вам с выбором.
Я рекомендую использовать box-shadow – лучшая смесь простоты, эффекта анимации, скорости и поддержки.
Автор: Stephen Shaw
Источник: //css-tricks.com/
Редакция: Команда webformyself.