Анимация раскрытия веера карт

Подскажите, пожалуйста, как сделать анимацию на js при наведении на блоки. Начальное положение блоков на рисунке 1, конечное, приблизительно, на рисунке 2.введите сюда описание изображения

введите сюда описание изображения

Спасибо.


Ответы (2 шт):

Автор решения: hu-fo

Раз

const deck = document.querySelector('.deck')
const cards = document.querySelectorAll('.deck__card')
const middle = Math.floor(cards.length / 2)

const animationDuration = 0.7
let isRunning = false

deck.addEventListener('mouseenter', function(e) {
  if (isRunning) return
  isRunning = true

  cards.forEach((c, i) => {
    c.style.transition = animationDuration + 's'
    c.style.transform = `translateX(240px) translateY(15px) rotateZ(30deg)`
  })

  setTimeout(() => {
    cards.forEach((c, i) => {
      const tx = i * 50 + Math.random() * 20
      const e = i == 0 || i == cards.length - 1 ? 15 : 0
      const ty = i < middle ? -i * 14 + e : (i - cards.length + 1) * 14 + e
      const rz = i * 7

      setData(c, 240 - tx, ty, 30 - rz)
      transform(c, c.dataset.tx, c.dataset.ty, c.dataset.rz, 0)
    })
  }, animationDuration * 1000)

  setTimeout(() => {
    cards.forEach((c, i) => {
      setTimeout(() => {
        const tx = +c.dataset.tx - (10 + Math.random() * 40)
        const ty = +c.dataset.ty + (Math.random() * 10)
        transform(c, tx, ty, c.dataset.rz, -180)
        if (i == cards.length - 1) isRunning = false
      }, 80 * i)
    })
  }, (animationDuration + 0.05) * 1000 * 2)
})

deck.addEventListener('mouseleave', mouseLeaveHandler)

function mouseLeaveHandler(e) {
  if (isRunning) return

  cards.forEach(c => transform(c, +c.dataset.tx, c.dataset.ty, c.dataset.rz, 0))

  setTimeout(
    () => cards.forEach((c, i) => transform(c, 0, 0, 0, 0)),
    animationDuration * 1000
  )
}

function transform(el, tx, ty, rz, ry = 0) {
  el.style.transform = `
    translateX(${tx}px)
    translateY(${ty}px)
    rotateZ(${rz}deg)
    rotateY(${ry}deg)`
}

function setData(el, tx, ty, rz) {
  el.dataset.tx = tx
  el.dataset.ty = ty
  el.dataset.rz = rz
}
* {
  margin: 0;
  padding: 0;
}

body {
  height: 100vh;
  display: grid;
  align-items: center;
  justify-content: center;
  perspective: 1000px;
  overflow: hidden;
}

.deck {
  height: 300px;
  width: 200px;
  display: grid;
  list-style: none;
}

.deck__card {
  grid-row: 1 / 1;
  grid-column: 1 / 1;
  display: grid;
  transform-style: preserve-3d;
  box-shadow: 0 1px 3px rgba(150, 150, 150, 0.4), 0 1px 2px rgba(150, 150, 150, 0.4);
}

.deck__card>* {
  grid-row: 1 / 1;
  grid-column: 1 / 1;
  backface-visibility: hidden;
  border-radius: 4px;
  background-size: 80%;
  background-repeat: no-repeat;
  background-position: center;
}

