在 Vue 3 中实现 动态粒子连线背景
·
在 Vue 3 中实现 动态粒子连线背景可以通过 Canvas 或 SVG 结合 Vue 的响应式特性来实现。下面我将提供 两种实现方式,并附上详细的代码示例。
方案 1:使用 Canvas(高性能,适合大量粒子)
Canvas 适合处理大量粒子(1000+),性能较好,适合动态科技感背景。
1. 创建 Vue 3 组件 (ParticlesBackground.vue)
<template>
<div class="particles-container">
<canvas ref="canvas" class="particles-canvas"></canvas>
</div>
</template>
<script setup>
import { ref, onMounted, onUnmounted } from 'vue';
const canvas = ref(null);
let ctx;
let particles = [];
let animationId;
// 粒子类
class Particle {
constructor(x, y) {
this.x = x;
this.y = y;
this.size = Math.random() * 2 + 1;
this.speedX = Math.random() * 2 - 1;
this.speedY = Math.random() * 2 - 1;
}
update() {
this.x += this.speedX;
this.y += this.speedY;
// 边界检测,让粒子在屏幕内反弹
if (this.x < 0 || this.x > canvas.value.width) this.speedX *= -1;
if (this.y < 0 || this.y > canvas.value.height) this.speedY *= -1;
}
draw() {
ctx.fillStyle = 'rgba(255, 255, 255, 0.8)';
ctx.beginPath();
ctx.arc(this.x, this.y, this.size, 0, Math.PI * 2);
ctx.fill();
}
}
// 初始化粒子
function initParticles() {
particles = [];
const particleCount = Math.floor(canvas.value.width * canvas.value.height / 10000);
for (let i = 0; i < particleCount; i++) {
const x = Math.random() * canvas.value.width;
const y = Math.random() * canvas.value.height;
particles.push(new Particle(x, y));
}
}
// 绘制连线(距离小于一定值时连线)
function drawConnections() {
const maxDistance = 100;
for (let i = 0; i < particles.length; i++) {
for (let j = i + 1; j < particles.length; j++) {
const dx = particles[i].x - particles[j].x;
const dy = particles[i].y - particles[j].y;
const distance = Math.sqrt(dx * dx + dy * dy);
if (distance < maxDistance) {
ctx.strokeStyle = `rgba(255, 255, 255, ${1 - distance / maxDistance})`;
ctx.lineWidth = 0.5;
ctx.beginPath();
ctx.moveTo(particles[i].x, particles[i].y);
ctx.lineTo(particles[j].x, particles[j].y);
ctx.stroke();
}
}
}
}
// 动画循环
function animate() {
ctx.clearRect(0, 0, canvas.value.width, canvas.value.height);
particles.forEach(particle => {
particle.update();
particle.draw();
});
drawConnections();
animationId = requestAnimationFrame(animate);
}
// 初始化 Canvas
function initCanvas() {
canvas.value.width = canvas.value.offsetWidth;
canvas.value.height = canvas.value.offsetHeight;
ctx = canvas.value.getContext('2d');
initParticles();
animate();
}
// 监听窗口大小变化
function handleResize() {
cancelAnimationFrame(animationId);
initCanvas();
}
onMounted(() => {
initCanvas();
window.addEventListener('resize', handleResize);
});
onUnmounted(() => {
cancelAnimationFrame(animationId);
window.removeEventListener('resize', handleResize);
});
</script>
<style scoped>
.particles-container {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: -1;
background: #000;
}
.particles-canvas {
display: block;
width: 100%;
height: 100%;
}
</style>
如何使用?
-
在
App.vue或任意页面中引入:
<script setup>
import ParticlesBackground from './components/ParticlesBackground.vue';
</script>
<template>
<ParticlesBackground />
<div>你的页面内容...</div>
</template>
-
效果:
-
白色粒子随机移动
-
粒子靠近时会连线
-
背景为黑色(可修改)
-
方案 2:使用 SVG + Vue(更简单,适合少量粒子)
如果粒子较少(< 100),可以使用 SVG + CSS 动画,代码更简洁。
SVG 粒子连线实现
<template>
<div class="particles-container">
<svg ref="svg" class="particles-svg" width="100%" height="100%">
<circle
v-for="(particle, index) in particles"
:key="index"
:cx="particle.x"
:cy="particle.y"
:r="particle.size"
fill="rgba(255, 255, 255, 0.8)"
/>
<line
v-for="(line, index) in lines"
:key="'line-' + index"
:x1="line.x1"
:y1="line.y1"
:x2="line.x2"
:y2="line.y2"
stroke="rgba(255, 255, 255, 0.3)"
stroke-width="0.5"
/>
</svg>
</div>
</template>
<script setup>
import { ref, onMounted, onUnmounted } from 'vue';
const svg = ref(null);
const particles = ref([]);
const lines = ref([]);
let animationId;
function initParticles() {
const width = svg.value.clientWidth;
const height = svg.value.clientHeight;
const particleCount = 50;
particles.value = Array.from({ length: particleCount }, () => ({
x: Math.random() * width,
y: Math.random() * height,
size: Math.random() * 2 + 1,
speedX: Math.random() * 2 - 1,
speedY: Math.random() * 2 - 1,
}));
}
function updateParticles() {
const width = svg.value.clientWidth;
const height = svg.value.clientHeight;
particles.value.forEach(p => {
p.x += p.speedX;
p.y += p.speedY;
// 边界反弹
if (p.x < 0 || p.x > width) p.speedX *= -1;
if (p.y < 0 || p.y > height) p.speedY *= -1;
});
}
function updateLines() {
const maxDistance = 100;
lines.value = [];
for (let i = 0; i < particles.value.length; i++) {
for (let j = i + 1; j < particles.value.length; j++) {
const dx = particles.value[i].x - particles.value[j].x;
const dy = particles.value[i].y - particles.value[j].y;
const distance = Math.sqrt(dx * dx + dy * dy);
if (distance < maxDistance) {
lines.value.push({
x1: particles.value[i].x,
y1: particles.value[i].y,
x2: particles.value[j].x,
y2: particles.value[j].y,
opacity: 1 - distance / maxDistance,
});
}
}
}
}
function animate() {
updateParticles();
updateLines();
animationId = requestAnimationFrame(animate);
}
onMounted(() => {
initParticles();
animate();
});
onUnmounted(() => {
cancelAnimationFrame(animationId);
});
</script>
<style scoped>
.particles-container {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: -1;
background: #000;
}
.particles-svg {
display: block;
}
</style>
SVG 方案特点
-
使用 Vue 的
v-for动态渲染circle和line -
适合粒子较少的情况(性能不如 Canvas)
-
代码更简洁,适合简单场景
总结
|
方案 |
适用场景 |
性能 |
代码复杂度 |
|---|---|---|---|
|
Canvas |
大量粒子(1000+)、高性能需求 |
⭐⭐⭐⭐⭐ |
⭐⭐⭐ |
|
SVG + Vue |
少量粒子(<100)、简单交互 |
⭐⭐ |
⭐⭐ |
推荐:
-
如果是 科技感背景(如官网、登录页),用 Canvas 方案(性能更好)。
-
如果是 少量粒子动画,用 SVG 方案(代码更简单)。
希望这个实现能帮到你!🚀
更多推荐
所有评论(0)