ParticlesCanvas.vue 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114
  1. <template>
  2. <canvas ref="canvasRef" class="particles-canvas" />
  3. </template>
  4. <script setup>
  5. import { onMounted, onUnmounted, ref } from 'vue'
  6. const canvasRef = ref(null)
  7. let animationId = null
  8. let particles = []
  9. const initParticles = (width, height) => {
  10. particles = []
  11. const particleCount = 30
  12. const centerX = width / 2
  13. const centerY = height / 2
  14. const maxDistance = Math.min(width, height * 2) * 0.4
  15. for (let i = 0; i < particleCount; i++) {
  16. const angle = Math.random() * Math.PI * 2
  17. const distance = maxDistance + Math.random() * 50
  18. particles.push({
  19. x: centerX + Math.cos(angle) * distance,
  20. y: centerY + Math.sin(angle) * distance,
  21. targetX: centerX,
  22. targetY: centerY,
  23. speed: 1.0 + Math.random() * 1.5,
  24. size: 1.5 + Math.random() * 1,
  25. opacity: 0.3 + Math.random() * 0.7
  26. })
  27. }
  28. }
  29. const drawAnimation = (ctx, width, height) => {
  30. const centerX = width / 2
  31. const centerY = height / 2
  32. ctx.clearRect(0, 0, width, height)
  33. if (particles.length > 0) {
  34. particles.forEach(particle => {
  35. const dx = particle.targetX - particle.x
  36. const dy = particle.targetY - particle.y
  37. const distance = Math.sqrt(dx * dx + dy * dy)
  38. if (distance < 5) {
  39. const angle = Math.random() * Math.PI * 2
  40. const maxDistance = Math.min(width, height * 2) * 0.4
  41. const newDistance = maxDistance + Math.random() * 50
  42. particle.x = centerX + Math.cos(angle) * newDistance
  43. particle.y = centerY + Math.sin(angle) * newDistance
  44. particle.opacity = 0.3 + Math.random() * 0.7
  45. } else {
  46. const moveX = (dx / distance) * particle.speed
  47. const moveY = (dy / distance) * particle.speed
  48. particle.x += moveX
  49. particle.y += moveY
  50. }
  51. ctx.globalAlpha = particle.opacity
  52. ctx.fillStyle = '#ffffff'
  53. ctx.beginPath()
  54. ctx.arc(particle.x, particle.y, particle.size, 0, Math.PI * 2)
  55. ctx.fill()
  56. ctx.globalAlpha = 1
  57. })
  58. }
  59. }
  60. const startAnimation = () => {
  61. stopAnimation()
  62. const canvas = canvasRef.value
  63. if (!canvas) return
  64. const rect = canvas.getBoundingClientRect()
  65. const width = rect.width || window.innerWidth
  66. const height = rect.height || rect.width / 2
  67. canvas.width = width
  68. canvas.height = height
  69. const ctx = canvas.getContext('2d')
  70. initParticles(width, height)
  71. const loop = () => {
  72. drawAnimation(ctx, width, height)
  73. animationId = requestAnimationFrame(loop)
  74. }
  75. loop()
  76. }
  77. const stopAnimation = () => {
  78. if (animationId) {
  79. cancelAnimationFrame(animationId)
  80. animationId = null
  81. }
  82. }
  83. onMounted(() => {
  84. startAnimation()
  85. window.addEventListener('resize', startAnimation)
  86. })
  87. onUnmounted(() => {
  88. stopAnimation()
  89. window.removeEventListener('resize', startAnimation)
  90. })
  91. </script>
  92. <style scoped>
  93. .particles-canvas {
  94. width: 100%;
  95. height: 100%;
  96. display: block;
  97. pointer-events: none;
  98. }
  99. </style>