Псевдо 3D эффект при наведении на карточку

В данной статье кратко опишу как добиться такого эффекта карточки при наведении

1

Структура примера

Сверстаем 4 карточки, при этом будем использовать CSS переменную --color прямо в инлайн стилях для элементов выбора цвета

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8"/>
    <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
    <title>Pseudo 3D Effect on Hover</title>
    <link rel="stylesheet" href="css/bootstrap-reboot.min.css"/>
    <link rel="preconnect" href="https://fonts.gstatic.com"/>
    <link href="https://fonts.googleapis.com/css2?family=Lora:wght@700&amp;family=Roboto:wght@300;400&amp;display=swap" rel="stylesheet"/>
    <link rel="stylesheet" href="css/main.css"/>
  </head>
  <body>
    <section class="section">
      <div class="container">
        <div class="section__wrapper">
          <div class="card"><a href="">
              <div class="card__image"><img src="https://images.unsplash.com/photo-1545838593-b4aeec49204b?ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&amp;ixlib=rb-1.2.1&amp;auto=format&amp;fit=crop&amp;w=1000&amp;q=80" alt=""/></div>
              <div class="card__body">
                <div class="card__title"><span>Relaxed Fit Short-sleeve Shirt</span></div>
                <div class="card__price"><span>$14</span></div>
                <div class="card__colors">
                  <div class="card__color" style="--color:#92D1C3"></div>
                  <div class="card__color" style="--color:#6A7FDB"></div>
                  <div class="card__color" style="--color:#EE7674"></div>
                </div>
              </div></a></div>
          <div class="card"><a href="">
              <div class="card__image"><img src="https://images.unsplash.com/photo-1568151556783-f2b5999eada6?ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&amp;ixlib=rb-1.2.1&amp;auto=format&amp;fit=crop&amp;w=1000&amp;q=80" alt=""/></div>
              <div class="card__body">
                <div class="card__title"><span>Fit Short-sleeve Shirt</span></div>
                <div class="card__price"><span>$24</span></div>
                <div class="card__colors">
                  <div class="card__color" style="--color:#216869"></div>
                  <div class="card__color" style="--color:#F6AE2D"></div>
                  <div class="card__color" style="--color:#EE7674"></div>
                </div>
              </div></a></div>
          <div class="card"><a href="">
              <div class="card__image"><img src="https://images.unsplash.com/photo-1566204580788-1437e545bb36?ixlib=rb-1.2.1&amp;ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&amp;auto=format&amp;fit=crop&amp;w=1000&amp;q=80" alt=""/></div>
              <div class="card__body">
                <div class="card__title"><span>Sleeve Shirt</span></div>
                <div class="card__price"><span>$19</span></div>
                <div class="card__colors">
                  <div class="card__color" style="--color:#FDF5BF"></div>
                  <div class="card__color" style="--color:#F26419"></div>
                  <div class="card__color" style="--color:#EE7674"></div>
                </div>
              </div></a></div>
          <div class="card"><a href="">
              <div class="card__image"><img src="https://images.unsplash.com/photo-1549043671-1e4550948355?ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&amp;ixlib=rb-1.2.1&amp;auto=format&amp;fit=crop&amp;w=1000&amp;q=80" alt=""/></div>
              <div class="card__body">
                <div class="card__title"><span>Short-sleeve Shirt</span></div>
                <div class="card__price"><span>$9</span></div>
                <div class="card__colors">
                  <div class="card__color" style="--color:#216869"></div>
                  <div class="card__color" style="--color:#6A7FDB"></div>
                  <div class="card__color" style="--color:#47624F"></div>
                </div>
              </div></a></div>
        </div>
      </div>
    </section>
  </body>
</html>


CSS стили

Основную сетку карточек и элементы выбора цвета сделаем через display: grid;, чтобы более удобно и гибко и настраивать расположение элементов и расстояние между ними


Для элемента выбора цвета задаем начальное значение CSS переменной --color: transparent; и сразу применяем ее значение для background-color: var(--color);

Значение переменной мы заменяем необходимым цветом в инлайн стилях в HTML структуре


