Files
imen_pishghaman/apps/web/app/components/layout/Cursor.vue
T

53 lines
1.6 KiB
Vue
Raw Normal View History

<script setup lang="ts">
import { motion, useMotionValue, useSpring } from "motion-v";
const x = useMotionValue(-100);
const y = useMotionValue(-100);
const sx = useSpring(x, { stiffness: 550, damping: 45, mass: 0.4 });
const sy = useSpring(y, { stiffness: 550, damping: 45, mass: 0.4 });
const hover = ref(false);
const visible = ref(false);
const move = (e: MouseEvent) => {
x.set(e.clientX);
y.set(e.clientY);
visible.value = true;
};
const over = (e: MouseEvent) => {
const t = e.target as HTMLElement | null;
hover.value = !!t?.closest?.(
"a, button, input, textarea, select, label, [data-hover]",
);
};
onMounted(() => {
window.addEventListener("mousemove", move, { passive: true });
window.addEventListener("mouseover", over, { passive: true });
});
onBeforeUnmount(() => {
window.removeEventListener("mousemove", move);
window.removeEventListener("mouseover", over);
});
</script>
<template>
<div class="pointer-events-none fixed inset-0 z-[120] hidden [@media(pointer:fine)]:block">
<motion.div :style="{ x: sx, y: sy }" class="absolute -left-4 -top-4 size-8">
<motion.div
:animate="{
scale: hover ? 1.9 : 1,
opacity: visible ? 1 : 0,
rotate: hover ? 45 : 0,
}"
:transition="{ type: 'spring', stiffness: 320, damping: 24 }"
class="size-8 rounded-md border-2 border-accent/80"
/>
<div
:class="`absolute inset-0 m-auto size-1 rounded-full bg-accent transition-opacity duration-300 ${
visible ? 'opacity-100' : 'opacity-0'
}`"
/>
</motion.div>
</div>
</template>