使用Vue3实现简单的鼠标跟随效果

 更新时间:2025年09月08日 16:37:04   作者:Xiecj  
鼠标跟随效果是一种能显著提升页面交互性、增加动态感与趣味性的常见方式,本文将使用Vue3实现简单的鼠标跟随效果,希望对大家有所帮助

1. 创建组件基本结构

首先,创建一个 Vue3 组件,我们把它命名为 PageCursor.vue。基本结构如下:

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

const props = withDefaults(defineProps<{
  hideCursorSelector?: string | string[]
}>(), {
  hideCursorSelector: '.hide-page-cursor'
})

const cursor = ref<HTMLElement | null>(null)
const cursorType = ref('auto')
const cursorState = ref('')

onMounted(() => {})

onUnmounted(() => {})
</script>

<template>
  <div
    ref="cursor"
    class="page-cursor"
    :class="[cursorType, cursorState]"
  ></div>
</template>

<style lang="scss" scoped>
.page-cursor {
  --cursor-size: 20px;
  position: fixed;
  z-index: 9999;
  top: calc(-1 * var(--cursor-size) / 2);
  left: calc(-1 * var(--cursor-size) / 2);
  width: var(--cursor-size);
  height: var(--cursor-size);
  border-radius: 50%;
  backdrop-filter: invert(100%);
  pointer-events: none;
  opacity: 0;
}
</style>

在组件中,我们定义了 props 对象、三个响应式对象和一个 page-cursor 样式类

props 对象用于接收参数。使用 props 传参可以在外部引用组件时控制组件的样式或行为,这里我们只定义了一个 hideCursorSelector 参数用于设置隐藏光标这个行为。

三个响应式对象分别是:

  • cursor:跟随鼠标运动的光标元素。
  • cursorType:光标的类型。
  • cursorState:光标的状态。

page-cursor 样式类:

  • --cursor-size:主要是设置光标的大小,后续有多个地方会用到,所以将其定义为 CSS 变量。
  • topleftwidthheight:基于 --cursor-size 变量进行位置和大小的设置。
  • backdrop-filter:将其值设置为 invert(100%) 为光标后面区域添加反色效果。
  • pointer-events:将其值设置为 none 来禁用光标的指针事件,使其不会影响页面上其他元素的交互。

2. 添加鼠标响应事件

添加鼠标响应事件(移动、按下、弹起)并在组件挂载时注册事件,在组件卸载时移除事件:

function onMousemove() {}

function onMousedown() {}

function onMouseup() {}

onMounted(() => {
  document.addEventListener('mousemove', onMousemove)
  document.addEventListener('mousedown', onMousedown)
  document.addEventListener('mouseup', onMouseup)
})

onUnmounted(() => {
  document.removeEventListener('mousemove', onMousemove)
  document.removeEventListener('mousedown', onMousedown)
  document.removeEventListener('mouseup', onMouseup)
})

3. 实现具体功能

在 onMousedown 和 onMouseup 中修改光标状态:

function onMousedown() {
  cursorState.value = 'pressed'
}

function onMouseup() {
  cursorState.value = ''
}

在 onMousemove 事件中获取鼠标位置,并在 requestAnimationFrame 方法中进行更新位置:

let myReq: number = 0

function onMousemove(event: MouseEvent) {
  if(!cursor.value) return

  cancelAnimationFrame(myReq)

  const { clientX, clientY } = event
  const target = event.target as HTMLElement

  myReq = requestAnimationFrame(() => {
    const style = cursor.value!.style
    style.transform = `translate3d(${clientX}px, ${clientY}px, 0)`
    cursorType.value = getComputedStyle(target)?.cursor || 'auto'

    const hideCursorSelectorList = Array.isArray(props.hideCursorSelector)
      ? props.hideCursorSelector
      : [props.hideCursorSelector]
    const hideCursor = hideCursorSelectorList.some(item => target.closest(item) !== null)
    style.opacity = hideCursor ? '0' : '1'
    style.transition = hideCursor ? '0.2s ease-out' : '0.125s ease-out'
  })
}

在这段代码中,首先使用 cancelAnimationFrame 方法关闭之前创建的动画帧任务。然后获取当前鼠标的坐标和指向的元素。利用 requestAnimationFrame 方法在下一帧渲染前进行样式设置,以防止在同一帧内执行多次样式设置。并使用 getComputedStyle 方法获取当前鼠标指向元素的 CSS 属性,并从中获取鼠标指针的类型。

最后,将 hideCursorSelector 格式化为 hideCursorSelectorList,通过检查鼠标指向元素与 hideCursorSelectorList 匹配特定选择器且离当前元素最近的祖先元素是否存在来判断是否隐藏光标。

4. 光标的状态设置

.page-cursor {
  // 其他 page-cursor 样式

  // 鼠标光标类型为指针时
  &.pointer {
    --cursor-size: 40px;

    // 指针类型并且按下时
    &.pressed {
      --cursor-size: 20px;
    }
  }

  // 默认类型按下时
  &.pressed {
    --cursor-size: 10px;
  }
}

你还可以在不同的鼠标事件中对 cursorState 和 cursorType 进行赋值,并对 page-cursor 样式类进行更多的定义,来实现更多光标形态的展示。

5. 完整代码

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