Карточке задаём начальные значения свойств, которые будет меняться при наведении курсора - filter: drop-shadow(0px 0px 0px #ccc9dc); transform: perspective(1000px) rotateY(0deg) rotateX(0deg);

filter: drop-shadow() создаёт тень на основе контента, в данном примере размытие тени установлено в 0px, поэтому тень полностью повторяет контуры элементов карточки

transform со значением perspective(1000px) создает имитацию 3D-пространства

Для плавного изменения значений используем transition: 0.4s ease-out;

При наведении на карточку меняем её значения на filter: drop-shadow(24px 12px 0px #ccc9dc); transform: perspective(1000px) rotateY(-16deg) rotateX(8deg);

Документация по transform: perspective(); - https://developer.mozilla.org/en-US/docs/Web/CSS/transform-function/perspective()

Документация по filter: drop-shadow(); - https://developer.mozilla.org/en-US/docs/Web/CSS/filter-function/drop-shadow()


Для zoom эффекта изображения используем object-fit: cover; и transform: scale();, подробнее в статье Zoom-эффект - масштаб и позиционирование изображения

Для изменения формы изображения используем свойство clip-path

В данном примере задаем начальные значения clip-path: polygon(0% 0%, 100% 0, 100% 50%, 100% 100%, 0% 100%);

И при наведении курсора на карточку меняем значения на clip-path: polygon(0% 0%, 88% 0%, 100% 50%, 88% 100%, 0% 100%);

Для плавного изменения значений также используем transition: 0.4s ease-out;

Чтобы посмотреть, каких фигур можно добиться данным свойством, можно воспользоваться генератором CSS clip-path maker

Документация по clip-path - https://developer.mozilla.org/en-US/docs/Web/CSS/clip-path


body {
  font-family: 'Roboto', sans-serif;
  background: #fff;
  position: relative;
}
.container {
  max-width: 1140px;
  padding: 0 16px;
  margin: 0 auto;
}
.section {
  padding: 64px 0;
}
.section__wrapper {
  display: grid;
  grid-template-columns: 1fr 1fr;
  grid-gap: 32px;
  gap: 32px;
}
.card {
  width: 100%;
  transition: 0.4s ease-out;
  -webkit-filter: drop-shadow(0px 0px 0px #ccc9dc);
  filter: drop-shadow(0px 0px 0px #ccc9dc);
  transform: perspective(1000px) rotateY(0deg) rotateX(0deg);
}
.card a {
  text-decoration: none;
}
.card__image {
  overflow: hidden;
  height: 400px;
  -webkit-clip-path: polygon(0% 0%, 100% 0, 100% 50%, 100% 100%, 0% 100%);
  clip-path: polygon(0% 0%, 100% 0, 100% 50%, 100% 100%, 0% 100%);
  transition: 0.4s ease-out;
}
.card__image img {
  display: block;
  -o-object-fit: cover;
  object-fit: cover;
  width: 100%;
  height: 100%;
  transition: 0.4s ease-out;
}
.card__body {
  padding: 16px;
}
.card__title span {
  display: inline-block;
  color: #121212;
  font-size: 16px;
  margin-bottom: 4px;
  transition: 0.4s ease-out;
  font-family: 'Lora', serif;
}
.card__price {
  text-align: right;
}
.card__price span {
  display: inline-block;
  font-family: 'Lora', serif;
  margin-bottom: 12px;
  font-size: 16px;
  color: #121212;
  transition: 0.4s ease-out;
}
.card__colors {
  display: grid;
  grid-template-columns: repeat(auto-fill, 16px);
  grid-gap: 4px;
  gap: 4px;
  transition: 0.4s ease-out;
}
.card__color {
  --color: transparent;
  width: 16px;
  height: 16px;
  display: block;
  border-radius: 8px;
  background-color: var(--color);
}
.card:hover {
  -webkit-filter: drop-shadow(24px 12px 0px #ccc9dc);
  filter: drop-shadow(24px 12px 0px #ccc9dc);
  transform: perspective(1000px) rotateY(-16deg) rotateX(8deg);
}
.card:hover .card__image {
  -webkit-clip-path: polygon(0% 0%, 88% 0%, 100% 50%, 88% 100%, 0% 100%);
  clip-path: polygon(0% 0%, 88% 0%, 100% 50%, 88% 100%, 0% 100%);
}
.card:hover .card__image img {
  transform: scale(1.2);
}
@media (max-width: 767.98px) {
  .section__wrapper {
    grid-template-columns: 1fr;
  }
}


Получаем следующий результат


Архив с примером можно скачать по ссылке



Буду рад, если статья оказалась полезной

Спасибо за ваше внимание и уделённое время!