效果概述
本组件实现了一个跟随鼠标移动的粒子轨迹效果,包含以下特性:
- 彩色粒子的生成与消散动画
- 粒子数量上限控制(最大100个)
- 自适应窗口大小变化
- 层级置顶且不阻挡交互
- 流畅的粒子运动效果

组件结构
模板部分
vue
// MouseTrail.vue
<template>
<canvas ref="canvas" class="mouse-trail"></canvas>
</template>
script部分
javascript
<script>
import { ref, onMounted, onUnmounted } from 'vue'
export default {
name: 'MouseTrail',
setup() {
const canvas = ref(null)
const particles = ref([])
const maxParticles = 100
let animationFrameId = null
const initCanvas = () => {
canvas.value.width = window.innerWidth
canvas.value.height = window.innerHeight
}
const draw = () => {
try {
const ctx = canvas.value.getContext('2d')
ctx.clearRect(0, 0, canvas.value.width, canvas.value.height)
// 在主 Canvas 上绘制尾随粒子
particles.value = particles.value.filter((particle) => {
particle.update()
particle.draw(ctx)
return particle.alpha > 0
})
// 循环调用
animationFrameId = requestAnimationFrame(draw)
} catch (error) {
console.error('Error in draw loop:', error)
}
}
class Particle {
constructor(x, y) {
this.x = x
this.y = y
this.vx = (Math.random() - 0.5) * 2
this.vy = (Math.random() - 0.5) * 2
this.alpha = 1
this.size = Math.random() * 5 + 3
this.color = `rgba(255, ${Math.random() * 100 + 155}, 180, ${
this.alpha
})`
}
update() {
this.x += this.vx
this.y += this.vy
this.alpha -= 0.01
this.size -= 0.02
}
draw(ctx) {
ctx.save()
ctx.beginPath()
ctx.arc(this.x, this.y, this.size, 0, Math.PI * 2)
ctx.fillStyle = this.color
ctx.fill()
ctx.restore()
}
}
const handleMouseMove = (event) => {
const rect = canvas.value.getBoundingClientRect()
const x = event.clientX - rect.left
const y = event.clientY - rect.top
if (particles.value.length < maxParticles) {
particles.value.push(new Particle(x, y))
} else {
particles.value.shift() // 移除最旧的粒子
particles.value.push(new Particle(x, y))
}
}
onMounted(() => {
initCanvas()
window.addEventListener('mousemove', handleMouseMove)
window.addEventListener('resize', initCanvas)
draw()
})
onUnmounted(() => {
window.removeEventListener('mousemove', handleMouseMove)
window.removeEventListener('resize', initCanvas)
if (animationFrameId) {
cancelAnimationFrame(animationFrameId)
}
})
return {
canvas
}
}
}
</script>
css部分
scss
<style scoped>
.mouse-trail {
position: fixed;
top: 0;
left: 0;
pointer-events: none;
z-index: 9999;
}
</style>
首页引入
vue
<template>
<div >
<mouse-trail></mouse-trail>
<NuxtLoadingIndicator />
<NuxtLayout>
<NuxtPage />
</NuxtLayout>
</div>
</template>