如何在Vue.js站点中实现元素堆叠上升的拼贴动效?
Vue.js 实现鼠标跟随流体光晕动效方案
你提到的动效本质是基于Canvas渲染的鼠标交互效果,核心是鼠标移动时生成跟随的渐变光晕,搭配背景悬浮粒子的联动效果,在Vue项目中可以参考以下两种实现方案:
方案1:基于轻量动效库快速实现(适配Vue3)
适合需要快速上线、不需要高度自定义的场景,使用@tsparticles/vue3实现:
- 第一步安装依赖:
npm install @tsparticles/vue3 @tsparticles/slim - 第二步在入口文件注册插件:
import { createApp } from 'vue' import App from './App.vue' import Particles from "@tsparticles/vue3"; import { loadSlim } from "@tsparticles/slim"; const app = createApp(App) app.use(Particles, { init: async (engine) => { await loadSlim(engine); } }) app.mount('#app')
- 第三步在需要增加动效的页面组件中调用:
<template> <div class="page-wrapper"> <VueParticles id="particle-canvas" :options="particleConfig" /> <!-- 页面内容层级要高于Canvas,避免被遮挡 --> <div class="page-content"> 你的站点原有内容 </div> </div> </template> <script setup> const particleConfig = { interactivity: { events: { onHover: { enable: true, mode: "grab" } }, modes: { grab: { distance: 140, links: { opacity: 0.2 } } } }, particles: { color: { value: "#6366f1" // 可替换为你站点的主色调 }, links: { color: "#6366f1", distance: 150, enable: true, opacity: 0.1, width: 1 }, move: { enable: true, speed: { min: 1, max: 3 }, }, opacity: { value: { min: 0.1, max: 0.5 } }, size: { value: { min: 1, max: 3 } } }, background: { color: "#0f172a" // 和你站点背景色保持一致即可 } } </script> <style scoped> .page-wrapper { position: relative; min-height: 100vh; } #particle-canvas { position: absolute; top: 0; left: 0; width: 100%; height: 100%; z-index: 0; } .page-content { position: relative; z-index: 1; } </style>
方案2:原生Canvas手写实现(无额外依赖,性能更高)
适合需要高度自定义动效参数的场景,核心逻辑如下:
<template> <div class="canvas-wrapper"> <canvas ref="canvasRef" id="mouse-effect"></canvas> <div class="page-content">你的站点原有内容</div> </div> </template> <script setup> import { ref, onMounted, onUnmounted } from 'vue' const canvasRef = ref(null) let ctx = null let animationId = null // 鼠标参数配置 let mouse = { x: 0, y: 0, radius: 150 } let particles = [] // 粒子类 class Particle { constructor() { this.x = Math.random() * window.innerWidth this.y = Math.random() * window.innerHeight this.size = Math.random() * 3 + 1 this.speedX = Math.random() * 1 - 0.5 this.speedY = Math.random() * 1 - 0.5 } update() { this.x += this.speedX this.y += this.speedY // 粒子边界回弹逻辑 if (this.x < 0 || this.x > window.innerWidth) this.speedX *= -1 if (this.y < 0 || this.y > window.innerHeight) this.speedY *= -1 // 计算鼠标距离,靠近时放大 const dx = this.x - mouse.x const dy = this.y - mouse.y const distance = Math.sqrt(dx * dx + dy * dy) this.size = distance < mouse.radius ? Math.min(8, this.size + 0.3) : Math.max(1, this.size - 0.1) } draw() { ctx.fillStyle = 'rgba(99, 102, 241, 0.6)' ctx.beginPath() ctx.arc(this.x, this.y, this.size, 0, Math.PI * 2) ctx.fill() } } // 初始化Canvas const initCanvas = () => { const canvas = canvasRef.value ctx = canvas.getContext('2d') canvas.width = window.innerWidth canvas.height = window.innerHeight // 生成初始粒子,数量可按需调整 particles = Array.from({ length: 150 }, () => new Particle()) renderLoop() } // 渲染循环 const renderLoop = () => { ctx.clearRect(0, 0, canvasRef.value.width, canvasRef.value.height) // 绘制鼠标渐变光晕 const gradient = ctx.createRadialGradient(mouse.x, mouse.y, 0, mouse.x, mouse.y, mouse.radius) gradient.addColorStop(0, 'rgba(99, 102, 241, 0.15)') gradient.addColorStop(1, 'rgba(99, 102, 241, 0)') ctx.fillStyle = gradient ctx.fillRect(0, 0, canvasRef.value.width, canvasRef.value.height) // 更新绘制所有粒子 particles.forEach(p => { p.update() p.draw() }) animationId = requestAnimationFrame(renderLoop) } // 事件监听 const handleMouseMove = (e) => { mouse.x = e.clientX mouse.y = e.clientY } const handleResize = () => { canvasRef.value.width = window.innerWidth canvasRef.value.height = window.innerHeight } onMounted(() => { initCanvas() window.addEventListener('mousemove', handleMouseMove) window.addEventListener('resize', handleResize) }) onUnmounted(() => { window.removeEventListener('mousemove', handleMouseMove) window.removeEventListener('resize', handleResize) cancelAnimationFrame(animationId) }) </script> <style scoped> .canvas-wrapper { position: relative; width: 100vw; min-height: 100vh; background: #0f172a; overflow: hidden; } #mouse-effect { position: absolute; top: 0; left: 0; z-index: 0; } .page-content { position: relative; z-index: 1; color: #fff; } </style>
注意事项
- 如果是Vue2项目,把上述Composition API的写法替换为Options API即可,核心动效逻辑完全通用
- 可自行调整粒子数量、颜色、光晕半径、移动速度等参数,匹配你站点的设计风格
- 移动端可以降低粒子数量避免性能损耗
内容的提问来源于stack exchange,提问作者larkx
相关产品推荐
相关产品推荐

