Skip to content

Animated Tooltip

A cool tooltip that reveals on hover, follows mouse pointer.

Installation

Install the following dependencies

bash
pnpm add motion-v

Copy and paste the following code into your project:

vue
<script setup lang='ts'>
import { AnimatePresence, motion, useMotionValue, useSpring, useTransform } from 'motion-v'
import { ref } from 'vue'
export interface Items {
  id: number
  name: string
  designation: string
  image: string
}
const props = defineProps<{
  items: Items[]
}>()

const hoveredIndex = ref<number | null>(null)
const springConfig = { stiffness: 100, damping: 5 }
const x = useMotionValue(0)
const rotate = useSpring(
  useTransform(x, [-100, 100], [-45, 45]),
  springConfig,
)
// translate the tooltip
const translateX = useSpring(
  useTransform(x, [-100, 100], [-50, 50]),
  springConfig,
)
function handleMouseMove(event: any) {
  const halfWidth = event.target.offsetWidth / 2
  x.set(event.offsetX - halfWidth)
}
</script>

<template>
  <div v-for="(item, idx) in props.items" :key="idx">
    <div
      :key="item.name" class="group relative -mr-4" @mouseenter="() => hoveredIndex = item.id"
      @mouseleave="() => hoveredIndex = null"
    >
      <AnimatePresence mode="popLayout">
        <div v-if="hoveredIndex === item.id">
          <motion.div
            :initial="{
              opacity: 0, y: 20, scale: 0.6,
            }" :animate="{
              opacity: 1,
              y: 0,
              scale: 1,
              transition: {
                type: 'spring',
                stiffness: 260,
                damping: 10,
              },
            }" :exit="{
              opacity: 0, y: 20, scale: 0.6,
            }" :style="{
              translateX,
              rotate,
              whiteSpace: 'nowrap',
            }"
            class="absolute -top-16 -left-1/2 z-50 flex -translate-x-1/2 flex-col items-center justify-center rounded-md bg-black px-4 py-2 text-xs shadow-xl"
          >
            <div
              class="absolute inset-x-10 -bottom-px z-30 h-px w-[20%] bg-gradient-to-r from-transparent via-emerald-500 to-transparent"
            />
            <div
              class="absolute -bottom-px left-10 z-30 h-px w-[40%] bg-gradient-to-r from-transparent via-sky-500 to-transparent"
            />
            <div class="relative z-30 text-base font-bold text-white">
              {{ item.name }}
            </div>
            <div class="text-xs text-white">
              {{ item.designation }}
            </div>
          </motion.div>
        </div>
      </AnimatePresence>
      <img
        :src="item.image" :alt="item.name" height="100" width="100" class="relative !m-0 h-14 w-14 rounded-full border-2 border-white object-cover object-top !p-0 transition duration-500 group-hover:z-30 group-hover:scale-105"
        @mousemove="handleMouseMove"
      >
    </div>
  </div>
</template>

Props

PropTypeDescriptionDefault
itemsArray<{id: number, name: string, designation: string, image: string}>An array of objects, each representing an item. Each object in the array should have the following properties: id, name, designation, image.-

Released under the MIT License.