Vue3中如何让目标元素在复合函数内跟随鼠标移动?
实现Vue3(Nuxt+VueUse)元素跟随鼠标移动的最优方案
原代码问题分析
- 直接修改style无效:目标元素未设置
position属性(如absolute/fixed),left/top样式仅对定位元素生效;且useMouseInElement返回的是鼠标在目标元素内部的坐标,而非视口全局坐标,用于定位会导致位置偏移。 - anime.js方案异常:每次鼠标移动都创建新的动画实例,多个动画同时执行会导致冲突,出现卡顿、位置跳变等问题。
最优方案:纯VueUse+原生样式(轻量高效)
无需额外依赖,直接通过VueUse获取鼠标坐标,结合定位样式实现流畅跟随,还可按需添加居中逻辑提升体验。
优化后的复合函数
import { onMounted } from 'vue'; import { useMouse, useEventListener } from '@vueuse/core'; export function useStickToMouse(target) { onMounted(() => { if (!target.value) return; // 获取视口内的鼠标坐标 const { x: mouseX, y: mouseY } = useMouse(); // 计算元素中心偏移量(让鼠标对准元素中心) const rect = target.value.getBoundingClientRect(); const offsetX = rect.width / 2; const offsetY = rect.height / 2; function moveTarget() { // 更新元素定位 target.value.style.left = `${mouseX.value - offsetX}px`; target.value.style.top = `${mouseY.value - offsetY}px`; } // 绑定鼠标移动事件 useEventListener(window, 'mousemove', moveTarget); // 初始位置直接定位到鼠标处 moveTarget(); }); }
跟随组件实现(添加定位样式)
<template> <div ref="target" class="follow-mouse-wrapper"> <slot></slot> </div> </template> <script setup> import { ref } from 'vue'; import { useStickToMouse } from '@/composables/useStickToMouse'; const target = ref(null); useStickToMouse(target); </script> <style scoped> .follow-mouse-wrapper { position: fixed; /* 根据需求切换为absolute */ pointer-events: none; /* 可选:避免元素遮挡鼠标事件,需要交互则删除 */ } </style>
使用方式
<follow-mouse-wrapper> <component-to-move></component-to-move> </follow-mouse-wrapper>
可选:平滑跟随(基于VueUse过渡)
如果需要平滑移动效果,推荐使用VueUse的useTransition替代anime.js,避免动画堆积:
import { onMounted, computed, watch } from 'vue'; import { useMouse, useTransition } from '@vueuse/core'; export function useStickToMouse(target) { onMounted(() => { if (!target.value) return; const { x: mouseX, y: mouseY } = useMouse(); const rect = target.value.getBoundingClientRect(); const offsetX = rect.width / 2; const offsetY = rect.height / 2; // 计算目标位置 const targetX = computed(() => mouseX.value - offsetX); const targetY = computed(() => mouseY.value - offsetY); // 启用平滑过渡 const [x, y] = useTransition([targetX, targetY], { duration: 100, easing: 'easeOutQuad' }); // 监听过渡值更新样式 watch([x, y], ([newX, newY]) => { target.value.style.left = `${newX}px`; target.value.style.top = `${newY}px`; }); }); }
内容的提问来源于stack exchange,提问作者Miguel
相关产品推荐
相关产品推荐

