粒子互动特效


前端实现粒子互动背景:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>粒子互动背景</title>
  <!-- 引入Tailwind CSS -->
  <script src="https://cdn.tailwindcss.com"></script>
  <!-- 引入Font Awesome -->
  <link href="https://cdn.jsdelivr.net/npm/font-awesome@4.7.0/css/font-awesome.min.css" rel="stylesheet">

  <!-- 配置Tailwind -->
  <script>
    tailwind.config = {
      theme: {
        extend: {
          colors: {
            primary: '#3B82F6',
            secondary: '#8B5CF6',
            accent: '#10B981',
            dark: '#1E293B',
          },
          fontFamily: {
            inter: ['Inter', 'sans-serif'],
          },
        },
      }
    }
  </script>

  <style type="text/tailwindcss">
    @layer utilities {
      .content-auto {
        content-visibility: auto;
      }
      .glass {
        @apply bg-white/10 backdrop-blur-md border border-white/20;
      }
      .btn-effect {
        @apply transition-all duration-300 hover:scale-105 active:scale-95;
      }
    }
  </style>

  <style>
    body {
      margin: 0;
      overflow: hidden;
      font-family: 'Inter', sans-serif;
    }

    #canvas {
      display: block;
      position: absolute;
      top: 0;
      left: 0;
      z-index: 0;
    }

    .control-panel {
      transition: transform 0.3s ease, opacity 0.3s ease;
    }

    .panel-hidden {
      transform: translateX(-100%);
      opacity: 0;
      pointer-events: none;
    }

    .toggle-btn {
      transition: transform 0.3s ease;
    }

    .toggle-btn.rotated {
      transform: rotate(180deg);
    }
  </style>
