Добавить в корзинуПозвонить
Найти в Дзене
WebTutorials

Причудливый эффект наведения для вашего аватара

Вам знаком такой эффект, когда чья-то голова просовывается сквозь круг или отверстие? Знаменитая анимация Porky Pig, где он машет рукой на прощание, выскакивая из серии красных колец, является прекрасным примером, и Килиан Валкхоф фактически воссоздал это на CSS-Tricks некоторое время назад. У меня есть похожая идея, но реализованная по-другому и с небольшим количеством анимации. Я думаю, что это довольно практично и создает аккуратный эффект наведения, который вы можете использовать на чем-то вроде своего собственного аватара. Мы собираемся сделать анимацию масштабирования, в которой аватар, кажется, выпрыгивает прямо из круга, в котором он находится. Круто, правда? Давайте вместе создадим эту анимацию шаг за шагом. HTML: всего один элемент Если вы еще не проверяли код демо и вам интересно, сколько div это займет, то остановитесь прямо сейчас, потому что наша разметка — это не что иное, как один элемент изображения: Да, один элемент! Сложной частью этого упражнения является использова
Оглавление

Вам знаком такой эффект, когда чья-то голова просовывается сквозь круг или отверстие? Знаменитая анимация Porky Pig, где он машет рукой на прощание, выскакивая из серии красных колец, является прекрасным примером, и Килиан Валкхоф фактически воссоздал это на CSS-Tricks некоторое время назад.

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

Мы собираемся сделать анимацию масштабирования, в которой аватар, кажется, выпрыгивает прямо из круга, в котором он находится. Круто, правда? Давайте вместе создадим эту анимацию шаг за шагом.

HTML: всего один элемент

Если вы еще не проверяли код демо и вам интересно, сколько div это займет, то остановитесь прямо сейчас, потому что наша разметка — это не что иное, как один элемент изображения:

-2

Да, один элемент! Сложной частью этого упражнения является использование наименьшего возможного количества кода. Если вы следили за мной некоторое время, вы должны привыкнуть к этому. Я изо всех сил стараюсь найти CSS-решения, которые можно реализовать с помощью наименьшего, наиболее удобного для сопровождения кода.

Квадратный файл изображения с прозрачным фоном лучше всего подойдет для того, что мы делаем. Вот тот, который я использую, если вы хотите начать с него.

-3

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

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

Давай приступим к работе!

Эффект масштабирования

Начнем с добавления трансформации:

-4

Пока ничего сложного, правда? Давайте двигаться дальше.

Круг

Мы сказали, что фон будет радиальным градиентом. Это идеально, потому что мы можем создавать резкие переходы между цветами радиального градиента, что делает его похожим на то, что мы рисуем круг со сплошными линиями.

-5

Обратите внимание на переменную CSS --b, которую я использую. Она представляет собой толщину «границы», которая на самом деле просто используется для определения жестких цветовых остановок для красной части радиального градиента.

-6

Следующий шаг — поиграть с размером градиента при наведении. Круг должен сохранять свой размер по мере роста изображения. Поскольку мы применяем преобразование scale(), нам действительно нужно уменьшить размер круга, потому что в противном случае он увеличивается вместе с аватаром. Таким образом, пока изображение увеличивается, нам нужно, чтобы градиент уменьшался.

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

Вот демонстрация, чтобы проиллюстрировать трюк. Наведите курсор, чтобы увидеть, что происходит за кулисами.

Я добавил третий цвет к радиальному градиенту, чтобы лучше идентифицировать область градиента при наведении:

-7

Теперь нам нужно расположить наш фон в центре круга и убедиться, что он занимает всю высоту. Мне нравится объявлять все непосредственно в сокращенном свойстве фона, поэтому мы можем добавить наше позиционирование фона и убедиться, что оно не повторяется, прикрепив эти значения сразу после radio-gradient():

-8

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

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

Вот что мы получаем, когда применяем все это к нашей демонстрации.

Мы близки к финишу! У нас есть эффект переполнения вверху, но нам все еще нужно скрыть нижнюю часть изображения, чтобы оно выглядело так, как будто оно выходит из круга, а не находится перед ним. Это сложная часть всего этого, и это то, что мы собираемся делать дальше.

Нижняя граница

Сначала я попытался решить эту проблему с помощью свойства border-bottom, но не смог найти способ сопоставить размер границы с размером круга. Вот лучшее, что я смог получить, и вы сразу видите, что это неправильно.

Фактическим решением является использование свойства outline. Да, outline, а не border. В предыдущей статье я показывал, насколько мощен outline и позволяет нам создавать крутые эффекты при наведении курсора. В сочетании с смещением контура у нас есть именно то, что нам нужно для нашего эффекта.

