Чудовий ефект наведення для вашого аватара

Чудовий ефект наведення для вашого аватара

Чи знаєте ви такий ефект, коли чиясь голова просовується крізь коло чи отвір? Відомий анімаційний фільм Поркі Піг, де він махає рукою на прощання, вискакуючи з серії червоних кілець, є чудовим прикладом, і Кіліан Валкхоф нещодавно відтворив це тут, на CSS-Tricks.

У мене є схожа ідея, але я взявся за інший спосіб і з додаванням анімації. Я вважаю, що це досить практично та створює гарний ефект наведення, який можна використовувати на чомусь на зразок власного аватара.

Бачиш це? Ми збираємося створити масштабну анімацію, де аватар ніби вискочить із кола, у якому він знаходиться. Круто, правда? Не дивіться на код, а давайте створимо цю анімацію разом крок за кроком.

HTML: лише один елемент

Якщо ви не перевірили код демо-версії і вам цікаво, скільки divЦе займе, а потім зупиніться на цьому, тому що наша розмітка — це не що інше, як один елемент зображення:

<img src="" alt="">

Так, єдиний елемент! Складною частиною цієї вправи є використання найменшої можливої ​​кількості коду. Якщо ви були за мною деякий час ви повинні звикнути до цього. Я наполегливо намагаюся знайти рішення CSS, які можна досягти за допомогою найменшого коду, який максимально зручно підтримувати.

Я написав серія статей тут, на CSS-Tricks, де я досліджую різні ефекти наведення, використовуючи ту саму розмітку HTML, що містить один елемент. Я детально описую градієнти, маскування, відсікання, контури та навіть техніку компонування. Я настійно рекомендую перевірити їх, тому що я повторно використаю багато трюків у цій публікації.

Файл зображення квадратної форми з прозорим фоном найкраще підійде для того, що ми робимо. Ось той, який я використовую, якщо ви хочете почати з нього.

Фантастичний ефект наведення для вашого аватару PlatoBlockchain Data Intelligence. Вертикальний пошук. Ai.
Designed by Cang

Я сподіваюся побачити якомога більше прикладів цього, використовуючи реальні зображення — тому, будь ласка, поділіться своїм кінцевим результатом у коментарях, коли закінчите, щоб ми могли створити колекцію!

Перш ніж переходити до CSS, давайте спочатку розберемо ефект. Зображення збільшується при наведенні, тому ми обов’язково скористаємося transform: scale() там. За аватаром є коло, і радіальний градієнт повинен зробити свою справу. Нарешті, нам потрібен спосіб створити рамку в нижній частині кола, яка створить видимість аватара за колом.

Приступаємо до роботи!

Ефект масштабу

Почнемо з додавання трансформації:

img { width: 280px; aspect-ratio: 1; cursor: pointer; transition: .5s;
}
img:hover { transform: scale(1.35);
}

Ще нічого складного, правда? Йдемо далі.

Коло

Ми сказали, що фон буде радіальним градієнтом. Це ідеально, тому що ми можемо створювати жорсткі межі між кольорами радіального градієнта, що створює враження, ніби ми малюємо коло суцільними лініями.