</head>
<body class="bg-dark text-white">
  <!-- 粒子画布 -->
  <canvas id="canvas"></canvas>

  <!-- 主标题 -->
  <div class="fixed top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2 z-10 text-center">
    <h1 class="text-[clamp(2rem,5vw,4rem)] font-bold mb-4 bg-clip-text text-transparent bg-gradient-to-r from-primary to-secondary">
      粒子互动背景
    </h1>
    <p class="text-[clamp(1rem,2vw,1.25rem)] text-white/80 max-w-md mx-auto">
      移动鼠标与粒子互动,点击左侧按钮打开控制面板调整效果
    </p>
  </div>

  <!-- 控制面板 -->
  <div id="controlPanel" class="control-panel fixed top-0 left-0 h-full glass p-6 w-80 z-20 overflow-y-auto">
    <div class="flex justify-between items-center mb-8">
      <h2 class="text-2xl font-bold">控制面板</h2>
      <button id="togglePanel" class="toggle-btn text-2xl btn-effect">
        <i class="fa fa-chevron-left"></i>
      </button>
    </div>

    <div class="space-y-6">
      <!-- 粒子数量控制 -->
      <div>
        <label class="block mb-2 text-white/80">粒子数量: <span id="particleCountValue">100</span></label>
        <input type="range" id="particleCount" min="50" max="300" value="100" 
               class="w-full h-2 bg-white/20 rounded-lg appearance-none cursor-pointer accent-primary">
      </div>

      <!-- 粒子速度控制 -->
      <div>
        <label class="block mb-2 text-white/80">粒子速度: <span id="particleSpeedValue">1</span></label>
        <input type="range" id="particleSpeed" min="0.5" max="5" step="0.5" value="1" 
               class="w-full h-2 bg-white/20 rounded-lg appearance-none cursor-pointer accent-primary">
      </div>

      <!-- 连线距离控制 -->
      <div>
        <label class="block mb-2 text-white/80">连线距离: <span id="connectionDistanceValue">150</span></label>
        <input type="range" id="connectionDistance" min="50" max="300" value="150" 
               class="w-full h-2 bg-white/20 rounded-lg appearance-none cursor-pointer accent-primary">
      </div>

      <!-- 鼠标引力控制 -->
      <div>
        <label class="block mb-2 text-white/80">鼠标引力: <span id="mouseForceValue">50</span></label>
        <input type="range" id="mouseForce" min="0" max="200" value="50" 
               class="w-full h-2 bg-white/20 rounded-lg appearance-none cursor-pointer accent-primary">
      </div>

      <!-- 粒子大小控制 -->
      <div>
        <label class="block mb-2 text-white/80">粒子大小: <span id="particleSizeValue">2</span></label>
        <input type="range" id="particleSize" min="1" max="10" value="2" 
               class="w-full h-2 bg-white/20 rounded-lg appearance-none cursor-pointer accent-primary">
      </div>

      <!-- 主题颜色选择 -->
      <div>
        <label class="block mb-2 text-white/80">主题颜色</label>
        <div class="flex gap-3">
          <button class="theme-btn w-8 h-8 rounded-full bg-gradient-to-r from-primary to-secondary btn-effect" data-theme="blue"></button>
          <button class="theme-btn w-8 h-8 rounded-full bg-gradient-to-r from-pink-500 to-rose-500 btn-effect" data-theme="pink"></button>
          <button class="theme-btn w-8 h-8 rounded-full bg-gradient-to-r from-accent to-emerald-400 btn-effect" data-theme="green"></button>
          <button class="theme-btn w-8 h-8 rounded-full bg-gradient-to-r from-amber-500 to-orange-500 btn-effect" data-theme="orange"></button>
          <button class="theme-btn w-8 h-8 rounded-full bg-gradient-to-r from-zinc-400 to-zinc-800 btn-effect" data-theme="gray"></button>
        </div>
      </div>

      <!-- 重置按钮 -->
      <button id="resetBtn" class="w-full py-3 rounded-lg bg-white/10 hover:bg-white/20 transition-all duration-300 btn-effect">
        <i class="fa fa-refresh mr-2"></i>重置所有设置
      </button>
    </div>
  </div>

  <script>
    // 获取画布和上下文
    const canvas = document.getElementById('canvas');
    const ctx = canvas.getContext('2d');

    // 设置画布尺寸
    function resizeCanvas() {
      canvas.width = window.innerWidth;
      canvas.height = window.innerHeight;
    }

    // 初始化尺寸并监听窗口大小变化
    resizeCanvas();
    window.addEventListener('resize', resizeCanvas);

    // 粒子类
    class Particle {
      constructor() {
        this.x = Math.random() * canvas.width;
        this.y = Math.random() * canvas.height;
        this.size = particleSize;
        this.speedX = (Math.random() - 0.5) * particleSpeed;
        this.speedY = (Math.random() - 0.5) * particleSpeed;
        this.color = primaryColor;
      }

      // 更新粒子位置
      update() {
        this.x += this.speedX;
        this.y += this.speedY;

        // 边界检测
        if (this.x < 0 || this.x > canvas.width) this.speedX *= -1;
        if (this.y < 0 || this.y > canvas.height) this.speedY *= -1;

        // 鼠标引力效果
        if (mouse.x && mouse.y) {
          const dx = mouse.x - this.x;
          const dy = mouse.y - this.y;
          const distance = Math.sqrt(dx * dx + dy * dy);

          if (distance < mouseForce) {
            const force = (mouseForce - distance) / mouseForce;
            this.x += dx * force * 0.1;
            this.y += dy * force * 0.1;
          }
        }
      }

      // 绘制粒子
      draw() {
        ctx.fillStyle = this.color;
        ctx.beginPath();
        ctx.arc(this.x, this.y, this.size, 0, Math.PI * 2);
        ctx.fill();
      }
    }

    // 粒子数组
    let particles = [];

    // 配置参数
    let particleCount = 100;
    let particleSpeed = 1;
    let connectionDistance = 150;
    let mouseForce = 50;
    let particleSize = 2;
    let primaryColor = '#3B82F6';
    let secondaryColor = '#8B5CF6';

    // 鼠标位置
    const mouse = {
      x: null,
      y: null,
      radius: 150
    };

    // 初始化粒子
    function init() {
      particles = [];
      for (let i = 0; i < particleCount; i++) {
        particles.push(new Particle());
      }
    }

    // 连接粒子
    function connectParticles() {
      for (let a = 0; a < particles.length; a++) {
        for (let b = a; b < particles.length; b++) {
          const dx = particles[a].x - particles[b].x;
          const dy = particles[a].y - particles[b].y;
          const distance = Math.sqrt(dx * dx + dy * dy);

          if (distance < connectionDistance) {
            const opacity = 1 - (distance / connectionDistance);
            ctx.strokeStyle = `rgba(139, 92, 246, ${opacity})`;
            ctx.lineWidth = 0.5;
            ctx.beginPath();
            ctx.moveTo(particles[a].x, particles[a].y);
            ctx.lineTo(particles[b].x, particles[b].y);
            ctx.stroke();
          }
        }
      }
    }

    // 动画循环
    function animate() {
      ctx.clearRect(0, 0, canvas.width, canvas.height);

      // 绘制渐变背景
      const gradient = ctx.createLinearGradient(0, 0, canvas.width, canvas.height);
      gradient.addColorStop(0, '#0F172A');
      gradient.addColorStop(1, '#1E293B');
      ctx.fillStyle = gradient;
      ctx.fillRect(0, 0, canvas.width, canvas.height);

      // 更新和绘制所有粒子
      for (let i = 0; i < particles.length; i++) {
        particles[i].update();
        particles[i].draw();
      }

      // 连接粒子
      connectParticles();

      requestAnimationFrame(animate);
    }

    // 事件监听
    window.addEventListener('mousemove', (e) => {
      mouse.x = e.x;
      mouse.y = e.y;
    });

    window.addEventListener('mouseout', () => {
      mouse.x = null;
      mouse.y = null;
    });

    // 控制面板交互
    const controlPanel = document.getElementById('controlPanel');
    const togglePanel = document.getElementById('togglePanel');
    const resetBtn = document.getElementById('resetBtn');

    // 控制面板切换
    togglePanel.addEventListener('click', () => {
      controlPanel.classList.toggle('panel-hidden');
      togglePanel.classList.toggle('rotated');
    });

    // 粒子数量控制
    const particleCountSlider = document.getElementById('particleCount');
    const particleCountValue = document.getElementById('particleCountValue');

    particleCountSlider.addEventListener('input', () => {
      particleCount = parseInt(particleCountSlider.value);
      particleCountValue.textContent = particleCount;
      init();
    });

    // 粒子速度控制
    const particleSpeedSlider = document.getElementById('particleSpeed');
    const particleSpeedValue = document.getElementById('particleSpeedValue');

    particleSpeedSlider.addEventListener('input', () => {
      particleSpeed = parseFloat(particleSpeedSlider.value);
      particleSpeedValue.textContent = particleSpeed;

      // 更新现有粒子的速度
      particles.forEach(particle => {
        const speedRatio = particleSpeed / parseFloat(particleSpeedSlider.previousValue || 1);
        particle.speedX *= speedRatio;
        particle.speedY *= speedRatio;
      });

      particleSpeedSlider.previousValue = particleSpeed;
    });

    // 连线距离控制
    const connectionDistanceSlider = document.getElementById('connectionDistance');
    const connectionDistanceValue = document.getElementById('connectionDistanceValue');

    connectionDistanceSlider.addEventListener('input', () => {
      connectionDistance = parseInt(connectionDistanceSlider.value);
      connectionDistanceValue.textContent = connectionDistance;
    });

    // 鼠标引力控制
    const mouseForceSlider = document.getElementById('mouseForce');
    const mouseForceValue = document.getElementById('mouseForceValue');

    mouseForceSlider.addEventListener('input', () => {
      mouseForce = parseInt(mouseForceSlider.value);
      mouseForceValue.textContent = mouseForce;
    });

    // 粒子大小控制
    const particleSizeSlider = document.getElementById('particleSize');
    const particleSizeValue = document.getElementById('particleSizeValue');

    particleSizeSlider.addEventListener('input', () => {
      particleSize = parseInt(particleSizeSlider.value);
      particleSizeValue.textContent = particleSize;

      // 更新现有粒子的大小
      particles.forEach(particle => {
        particle.size = particleSize;
      });
    });

    // 主题颜色选择
    const themeButtons = document.querySelectorAll('.theme-btn');
    const themeColors = {
      blue: ['#3B82F6', '#8B5CF6'],
      pink: ['#EC4899', '#F472B6'],
      green: ['#10B981', '#34D399'],
      orange: ['#F59E0B', '#F97316'],
      gray: ['#94A3B8', '#334155']
    };

    themeButtons.forEach(btn => {
      btn.addEventListener('click', () => {
        const theme = btn.getAttribute('data-theme');
        [primaryColor, secondaryColor] = themeColors[theme];

        // 更新粒子颜色
        particles.forEach(particle => {
          particle.color = primaryColor;
        });

        // 更新标题渐变
        document.querySelector('h1').style.background = `linear-gradient(to right, ${primaryColor}, ${secondaryColor})`;
        document.querySelector('h1').style.backgroundClip = 'text';
        document.querySelector('h1').style.textDecorationColor = 'transparent';
      });
    });

    // 重置按钮
    resetBtn.addEventListener('click', () => {
      particleCountSlider.value = 100;
      particleSpeedSlider.value = 1;
      connectionDistanceSlider.value = 150;
      mouseForceSlider.value = 50;
      particleSizeSlider.value = 2;

      particleCountValue.textContent = 100;
      particleSpeedValue.textContent = 1;
      connectionDistanceValue.textContent = 150;
      mouseForceValue.textContent = 50;
      particleSizeValue.textContent = 2;

      particleCount = 100;
      particleSpeed = 1;
      connectionDistance = 150;
      mouseForce = 50;
      particleSize = 2;
      primaryColor = '#3B82F6';
      secondaryColor = '#8B5CF6';

      // 更新标题渐变
      document.querySelector('h1').style.background = `linear-gradient(to right, ${primaryColor}, ${secondaryColor})`;

      init();
    });

    // 初始化并启动动画
    init();
    animate();
  </script>
</body>
</html>

创建一个.txt文本文件,把代码复制进去后保存,然后修改文本文件后缀为.html再打开即可。

0 条评论

发表评论

暂无评论,欢迎发表您的观点!