Идея состоит в том, чтобы установить outline изображения и настроить его смещение, чтобы создать нижнюю границу. Смещение будет зависеть от коэффициента масштабирования так же, как размер градиента.

-9

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

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

-10

Поскольку нам нужна круглая нижняя граница, мы добавили border-radius на нижней стороне, чтобы outline соответствовал кривизне градиента.

Вычисление, используемое для layout-offset, намного проще, чем кажется. По умолчанию outline рисуется вне поля элемента. И в нашем случае нам нужно, чтобы он перекрывал элемент. Точнее, нам нужно, чтобы он следовал кругу, созданному градиентом.

-11

Когда мы масштабируем элемент, мы видим пространство между кругом и краем. Не будем забывать, что идея состоит в том, чтобы сохранить окружность того же размера после выполнения преобразования масштаба, что оставляет нам пространство, которое мы будем использовать для определения смещения контура, как показано на рисунке выше.

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

-12

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

-13

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

-14

Теперь нам нужно найти, как удалить верхнюю часть из контура. Другими словами, нам нужна только нижняя часть контура изображения.

Во-первых, давайте добавим пространство вверху с отступом, чтобы избежать перекрытия вверху:

-15
Без наведения
Без наведения
С наведением
С наведением

В этом верхнем отступе нет особой логики. Идея состоит в том, чтобы контур не касался головы аватара. Я использовал размер элемента, чтобы определить, что пространство всегда имеет одну и ту же пропорцию.

Обратите внимание, что я добавил значение content-box на background:

-18

Нам это нужно, потому что мы добавили отступы, и мы хотим, чтобы фон был установлен только в поле содержимого, поэтому мы должны явно указать фону остановиться на этом.

Добавление mask CSS в mix

Мы дошли до последней части! Все, что нам нужно сделать, это скрыть некоторые части, и все готово. Для этого мы будем полагаться на свойство маски и, конечно же, на градиенты.

Вот рисунок, чтобы проиллюстрировать, что нам нужно скрыть или что нам нужно показать, чтобы быть более точным.

-19

Левое изображение — это то, что у нас есть на данный момент, а правое — то, что мы хотим. Зеленая часть иллюстрирует маску, которую мы должны применить к исходному изображению, чтобы получить окончательный результат.

Мы можем выделить две части нашей маски:

  • Круглая часть внизу, которая имеет тот же размер и кривизну, что и радиальный градиент, который мы использовали для создания круга позади аватара.
  • Прямоугольник вверху, покрывающий область внутри контура. Обратите внимание, что контур находится за пределами зеленой области вверху — это самая важная часть, поскольку она позволяет обрезать контур так, чтобы была видна только нижняя часть.

Вот наш окончательный CSS:

часть 1
часть 1
часть 2
часть 2

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

-22

Далее, здесь также есть linear-gradient():

-23

Это создает прямоугольную часть маски. Его ширина равна ширине радиального градиента минус удвоенная толщина границы:

-24

Высота прямоугольника равна половине, 50% размера элемента.

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

-25

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

Вот демонстрация, иллюстрирующая конфигурацию градиента маски.

Наведите курсор выше и посмотрите, как все движется вместе. В средней рамке показан слой маски, состоящий из двух градиентов. Представьте, что это видимая часть левого изображения, а конечный результат вы получите справа!

Подведение итогов

Уф, мы закончили! И мы не только получили плавную анимацию при наведении, но и сделали все это с помощью одного HTML-элемента img. Только это и менее 20 строк хитрости CSS!

Конечно, мы полагались на некоторые маленькие хитрости и математические формулы, чтобы добиться такого сложного эффекта. Но мы точно знали, что делать, поскольку заранее определили нужные элементы.

Могли бы мы упростить CSS, если бы позволили себе больше HTML? Абсолютно. Но мы здесь, чтобы изучить новые приемы CSS! Это было хорошее упражнение для изучения CSS-градиентов, маскирования, поведения свойств контура, преобразований и многого другого. Если в какой-то момент вы почувствовали себя потерянным, обязательно ознакомьтесь с моей серией, в которой используются те же общие концепции. Иногда полезно увидеть больше примеров и вариантов использования, чтобы понять суть.

Я оставлю вам последнюю демонстрацию, в которой используются фотографии популярных разработчиков CSS. Не забудьте показать мне демо со своим изображением, чтобы я мог добавить его в коллекцию!

-26

Перевод статьи "A Fancy Hover Effect For Your Avatar".