在 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>

如何使用?​

  1. App.vue或任意页面中引入:

<script setup>
import ParticlesBackground from './components/ParticlesBackground.vue';
</script>

<template>
  <ParticlesBackground />
  <div>你的页面内容...</div>
</template>
  1. 效果:

    • 白色粒子随机移动

    • 粒子靠近时会连线

    • 背景为黑色(可修改)


方案 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动态渲染 circleline

  • 适合粒子较少的情况(性能不如 Canvas)

  • 代码更简洁,适合简单场景


总结

方案

适用场景

性能

代码复杂度

Canvas

大量粒子(1000+)、高性能需求

⭐⭐⭐⭐⭐

⭐⭐⭐

SVG + Vue

少量粒子(<100)、简单交互

⭐⭐

⭐⭐

推荐:​

  • 如果是 ​科技感背景​(如官网、登录页),用 ​Canvas 方案​(性能更好)。

  • 如果是 ​少量粒子动画,用 ​SVG 方案​(代码更简单)。

希望这个实现能帮到你!🚀

更多推荐