3D анимация с использованием CSS и Javascript - PullRequest
1 голос
/ 23 января 2020

В Steam есть игра под названием Dota 2. У них был этот раздел «карт», в котором показывались персонажи, которых вы играете. При наведении курсора на карту появляется действительно крутая анимация. Карта поднимается и поворачивается к указателю мыши.

Изображения прилагаются. Красная точка обозначает место, где находится мышь:

Внизу слева: enter image description here

Центр: enter image description here

Левый центр: enter image description here

В настоящее время у меня есть базовая c версия здесь: https://codepen.io/riza-khan/pen/mdyvEeg, но анимация не работает как предназначен.

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

const container = document.querySelector('.container')
const card = document.querySelector('.card')
let x = document.querySelector('.x-axis')
let y = document.querySelector('.y-axis')

container.addEventListener('mousemove', (e) => {
  let xCoords = e.offsetX
  let yCoords = e.offsetY

  x.innerHTML = `xCoords:${xCoords}`
  y.innerHTML = `yCoords:${yCoords}`

  card.style.transform = `rotateY(${yCoords}deg) rotateX(${xCoords}deg) scale(1.1)`
})  

container.addEventListener('mouseout', (e) => {
  card.style.transform = `rotateY(0deg) rotateX(0deg) scale(1)`
})

container.addEventListener('click', (e) => {
  console.log(e)
})
body {
  display: flex;
  height: 90vh;
  background: grey;
}

.x-axis,
.y-axis {
  position: absolute;
}

.y-axis {
  left:100px;
}

.container {
  margin: auto;
}

.card {  
  height: 500px;
  width: 300px;  
  border-radius: 10px;  
  overflow: hidden;
  transition: all 1s ease;  
  box-shadow: 10px 8px 20px -20px black;

  img {
    width: 100%;
    height: 100%;
    object-fit: cover;
  }
}

.container:hover  {

  .card {        
    transform-style: preserve-3d;
  }
}
<p class="x-axis"></p>
<p class="y-axis"></p>

<div class="container">
  <div class="card">
    <img src="https://vignette.wikia.nocookie.net/wowwiki/images/d/d9/Illidan.png/revision/latest?cb=20140503134345" alt="">
  </div>
</div>

1 Ответ

1 голос
/ 24 января 2020

Это то, что вы ищете. Чтобы придать объекту трехмерный вид, вы должны использовать свойство CSS perspective. Вот рабочий пример (также доступен на CodePen ):

const container = document.querySelector('.container')
const card = document.querySelector('.card')
const output = document.querySelector('.output')
let x = document.querySelector('.x-axis')
let y = document.querySelector('.y-axis')

container.addEventListener('mousemove', (e) => {
  let xOffset = e.offsetX
  let yOffset = e.offsetY
  let cardHeight = card.clientHeight
  let cardWidth = card.clientWidth
  let heightCenter = Math.round(cardHeight / 2)
  let widthCenter = Math.round(cardWidth / 2)
  let rotateBaseValue = 20
  let rotateXValue = (yOffset - heightCenter) / heightCenter * rotateBaseValue
  let rotateYValue = (widthCenter - xOffset) / widthCenter * rotateBaseValue

  card.style.transform = `scale(1.1) rotateX(${rotateXValue}deg) rotateY(${rotateYValue}deg)`
})

container.addEventListener('mouseout', (e) => {
  card.style.transform = ''
})
html,
body {
  margin: 0;
  padding: 0;
  height: 100%;
  width: 100vw;
  overflow: hidden;
}

body {
  display: flex;
  background: url(https://pbs.twimg.com/media/DySLFjlV4AEWf_F.jpg) no-repeat;
  background-position: center bottom;
  background-size: cover;
}

.container {
  margin: auto;
  perspective: 1000px;
}

.card {
  height: 25vw;
  width: 15vw;
  border-radius: 10px;
  overflow: hidden;
  transition: all .25s linear;
}

.card img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.container:hover .card {
  box-shadow: 10px 30px 50px -6px black;
}
<div class="container">
  <div class="card">
    <img src="https://66.media.tumblr.com/baf4a8fec55a6cb6fd7b18a7855998e4/tumblr_ply7xcI7pl1sjt61g_540.png" alt="Moonlight Cookie's Alluring Crescent Moon Costume">
  </div>
</div>

РЕДАКТИРОВАТЬ

По запросу я объясню, как я получил формулу для rotateXValue и rotateYValue.

Прежде чем приступить к этому, вам необходимо знать, что делают rotateX и rotateY. rotateX вращает элемент по горизонтальной оси (ось X), а rotateY вращает элемент по вертикальной оси (ось Y). Положительное значение на rotateX и rotateY означает, что их движения по часовой стрелке; отрицательный означает, что их движения против часовой стрелки.

Попробуйте поднять лист бумаги. Вы заметите, что если немного повернуть бумагу (неважно, сколько градусов) против часовой стрелки, как по оси x, так и по оси y, вы увидите, что бумага выглядит точно так же, как при наведении на нее. верхний правый угол карты. Попробуйте по часовой стрелке и по оси x, и по оси y, и вы увидите, что бумага указывает на вас, как при наведении курсора на левый нижний угол карты. Попробуйте все четыре комбинации разных направлений вращения.

После проведенного выше эксперимента вы можете вскоре сделать вывод, что:

  • Верхний левый угол: rotateX против часовой стрелки, rotateY по часовой стрелке
  • Верхний правый угол: rotateX и rotateY против часовой стрелки
  • Нижний левый угол: rotateX и rotateY по часовой стрелке
  • Нижний правый угол: rotateX по часовой стрелке и rotateY против часовой стрелки

Скажите, что максимальное вращение составляет 15 градусов. На оси х значение колеблется от 15 градусов до -15 градусов (слева направо). На оси Y значение находится в диапазоне от -15 градусов до 15 градусов (сверху вниз). Карта не вращается, когда вы зависаете в центре карты. Рассчитать ось Y. Центр это когда значение 0 градусов. Просто вычтите текущее смещение по оси Y со смещением по центру, и вы получите величину расстояния от смещения по центру. Преобразуйте это в дробь относительно смещения центра путем деления на значение смещения центра. Умножьте дробное значение на максимальные градусы, чтобы получить, сколько градусов повернуть. Сделайте то же самое с осью X (в этом случае вам нужно инвертировать вычитание, потому что значение варьируется от положительного до отрицательного).

PS: сделать это было очень весело. Спасибо за идею!

...