img { --b: 5px; /* border width */ width: 280px; aspect-ratio: 1; background: radial-gradient( circle closest-side, #ECD078 calc(99% - var(--b)), #C02942 calc(100% - var(--b)) 99%, #0000 ); cursor: pointer; transition: .5s;
}
img:hover { transform: scale(1.35);
}

Зверніть увагу на змінну CSS, --b, я використовую там. Він представляє товщину «межі», яка насправді просто використовується для визначення жорстких кольорових зупинок для червоної частини радіального градієнта.

Наступний крок — пограти з розміром градієнта при наведенні. Коло має зберігати свій розмір у міру збільшення зображення. Оскільки ми застосовуємо a scale() трансформації, ми дійсно потребуємо зменшити розмір кола, тому що інакше воно збільшується до аватара. Таким чином, коли зображення збільшується, нам потрібен градієнт для зменшення масштабу.

Давайте почнемо з визначення змінної CSS, --f, який визначає «масштабний коефіцієнт», і використовуйте його для встановлення розміру кола. Я використовую 1 як значення за замовчуванням, оскільки це початковий масштаб для зображення та кола, з якого ми перетворюємо.

Ось демо для ілюстрації трюку. Наведіть курсор, щоб побачити, що відбувається за лаштунками:

Я додав третій колір до radial-gradient щоб краще визначити область градієнта при наведенні:

radial-gradient( circle closest-side, #ECD078 calc(99% - var(--b)), #C02942 calc(100% - var(--b)) 99%, lightblue
);

Тепер нам потрібно розташувати наш фон у центрі кола та переконатися, що він займає всю висоту. Я люблю заявляти про все прямо на background скорочена властивість, щоб ми могли додати наше фонове позиціонування та переконатися, що воно не повторюється, додавши ці значення відразу після radial-gradient():

background: radial-gradient() 50% / calc(100% / var(--f)) 100% no-repeat;

Фон розташовується по центру (50%), має ширину, рівну calc(100%/var(--f)), і має висоту, що дорівнює 100%.

Ніщо не масштабується, коли --f дорівнює 1 — знову наш початковий масштаб. Тим часом градієнт займає всю ширину контейнера. Коли ми збільшуємо --f, розмір елемента зростає — завдяки scale() transform — і розмір градієнта зменшується.

Ось що ми отримуємо, коли застосовуємо все це до нашої демонстрації:

Ми наближаємось! У нас є ефект переливу вгорі, але нам все одно потрібно приховати нижню частину зображення, щоб воно виглядало так, ніби воно вискакує з кола, а не стоїть перед ним. Це складна частина всієї цієї справи, і це те, що ми збираємося робити далі.

Нижня межа

Я вперше спробував вирішити це за допомогою border-bottom властивість, але я не зміг знайти спосіб зіставити розмір межі з розміром кола. Ось найкраще, що я міг отримати, і ви відразу бачите, що це неправильно:

Фактичним рішенням є використання outline власність. так, outline, Чи не border, в попередня стаття, я показую як outline є потужним і дозволяє нам створювати круті ефекти наведення. У поєднанні з outline-offset, ми маємо саме те, що нам потрібно для нашого ефекту.

Ідея полягає в тому, щоб встановити outline на зображенні та налаштуйте його зсув, щоб створити нижню межу. Зміщення залежатиме від коефіцієнта масштабування так само, як розмір градієнта.

Тепер у нас є наша нижня «межа» (насправді an outline) у поєднанні з «рамкою», створеною градієнтом, щоб створити повне коло. Нам ще потрібно приховати частини outline (зверху та з боків), до якого ми дійдемо за мить.

Ось наш код на даний момент, включаючи ще кілька змінних CSS, за допомогою яких можна налаштувати розмір зображення (--s) і колір «рамки» (--c):

img { --s: 280px; /* image size */ --b: 5px; /* border thickness */ --c: #C02942; /* border color */ --f: 1; /* initial scale */ width: var(--s); aspect-ratio: 1; cursor: pointer; border-radius: 0 0 999px 999px; outline: var(--b) solid var(--c); outline-offset: calc((1 / var(--f) - 1) * var(--s) / 2 - var(--b)); background: radial-gradient( circle closest-side, #ECD078 calc(99% - var(--b)), var(--c) calc(100% - var(--b)) 99%, #0000 ) 50% / calc(100% / var(--f)) 100% no-repeat; transform: scale(var(--f)); transition: .5s;
}
img:hover { --f: 1.35; /* hover scale */
}

Оскільки нам потрібна кругла нижня межа, ми додали a border-radius на нижній стороні, дозволяючи outline щоб відповідати кривизні градієнта.

Розрахунок, використаний на outline-offset набагато простіше, ніж здається. За замовчуванням, outline малюється поза коробки елемента. А в нашому випадку це потрібно перекриття елемент. Точніше, нам потрібно, щоб він слідував колу, створеному градієнтом.

Схема фонового переходу.
Чудовий ефект наведення для вашого аватара

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

Не забуваймо, що другий елемент масштабується, тому наш результат також масштабується… це означає, що нам потрібно розділити результат на f щоб отримати реальне значення зсуву:

Offset = ((f - 1) * S/2) / f = (1 - 1/f) * S/2

Ми додаємо мінус, оскільки нам потрібно, щоб контур йшов ззовні всередину:

Offset = (1/f - 1) * S/2

Ось коротка демонстрація, яка показує, як контур повторює градієнт:

Можливо, ви вже бачите це, але нам все одно потрібен нижній контур, щоб перекривати коло, а не дозволяти йому протікати крізь нього. Ми можемо зробити це, видаливши розмір рамки зі зсуву:

outline-offset: calc((1 / var(--f) - 1) * var(--s) / 2) - var(--b));

Тепер нам потрібно знайти, як видалити верхню частину з контуру. Іншими словами, нам потрібна лише нижня частина зображення outline.

По-перше, давайте додамо простір угорі за допомогою відступу, щоб уникнути перекриття вгорі:

img { --s: 280px; /* image size */ --b: 5px; /* border thickness */ --c: #C02942; /* border color */ --f: 1; /* initial scale */ width: var(--s); aspect-ratio: 1; padding-block-start: calc(var(--s)/5); /* etc. */
}
img:hover { --f: 1.35; /* hover scale */
}

Немає особливої ​​логіки в цьому верхньому прокладці. Ідея полягає в тому, щоб контур не торкався голови аватара. Я використовував розмір елемента, щоб визначити, що простір завжди має однакову пропорцію.

Зверніть увагу, що я додав content-box значення для background:

background: radial-gradient( circle closest-side, #ECD078 calc(99% - var(--b)), var(--c) calc(100% - var(--b)) 99%, #0000 ) 50%/calc(100%/var(--f)) 100% no-repeat content-box;

Нам це потрібно, тому що ми додали відступи, і ми хочемо встановити фон лише для вікна вмісту, тому ми повинні явно вказати фону зупинитися на цьому.

Додавання маски CSS до суміші

Ми дійшли до останньої частини! Все, що нам потрібно зробити, це сховати деякі частини, і ми готові. Для цього ми будемо спиратися на mask і, звичайно, градієнти.

Ось малюнок, щоб проілюструвати те, що нам потрібно приховати або що нам потрібно показати, щоб бути точнішим

Показує, як маска застосовується до нижньої частини кола.
Чудовий ефект наведення для вашого аватара

Ліве зображення – це те, що ми маємо зараз, а праве – те, що ми хочемо. Зелена частина ілюструє маску, яку ми повинні застосувати до вихідного зображення, щоб отримати остаточний результат.

Ми можемо визначити дві частини нашої маски:

  • Кругла частина внизу, яка має той самий розмір і кривизну, що й радіальний градієнт, який ми використали для створення кола позаду аватара
  • Прямокутник у верхній частині, який покриває область всередині контуру. Зверніть увагу на те, що контур знаходиться за межами зеленої зони вгорі — це найважливіша частина, оскільки це дозволяє обрізати контур так, щоб було видно лише нижню частину.

Ось наш остаточний CSS:

img { --s: 280px; /* image size */ --b: 5px; /* border thickness */ --c: #C02942; /* border color */ --f: 1; /* initial scale */ --_g: 50% / calc(100% / var(--f)) 100% no-repeat content-box; --_o: calc((1 / var(--f) - 1) * var(--s) / 2 - var(--b)); width: var(--s); aspect-ratio: 1; padding-top: calc(var(--s)/5); cursor: pointer; border-radius: 0 0 999px 999px; outline: var(--b) solid var(--c); outline-offset: var(--_o); background: radial-gradient( circle closest-side, #ECD078 calc(99% - var(--b)), var(--c) calc(100% - var(--b)) 99%, #0000) var(--_g); mask: linear-gradient(#000 0 0) no-repeat 50% calc(-1 * var(--_o)) / calc(100% / var(--f) - 2 * var(--b)) 50%, radial-gradient( circle closest-side, #000 99%, #0000) var(--_g); transform: scale(var(--f)); transition: .5s;
}
img:hover { --f: 1.35; /* hover scale */
}

Давайте розберемо це mask власність. Для початку зауважте, що схоже radial-gradient() від background власність є там. Я створив нову змінну, --_g, для загальних частин, щоб речі були менш захаращеними.

--_g: 50% / calc(100% / var(--f)) 100% no-repeat content-box; mask: radial-gradient( circle closest-side, #000 99%, #0000) var(--_g);

Далі є a linear-gradient() там також:

--_g: 50% / calc(100% / var(--f)) 100% no-repeat content-box; mask: linear-gradient(#000 0 0) no-repeat 50% calc(-1 * var(--_o)) / calc(100% / var(--f) - 2 * var(--b)) 50%, radial-gradient( circle closest-side, #000 99%, #0000) var(--_g);

Це створює прямокутну частину маски. Його ширина дорівнює ширині радіального градієнта мінус подвоєна товщина межі:

calc(100% / var(--f) - 2 * var(--b))

Висота прямокутника дорівнює половині, 50%, від розміру елемента.

Нам також потрібен лінійний градієнт, розміщений у горизонтальному центрі (50%) і зсув від верху на те саме значення, що й зміщення контуру. Я створив ще одну змінну CSS, --_o, для зміщення, яке ми визначили раніше:

--_o: calc((1 / var(--f) - 1) * var(--s) / 2 - var(--b));

Однією з заплутаних речей є те, що нам потрібен a негативний зсув для контуру (щоб перемістити його ззовні всередину), але a позитивний зсув для градієнта (для переміщення зверху вниз). Отже, якщо вам цікаво, чому ми множимо зсув, --_o, по -1ну, тепер ти знаєш!

Ось демонстрація, щоб проілюструвати конфігурацію градієнта маски:

Наведіть вказівник миші на зображення вище та подивіться, як усе рухається разом. Середнє поле ілюструє шар маски, що складається з двох градієнтів. Уявіть це як видиму частину лівого зображення, і ви отримаєте кінцевий результат справа!

Підводячи підсумок

Ой, ми закінчили! І ми не тільки закінчили з гладкою анімацією наведення, але ми зробили все це за допомогою одного HTML <img> елемент. Тільки це та менше 20 рядків CSS-обману!

Звичайно, ми покладалися на деякі невеликі хитрощі та математичні формули, щоб досягти такого складного ефекту. Але ми точно знали, що робити, оскільки заздалегідь визначили потрібні елементи.

Чи могли б ми спростити CSS, якби дозволили собі більше HTML? Абсолютно. Але ми тут, щоб вивчати нові прийоми CSS! Це була хороша вправа для вивчення градієнтів CSS, маскування тощо outline поведінка власності, перетворення та багато іншого. Якщо ви колись відчули, що загубилися, обов’язково перевірте моя серія який використовує ті самі загальні поняття. Іноді допомагає побачити більше прикладів і варіантів використання, щоб переконатися в тому.

Я залишу вам останню демонстрацію, яка використовує фотографії популярних розробників CSS. Не забудьте показати мені демо-версію з вашим власним зображенням, щоб я міг додати її до колекції!

Часова мітка:

Більше від CSS-хитрощі