Анімація border в CSS

582

Від автора: як робиться анімація border CSS станом hover? Просто, так адже? Ви здивуєтеся. Задача проста – створити кнопку з розширюється рамкою при наведенні миші. У сьогоднішній статті обговоримо реальні CSS поради, які легко впровадити в будь-проект без втручання в DOM і підключення JS. Описуються методи дотримуються наступних правил.

Один елемент (без допоміжних div, псевдоелементи використовувати можна)

Тільки CSS (без JS)

Робота при будь-якому розмірі (без обмеження по ширині, висоті і співвідношенням сторін)

Підтримка прозорих фонів

Плавний і швидкий перехід

Метод 1: анімація border

Найпростіший спосіб анімувати рамку… це анімувати властивість border.

.border-button {
border: 5px solid #FC5185;
transition: border-width 0.6 s linear;
}
.border-button:hover { border-width: 10px; }

Гарно і просто, але продуктивність сильно кульгає.

Так як border займає місце в макеті документа, то зміна border-width викличе зміна макета. Навколишні елементи зрушаться від елемента з-за нового розміру рамки, що змушує браузер заново обчислювати позиції елементів кожен кадр анімації, якщо кнопки не задані явні розміри.

Перебудова макета не найгірше, адже плавний перехід «уривчастий». У наступному прикладі я покажу вам це.

Метод 2: покращений border з outline

Як змінити рамку без перебудови макета? З допомогою outline! Швидше за все, ви знайомі з outline (видаляєте outline по :focus (не варто цього робити)). Однак outline це зовнішня лінія, не змінює розміри і положення елементів в макеті.

.border-button {
outline: 5px solid #FC5185;
transition: outline 0.6 s linear;
margin: 0.5 em; /* 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 і залишає незрозумілі артифакты.

Анімація border в CSS

Метод 3: кадрування через clip-path

Steve Gardner створив цей метод. Метод використовує clip-path і calc для обрізання рамки всередину. При наведенні миші ми можемо показувати повну ширину рамки.

.border-button {
/* Full border width 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.6 s linear;
}
.border-button:hover {
/* Clip-path spanning the entire box so it’s no longer hiding the full-border width. */
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 встановлюють всі градієнти в правильне місце і розміри, а їх вже можна плавно змінювати і розширювати рамку.

.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.6 s 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, не впливає на макет.

.border-button {
box-shadow: 0px 0px 0px 5px #FC5185;
transition: box-shadow 0.6 s linear;
margin: 0.5 em; /* 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 найкраще анімуються по швидкості, так чому не використовувати псевдоэлемент і змінювати розмір рамки?

.border-button {
position: relative;
margin: 0.5 em;
border: 5px solid 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.6 s 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 – найкраща суміш простоти, ефекту анімації, швидкості і підтримки.