const props = withDefaults(defineProps<{
  hideCursorSelector?: string | string[]
}>(), {
  hideCursorSelector: '.hide-page-cursor'
})

const cursor = ref<HTMLElement | null>(null)
const cursorType = ref('auto')
const cursorState = ref('')

let myReq: number = 0

function onMousemove(event: MouseEvent) {
  if(!cursor.value) return

  cancelAnimationFrame(myReq)

  const { clientX, clientY } = event
  const target = event.target as HTMLElement

  myReq = requestAnimationFrame(() => {
    const style = cursor.value!.style
    style.transform = `translate3d(${clientX}px, ${clientY}px, 0)`
    cursorType.value = getComputedStyle(target)?.cursor || 'auto'

    const hideCursorSelectorList = Array.isArray(props.hideCursorSelector)
      ? props.hideCursorSelector
      : [props.hideCursorSelector]
    const hideCursor = hideCursorSelectorList.some(item => target.closest(item) !== null)
    style.opacity = hideCursor ? '0' : '1'
    style.transition = hideCursor ? '0.2s ease-out' : '0.125s ease-out'
  })
}

function onMousedown() {
  cursorState.value = 'pressed'
}

function onMouseup() {
  cursorState.value = ''
}

onMounted(() => {
  globalThis.document.addEventListener('mousemove', onMousemove)
  globalThis.document.addEventListener('mousedown', onMousedown)
  globalThis.document.addEventListener('mouseup', onMouseup)
})

onUnmounted(() => {
  globalThis.document.removeEventListener('mousemove', onMousemove)
  globalThis.document.removeEventListener('mousedown', onMousedown)
  globalThis.document.removeEventListener('mouseup', onMouseup)
})
</script>

<template>
  <div
    ref="cursor"
    class="page-cursor"
    :class="[cursorType, cursorState]"
  ></div>
</template>

<style lang="scss" scoped>
.page-cursor {
  --cursor-size: 20px;
  position: fixed;
  z-index: 9999;
  top: calc(-1 * var(--cursor-size) / 2);
  left: calc(-1 * var(--cursor-size) / 2);
  width: var(--cursor-size);
  height: var(--cursor-size);
  border-radius: 50%;
  backdrop-filter: invert(100%);
  pointer-events: none;
  opacity: 0;

  &.pointer {
    --cursor-size: 40px;

    &.pressed {
      --cursor-size: 20px;
    }
  }

  &.pressed {
    --cursor-size: 10px;
  }
}
</style>

6.效果图

到此这篇关于使用Vue3实现简单的鼠标跟随效果的文章就介绍到这了,更多相关Vue3鼠标跟随内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Vue3中的组件通信$refs $parent使用

    Vue3中的组件通信$refs $parent使用

    这篇文章主要介绍了Vue3中的组件通信$refs $parent使用方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2025-06-06
  • Vue打包路径配置过程

    Vue打包路径配置过程

    这篇文章主要介绍了Vue打包路径配置过程,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-07-07
  • vue $attrs和$listeners的使用与区别

    vue $attrs和$listeners的使用与区别

    本文主要介绍了vue $attrs和$listeners的使用与区别,文中通过示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-02-02
  • vue.js中ref和$refs的使用及示例讲解

    vue.js中ref和$refs的使用及示例讲解

    这篇文章主要给大家介绍了关于vue.js中ref和$refs使用的相关资料,文中通过示例代码介绍的非常详细,对大家学习或者使用vue.js具有一定的参考学习价值,需要的朋友们下面来一起学习学习吧
    2019-08-08
  • VUE入门学习之事件处理

    VUE入门学习之事件处理

    这篇文章主要介绍了vue事件处理原理及过程详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2021-10-10
  • vant中的Cascader级联选择异步加载地区数据方式

    vant中的Cascader级联选择异步加载地区数据方式

    这篇文章主要介绍了vant中的Cascader级联选择异步加载地区数据方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2024-07-07
  • vue里的axios如何获取本地json数据

    vue里的axios如何获取本地json数据

    这篇文章主要介绍了vue里的axios如何获取本地json数据,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-08-08
  • vue3+vite加载本地js/json文件不能使用require浅析

    vue3+vite加载本地js/json文件不能使用require浅析

    这篇文章主要给大家介绍了关于vue3+vite加载本地js/json文件不能使用require的相关资料,require 是属于 Webpack 的方法,在v3+vite的项目里面并不适用,需要的朋友可以参考下
    2023-07-07
  • 详解如何在vue项目中使用eslint+prettier格式化代码

    详解如何在vue项目中使用eslint+prettier格式化代码

    在开发中我们需要一种能够统一团队代码风格的工具,作为强制性的规范,统一整个项目的代码风格,这篇文章主要介绍了详解如何在vue项目中使用eslint+prettier格式化代码,需要的朋友可以参考下
    2018-11-11
  • Vue浅析axios二次封装与节流及防抖的实现

    Vue浅析axios二次封装与节流及防抖的实现

    axios是基于promise的HTTP库,可以使用在浏览器和node.js中,它不是vue的第三方插件,vue-axios是axios集成到Vue.js的小包装器,可以像插件一样安装使用:Vue.use(VueAxios, axios),本文给大家介绍axios的二次封装和节流与防抖
    2022-08-08

最新评论