为登录页面实现动态粒子效果

0 阅读1分钟

image.png

今天想着登录页太俗了,用AI给登录页面整一个动态粒子效果。没想到效果还很不错,以下是实现的功能描述:

✨ 粒子效果特性

1. 粒子系统

  • 根据屏幕大小自动生成粒子数量
  • 每个粒子有随机位置、速度、大小和透明度
  • 使用蓝色系(#1890ff)粒子

2. 鼠标交互

  • 粒子会被鼠标吸引
  • 在鼠标 150px 范围内的粒子会受到引力影响
  • 越靠近鼠标,吸引力越强

3. 物理效果

  • 粒子有摩擦力,速度会逐渐衰减
  • 粒子会在边界反弹
  • 平滑的运动效果

4. 视觉连线

  • 距离较近的粒子之间会绘制连线
  • 线条透明度根据距离动态变化
  • 距离越近,线条越明显

5. 性能优化

  • 使用 Canvas 2D 渲染,性能良好
  • 自适应屏幕大小
  • 组件卸载时自动清理资源

🎨 视觉效果

  • 粒子颜色: 蓝色系(主题色 #1890ff)
  • 粒子连线: 半透明蓝色连线
  • 背景: 保持原有的深色渐变背景
  • 动画: 持续循环动画,60fps

然后把这些描述喂给AI,您可以得到一个粒子跟随鼠标移动的动态效果!

以下就是我得到的代码,

 <canvas ref="particleCanvas" class="particle-canvas"></canvas>
<script setup lang="ts">
import { reactive, ref, onMounted, onBeforeUnmount } from 'vue'

// 粒子系统
const particleCanvas = ref<HTMLCanvasElement>()
let animationId: number
let particles: Particle[] = []
let mouse = { x: 0, y: 0 }

class Particle {
  x: number
  y: number
  vx: number
  vy: number
  radius: number
  color: string
  alpha: number

  constructor(canvas: HTMLCanvasElement) {
    this.x = Math.random() * canvas.width
    this.y = Math.random() * canvas.height
    this.vx = (Math.random() - 0.5) * 0.5
    this.vy = (Math.random() - 0.5) * 0.5
    this.radius = Math.random() * 2 + 1
    this.color = `rgba(24, 144, 255, ${Math.random() * 0.5 + 0.2})`
    this.alpha = Math.random() * 0.5 + 0.5
  }

  update(canvas: HTMLCanvasElement, mouseX: number, mouseY: number) {
    // 鼠标影响
    const dx = mouseX - this.x
    const dy = mouseY - this.y
    const distance = Math.sqrt(dx * dx + dy * dy)
    const maxDistance = 150

    if (distance < maxDistance) {
      const force = (maxDistance - distance) / maxDistance
      const angle = Math.atan2(dy, dx)
      this.vx += Math.cos(angle) * force * 0.5
      this.vy += Math.sin(angle) * force * 0.5
    }

    // 摩擦力
    this.vx *= 0.99
    this.vy *= 0.99

    // 更新位置
    this.x += this.vx
    this.y += this.vy

    // 边界检测
    if (this.x < 0 || this.x > canvas.width) this.vx *= -1
    if (this.y < 0 || this.y > canvas.height) this.vy *= -1
  }

  draw(ctx: CanvasRenderingContext2D) {
    ctx.beginPath()
    ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2)
    ctx.fillStyle = this.color
    ctx.fill()
  }
}

function initParticles() {
  if (!particleCanvas.value) return

  const canvas = particleCanvas.value
  const ctx = canvas.getContext('2d')
  if (!ctx) return

  // 设置画布尺寸
  canvas.width = window.innerWidth
  canvas.height = window.innerHeight

  // 创建粒子
  const particleCount = Math.floor((canvas.width * canvas.height) / 10000)
  particles = []
  for (let i = 0; i < particleCount; i++) {
    particles.push(new Particle(canvas))
  }

  // 鼠标移动事件
  const handleMouseMove = (e: MouseEvent) => {
    mouse.x = e.clientX
    mouse.y = e.clientY
  }

  // 窗口大小改变事件
  const handleResize = () => {
    canvas.width = window.innerWidth
    canvas.height = window.innerHeight
  }

  window.addEventListener('mousemove', handleMouseMove)
  window.addEventListener('resize', handleResize)

  // 动画循环
  function animate() {
    if (!ctx || !canvas) return

    ctx.clearRect(0, 0, canvas.width, canvas.height)

    // 更新和绘制粒子
    particles.forEach(particle => {
      particle.update(canvas, mouse.x, mouse.y)
      particle.draw(ctx)
    })

    // 绘制连线
    particles.forEach((p1, i) => {
      particles.slice(i + 1).forEach(p2 => {
        const dx = p1.x - p2.x
        const dy = p1.y - p2.y
        const distance = Math.sqrt(dx * dx + dy * dy)

        if (distance < 100) {
          ctx.beginPath()
          ctx.moveTo(p1.x, p1.y)
          ctx.lineTo(p2.x, p2.y)
          ctx.strokeStyle = `rgba(24, 144, 255, ${0.2 * (1 - distance / 100)})`
          ctx.lineWidth = 0.5
          ctx.stroke()
        }
      })
    })

    animationId = requestAnimationFrame(animate)
  }

  animate()

  // 清理函数
  return () => {
    window.removeEventListener('mousemove', handleMouseMove)
    window.removeEventListener('resize', handleResize)
    cancelAnimationFrame(animationId)
  }
}

onMounted(() => {
  initParticles()
})

onBeforeUnmount(() => {
  if (animationId) {
    cancelAnimationFrame(animationId)
  }
})

我是用的智谱 GLM-5.0 模型,整体效果还是很不错的!!!