|  | 
|  | 1 | +const svg = document.getElementById('plexus'); | 
|  | 2 | +const width = window.innerWidth; | 
|  | 3 | +const height = window.innerHeight; | 
|  | 4 | + | 
|  | 5 | +svg.setAttribute('width', width); | 
|  | 6 | +svg.setAttribute('height', height); | 
|  | 7 | + | 
|  | 8 | +const numPoints = 80; | 
|  | 9 | +const maxDistance = 150; | 
|  | 10 | +const points = []; | 
|  | 11 | + | 
|  | 12 | +class Point { | 
|  | 13 | +  constructor() { | 
|  | 14 | +    this.x = Math.random() * width; | 
|  | 15 | +    this.y = Math.random() * height; | 
|  | 16 | +    this.vx = (Math.random() - 0.5) * 1.5; | 
|  | 17 | +    this.vy = (Math.random() - 0.5) * 1.5; | 
|  | 18 | +    this.opacity = Math.random(); | 
|  | 19 | +    this.fadeDirection = Math.random() > 0.5 ? 1 : -1; | 
|  | 20 | +    this.fadeSpeed = 0.005 + Math.random() * 0.01; | 
|  | 21 | +     | 
|  | 22 | +    this.element = document.createElementNS('http://www.w3.org/2000/svg', 'circle'); | 
|  | 23 | +    this.element.setAttribute('r', '2'); | 
|  | 24 | +    this.element.setAttribute('fill', '#6366f1'); | 
|  | 25 | +    svg.appendChild(this.element); | 
|  | 26 | +  } | 
|  | 27 | + | 
|  | 28 | +  update() { | 
|  | 29 | +    this.x += this.vx; | 
|  | 30 | +    this.y += this.vy; | 
|  | 31 | + | 
|  | 32 | +    if (this.x < 0 || this.x > width) this.vx *= -1; | 
|  | 33 | +    if (this.y < 0 || this.y > height) this.vy *= -1; | 
|  | 34 | + | 
|  | 35 | +    this.x = Math.max(0, Math.min(width, this.x)); | 
|  | 36 | +    this.y = Math.max(0, Math.min(height, this.y)); | 
|  | 37 | + | 
|  | 38 | +    this.opacity += this.fadeDirection * this.fadeSpeed; | 
|  | 39 | +    if (this.opacity >= 1) { | 
|  | 40 | +      this.opacity = 1; | 
|  | 41 | +      this.fadeDirection = -1; | 
|  | 42 | +    } else if (this.opacity <= 0.1) { | 
|  | 43 | +      this.opacity = 0.1; | 
|  | 44 | +      this.fadeDirection = 1; | 
|  | 45 | +    } | 
|  | 46 | + | 
|  | 47 | +    this.element.setAttribute('cx', this.x); | 
|  | 48 | +    this.element.setAttribute('cy', this.y); | 
|  | 49 | +    this.element.setAttribute('opacity', this.opacity); | 
|  | 50 | +  } | 
|  | 51 | +} | 
|  | 52 | + | 
|  | 53 | +for (let i = 0; i < numPoints; i++) { | 
|  | 54 | +  points.push(new Point()); | 
|  | 55 | +} | 
|  | 56 | + | 
|  | 57 | +const lineGroup = document.createElementNS('http://www.w3.org/2000/svg', 'g'); | 
|  | 58 | +lineGroup.setAttribute('id', 'lines'); | 
|  | 59 | +svg.insertBefore(lineGroup, svg.firstChild); | 
|  | 60 | + | 
|  | 61 | +function drawConnections() { | 
|  | 62 | +  lineGroup.innerHTML = ''; | 
|  | 63 | + | 
|  | 64 | +  for (let i = 0; i < points.length; i++) { | 
|  | 65 | +    for (let j = i + 1; j < points.length; j++) { | 
|  | 66 | +      const dx = points[i].x - points[j].x; | 
|  | 67 | +      const dy = points[i].y - points[j].y; | 
|  | 68 | +      const distance = Math.sqrt(dx * dx + dy * dy); | 
|  | 69 | + | 
|  | 70 | +      if (distance < maxDistance) { | 
|  | 71 | +        const opacity = (1 - distance / maxDistance) *  | 
|  | 72 | +                      Math.min(points[i].opacity, points[j].opacity) * 0.5; | 
|  | 73 | +         | 
|  | 74 | +        const line = document.createElementNS('http://www.w3.org/2000/svg', 'line'); | 
|  | 75 | +        line.setAttribute('x1', points[i].x); | 
|  | 76 | +        line.setAttribute('y1', points[i].y); | 
|  | 77 | +        line.setAttribute('x2', points[j].x); | 
|  | 78 | +        line.setAttribute('y2', points[j].y); | 
|  | 79 | +        line.setAttribute('stroke', '#8b5cf6'); | 
|  | 80 | +        line.setAttribute('stroke-width', '1'); | 
|  | 81 | +        line.setAttribute('opacity', opacity); | 
|  | 82 | +        lineGroup.appendChild(line); | 
|  | 83 | +      } | 
|  | 84 | +    } | 
|  | 85 | +  } | 
|  | 86 | +} | 
|  | 87 | + | 
|  | 88 | +function animate() { | 
|  | 89 | +  points.forEach(p => p.update()); | 
|  | 90 | +  drawConnections(); | 
|  | 91 | +  requestAnimationFrame(animate); | 
|  | 92 | +} | 
|  | 93 | + | 
|  | 94 | +window.addEventListener('resize', () => { | 
|  | 95 | +  const newWidth = window.innerWidth; | 
|  | 96 | +  const newHeight = window.innerHeight; | 
|  | 97 | +  svg.setAttribute('width', newWidth); | 
|  | 98 | +  svg.setAttribute('height', newHeight); | 
|  | 99 | +}); | 
|  | 100 | + | 
|  | 101 | +// Smooth scroll for navigation | 
|  | 102 | +document.querySelectorAll('a[href^="#"]').forEach(anchor => { | 
|  | 103 | +  anchor.addEventListener('click', function (e) { | 
|  | 104 | +    e.preventDefault(); | 
|  | 105 | +    const target = document.querySelector(this.getAttribute('href')); | 
|  | 106 | +    if (target) { | 
|  | 107 | +      target.scrollIntoView({ | 
|  | 108 | +        behavior: 'smooth', | 
|  | 109 | +        block: 'start' | 
|  | 110 | +      }); | 
|  | 111 | +    } | 
|  | 112 | +  }); | 
|  | 113 | +}); | 
|  | 114 | + | 
|  | 115 | +animate(); | 
0 commit comments