Voltar

Efeito 3D no Hover

Efeito 3D no Hover

Exemplo

Phil'sosophies
Dance Until Your Feet Hurt, Sing Until Your Lungs Hurt, Act Until You're William Hurt.

Vue tem uma super biblioteca repleta de composables, a @vueuse. E eles são muito úteis de fato. Facilitam o trabalho pra gente em tarefas rotineiras na criação dos nossos componentes.

Exemplo disso, é a criação de um componente com esse efeito 3D no hover.

Usaremos o composable useMouseInElement. Ele nos dá informações sobre a posição do mouse em relação a um elemento na tela. Então é muito útil quando queremos criar componentes que interagem com o mouse por exemplo.

Então vamos começar.

Instalação

Antes de tudo, precisamos instalar o @vueuse/core então:

yarn add @vueuse/core

Componente

Feita instalação, vamos ao nosso componente que aqui, vamos chamar de Card.vue

<script setup lang="ts">
</script>

<template>
  <div class="card">
    <div class="card-title">
      Phil'sosophies
    </div>
    <div class="card-body">
      Dance Until Your Feet Hurt, Sing Until Your Lungs Hurt, Act Until You're William Hurt.
    </div>
  </div>
</template>

<style scoped>
.card {
  padding: 12px;
  background-color: #636363;
  max-width: 300px;
  border-radius: 8px;
}

.card-title {
  font-size: 1.2em;
  font-weight: bold;
}

.card-body {
  margin-top: 24px;
}
</style>

Dada uma certa estilizada nele. Agora vamos para a implementação do efeito

A gente precisa fazer uma referência ao elemento que vai ser o target do composable então basta declarar um ref e inserir no elemento no template:

<script setup lang="ts">
import { ref } from 'vue'

const cardRef = ref(null)
</script>

<template>
  <div class="card" ref="cardRef">
   ...
  </div>
</template>

Agora só passar essa referência para o composable

<script setup lang="ts">
import { ref } from 'vue'
import { useMouseInElement } from '@vueuse/core'

const cardRef = ref(null)

const { } = useMouseInElement(cardRef)

</script>

E as informações do mouse, em relação ao elemento, que vamos usar vão ser:

<script setup lang="ts">
import { ref } from 'vue'
import { useMouseInElement } from '@vueuse/core'

const cardRef = ref(null)

const {
  elementHeight,
  elementWidth,
  elementX,
  elementY,
  isOutside
} = useMouseInElement(cardRef)

</script>

Os valores retornados pelo composable são reativos. Então se atualizam conforme a interação do mouse com o elemento alvo.

Animação

Vamos usar a propriedade transform do CSS para rotacionar o elemento nos eixos. E para isso vamos criar um valor computed que irá fazer os cálculos dos valores para a propriedade transform

Abstraia o entendimento do cálculo. Se quiser alterar o quanto é rotacionado basta mudar o valor de ROTATION

<script setup lang="ts">
import { computed, ref } from 'vue'

...

const transformElement = computed(() => {
  const ROTATION = 16
  const rX = (ROTATION / 2 - (elementY.value / elementHeight.value) * ROTATION).toFixed()
  const rY = (ROTATION / 2 - (elementX.value / elementWidth.value) * ROTATION).toFixed()

  if (isOutside.value) return ''

  return `perspective(${elementWidth.value / 2}px) rotateX(${rX}deg) rotateY(${rY}deg)`
})

</script>

Se o mouse estiver fora do elemento, isOutside.value for true, então não retornamos nenhum valor.

Agora o que a gente faz é só aplicar o estilo ao componente usando o reactive styles! Sim, podemos usar refs no <style>

<script setup lang="ts">
...
const transformElement = computed(() => {
  ...
})
</script>

<template>
...
</template>

<style scoped>
.card {
  padding: 12px;
  background-color: #636363;
  max-width: 300px;
  border-radius: 8px;
  transform: v-bind(transformElement);
  transition: transform 0.25s ease-out;
}
...
</style>

Resultado

Phil'sosophies
Dance Until Your Feet Hurt, Sing Until Your Lungs Hurt, Act Until You're William Hurt.

Código final

<script lang="ts" setup>
import { useMouseInElement } from '@vueuse/core'
import { computed, ref } from 'vue'
const cardRef = ref(null)

const { elementHeight, elementWidth, elementX, elementY, isOutside } = useMouseInElement(cardRef)

const transformElement = computed(() => {
  const ROTATION = 16
  const rX = (ROTATION / 2 - (elementY.value / elementHeight.value) * ROTATION).toFixed()
  const rY = (ROTATION / 2 - (elementX.value / elementWidth.value) * ROTATION).toFixed()

  if (isOutside.value) return ''

  return `perspective(${elementWidth.value / 2}px) rotateX(${rX}deg) rotateY(${rY}deg)`
})
</script>
<template>
  <div class="card" ref="cardRef">
    <div class="card-title">
      Phil'sosophies
    </div>
    <div class="card-body">
      Dance Until Your Feet Hurt, Sing Until Your Lungs Hurt, Act Until You're William Hurt.
    </div>
  </div>
</template>

<style scoped>
.card {
  padding: 12px;
  background-color: #636363;
  max-width: 300px;
  border-radius: 8px;
  transform: v-bind(transformElement);
  transition: transform 0.25s ease-out;
}

.card-title {
  font-size: 1.2em;
  font-weight: bold;
}

.card-body {
  margin-top: 24px;
}
</style>

E é isso!

Créditos ao LearnVue

Conteúdo extraído do vídeo: https://www.youtube.com/watch?v=AVMNjbKdU1M