.deck__card__front {
  background-color: #E0E0E0;
  background-image: url(https://cdn.pixabay.com/photo/2017/07/12/18/38/treasure-2497813_960_720.png);
  transform: rotateY(-180deg);
}

.deck__card__back {
  background-color: #212121;
  background-image: url(https://static.warthunder.ru/upload/image/!2015/August/roger_01.png);
}
<ul class="deck">
  <li class="deck__card">
    <div class="deck__card__front"></div>
    <div class="deck__card__back"></div>
  </li>

  <li class="deck__card">
    <div class="deck__card__front"></div>
    <div class="deck__card__back"></div>
  </li>

  <li class="deck__card">
    <div class="deck__card__front"></div>
    <div class="deck__card__back"></div>
  </li>

  <li class="deck__card">
    <div class="deck__card__front"></div>
    <div class="deck__card__back"></div>
  </li>

  <li class="deck__card">
    <div class="deck__card__front"></div>
    <div class="deck__card__back"></div>
  </li>

  <li class="deck__card">
    <div class="deck__card__front"></div>
    <div class="deck__card__back"></div>
  </li>

  <li class="deck__card">
    <div class="deck__card__front"></div>
    <div class="deck__card__back"></div>
  </li>

  <li class="deck__card">
    <div class="deck__card__front"></div>
    <div class="deck__card__back"></div>
  </li>

  <li class="deck__card">
    <div class="deck__card__front"></div>
    <div class="deck__card__back"></div>
  </li>

  <li class="deck__card">
    <div class="deck__card__front"></div>
    <div class="deck__card__back"></div>
  </li>
</ul>

Два

:root {
  --card-height: 250px;
  --card-width: 150px;
}

* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

body {
  height: 100vh;
  display: flex;
  align-items: center;
  justify-content: center;
  perspective: 1000px;
}

ul {
  display: flex;
  list-style: none;
}

li {
  height: var(--card-height);
  width: var(--card-width);
  margin: 5px;
  border-radius: 4px;
  background-color: #EEEEEE;
  background-image: url(https://cdn131.picsart.com/276403626017211.png?type=webp&to=min&r=1024);
  background-position: center;
  background-size: 140%;
  background-repeat: no-repeat;
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.12), 0 1px 2px rgba(0, 0, 0, 0.24);
  transition: 0.5s;
  transform-origin: bottom;
}

li:nth-child(1) {
  transform: translateX(calc(var(--card-width) * 3 - 160%)) translateY(5%) rotateZ(-30deg);
  transition-delay: 0s;
}

li:nth-child(2) {
  transition-delay: 0.02s;
  transform: translateX(calc(var(--card-width) * 2 - 110%)) translateY(-12%) rotateZ(-20deg);
}

li:nth-child(3) {
  transition-delay: 0.04s;
  transform: translateX(calc(var(--card-width) * 1 - 60%)) translateY(-22%) rotateZ(-10deg);
}

li:nth-child(4) {
  transition-delay: 0.06s;
  transform: translateX(calc(var(--card-width) * -1 + 60%)) translateY(-22%) rotateZ(10deg);
}

li:nth-child(5) {
  transition-delay: 0.08s;
  transform: translateX(calc(var(--card-width) * -2 + 110%)) translateY(-12%) rotateZ(20deg);
}

li:nth-child(6) {
  transition-delay: 0.1s;
  transform: translateX(calc(var(--card-width) * -3 + 160%)) translateY(5%) rotateZ(30deg);
}

ul:hover>li {
  transform: translateX(0) translateY(0) rotateZ(0);
}
<ul>
  <li></li>
  <li></li>
  <li></li>
  <li></li>
  <li></li>
  <li></li>
</ul>

→ Ссылка
Автор решения: Stranger in the Q

Вот вариант с переменными:

.hand {
  transform: translate(calc(50vw - 200px), calc(50vh - 50px));
  width: 400px;
  height: 100px;
}
.hand div {
  transition: 0.2s;
}
.hand div:hover {
  transform:translate(0,-9px);
}
.hand div:after, .hand div:before {
    content: '';
    position: absolute;
    background: radial-gradient(circle at center, wheat, steelblue);
    border: 1px solid;
    height: 76px;
    width: 50px;
    --rot: 0deg;
    transform: translate(175px) rotatez(calc(var(--i) * 15deg)) rotatey(var(--rot));
    transform-origin: 50% 200%;
    transition: 0.5s;
    backface-visibility:hidden;
}
.hand div:after  {
    content: attr(data-card);
    line-height: 76px;
    font-size: 30px;
    text-align: center;
    background: wheat;
    --rot: 180deg;
}
.hand:hover div:after, .hand:hover div:before {
  transform: translate(175px) translate(calc(var(--i) * 55px), 0) 
      rotatez(0) rotatey(calc(180deg + var(--rot)));
}
<div class="hand">
  <div data-card="♠️A" style="--i:-2.5"></div>
  <div data-card="♠️K" style="--i:-1.5"></div>
  <div data-card="♠️Q" style="--i:-0.5"></div>
  <div data-card="♠️J" style="--i: 0.5"></div>
  <div data-card="♠️10" style="--i: 1.5"></div>
  <div data-card="♠️9" style="--i: 2.5"></div>
</div>

→ Ссылка