You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Vue3中实现鼠标悬浮时单个数组元素的抖动过渡效果

解决Vue3+TypeScript中单个卡片悬浮抖动的问题

你的问题出在使用了全局的shaking变量控制所有卡片的动画状态,所以鼠标悬浮时所有元素都会触发抖动。要实现单个元素抖动,需要跟踪当前悬浮的元素标识,只给对应元素添加动画类。

以下是具体修改方案:

修改步骤

  • 替换全局shaking变量为shakingItemId,用来存储当前需要抖动的元素ID
  • 调整hoverShaking方法,接收当前元素的ID作为参数,控制仅该元素触发抖动
  • 在模板中,根据当前元素ID是否匹配shakingItemId来决定是否添加动画类
  • 给鼠标事件传递当前元素的ID

修改后的完整代码

Script 部分

<script setup lang="ts">
import Dashboard from '../components/Dashboard.vue'
import type { IOption } from '../interfaces/IOption'
import { ref } from 'vue'

const props = defineProps<{}>()
const emits = defineEmits<{}>()

const notificaciones = ref(14)
const prueba = ref(
  'Lorem ipsum dolor sit amet consectetur, adipisicing elit. Corporis numquam, dicta corrupti harum aliquid doloremque dignissimos. Provident ab sed consequatur commodi, aspernatur adipisci dolor veritatis rerum quidem nulla dolore nemo'
)

const optionsTest: IOption[] = [
  {
    id: 1,
    name: 'GitLab',
    desc: 'Plataforma para control de versiones',
    url: 'https://gitlab.com/users/sign_in',
    icono: 'http://localhost:5173/src/assets/gitlab.png',
    noti: 0
  },
  {
    id: 2,
    name: 'GL',
    desc: 'Descripcion de la opcion 2',
    url: '',
    icono: '',
    noti: 0
  },
  {
    id: 3,
    name: 'Swagger',
    desc: `Estructuración de API's`,
    url: 'https://swagger.io/',
    icono: 'http://localhost:5173/src/assets/swagger.png',
    noti: 0
  },
  {
    id: 4,
    name: `Cola de Email's`,
    desc: 'Emails',
    url: '',
    icono: 'http://localhost:5173/src/assets/email.png',
    noti: 0
  },
  {
    id: 5,
    name: 'Cola de Documentos',
    desc: 'Documentos',
    url: '',
    icono: 'http://localhost:5173/src/assets/documentos2.png',
    noti: 0
  },
  {
    id: 6,
    name: 'W2P',
    desc: 'Web to project',
    url: 'https://call.tpaas.es/?lang=es-ES',
    icono: '',
    noti: 0
  },
  {
    id: 7,
    name: 'Reservas',
    desc: 'Reservas',
    url: '',
    icono: '',
    noti: 0
  },
  {
    id: 8,
    name: 'Servicios BBDD',
    desc: 'Bases de datos',
    url: '',
    icono: 'http://localhost:5173/src/assets/BBDD.png',
    noti: 0
  }
]

// 改为跟踪当前需要抖动的元素ID
const shakingItemId = ref<number | null>(null)

function hoverShaking(itemId: number): void {
  shakingItemId.value = itemId
  setTimeout(() => {
    shakingItemId.value = null
  }, 750)
}
</script>

Template 部分

<template>
  <v-container class="container" :fluid="true">
    <v-row no-gutters justify="center">
      <!-- 仅当当前元素ID等于shakingItemId时添加动画类 -->
      <v-col cols="auto" v-for="(item, index) in optionsTest" :class="{ 'apply-shake': shakingItemId === item.id }" :key="item.id">
        <Dashboard
          :item="item"
          :notificaciones="notificaciones"
          @mouseover="hoverShaking(item.id)"
        ></Dashboard>
      </v-col>
    </v-row>
  </v-container>
</template>

Style 部分(无需修改)

<style>
.cardOption {
  text-align: center;
  margin: 5px;
  margin-left: 35px;
  margin-top: 5px;
  margin-bottom: 5px;
  background-color: #2a5e92;
  color: white;
}
.hover-enter-active {
  transition: all 0.5s ease-in-out;
}

.prueba {
  margin: 10px;
  text-align: center;
}
@keyframes shake {
  10%,
  90% {
    transform: translate3d(-1px, 0, 0);
  }

  20%,
  80% {
    transform: translate3d(2px, 0, 0);
  }

  30%,
  50%,
  70% {
    transform: translate3d(-4px, 0, 0);
  }

  40%,
  60% {
    transform: translate3d(4px, 0, 0);
  }
}
.apply-shake {
  animation: shake 0.82s cubic-bezier(0.36, 0.07, 0.19, 0.97) both;
  transform: translate3d(0, 0, 0);
}
.bounce:hover {
  animation-name: bounce;
  animation-timing-function: ease;
}
</style>

额外说明

  • 把v-for的key从index改为item.id,符合Vue最佳实践,避免元素复用导致的异常
  • shakingItemId仅存储当前需要抖动的元素ID,定时器结束后重置为null,确保动画只触发一次

内容的提问来源于stack exchange,提问作者francesco14

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.29 08:52:57