nodes.html 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224
  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8" />
  5. <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  6. <title>nodes</title>
  7. <style>
  8. :root {
  9. --node-color: #ff00ff;
  10. --bg-1: #05010a;
  11. --bg-2: #13051d;
  12. --line-alpha: 0.28;
  13. }
  14. * {
  15. box-sizing: border-box;
  16. }
  17. html, body {
  18. margin: 0;
  19. width: 100%;
  20. height: 100%;
  21. overflow: hidden;
  22. background:
  23. radial-gradient(circle at 20% 20%, rgba(255, 0, 255, 0.08), transparent 30%),
  24. radial-gradient(circle at 80% 30%, rgba(255, 0, 255, 0.06), transparent 24%),
  25. linear-gradient(160deg, var(--bg-1), var(--bg-2));
  26. font-family: Inter, Arial, sans-serif;
  27. }
  28. canvas {
  29. display: block;
  30. width: 100vw;
  31. height: 100vh;
  32. cursor: crosshair;
  33. }
  34. </style>
  35. </head>
  36. <body>
  37. <canvas id="network"></canvas>
  38. <script>
  39. const canvas = document.getElementById('network');
  40. const ctx = canvas.getContext('2d');
  41. const CONFIG = {
  42. nodeCount: 90,
  43. nodeColor: '#ff00ff',
  44. nodeRadiusMin: 2.2,
  45. nodeRadiusMax: 5.5,
  46. linkDistance: 150,
  47. mouseInfluence: 190,
  48. attractionStrength: 0.02,
  49. returnStrength: 0.012,
  50. friction: 0.92,
  51. pulseSpeed: 0.0025,
  52. wobble: 0.18,
  53. };
  54. let width = 0;
  55. let height = 0;
  56. let dpr = Math.min(window.devicePixelRatio || 1, 2);
  57. let time = 0;
  58. const mouse = {
  59. x: 0,
  60. y: 0,
  61. active: false,
  62. };
  63. class Node {
  64. constructor() {
  65. this.homeX = Math.random() * width;
  66. this.homeY = Math.random() * height;
  67. this.x = this.homeX;
  68. this.y = this.homeY;
  69. this.vx = 0;
  70. this.vy = 0;
  71. this.radius = CONFIG.nodeRadiusMin + Math.random() * (CONFIG.nodeRadiusMax - CONFIG.nodeRadiusMin);
  72. this.seed = Math.random() * Math.PI * 2;
  73. }
  74. update(t) {
  75. const dxHome = this.homeX - this.x;
  76. const dyHome = this.homeY - this.y;
  77. this.vx += dxHome * CONFIG.returnStrength;
  78. this.vy += dyHome * CONFIG.returnStrength;
  79. const wobbleX = Math.cos(t * CONFIG.pulseSpeed + this.seed) * CONFIG.wobble;
  80. const wobbleY = Math.sin(t * CONFIG.pulseSpeed * 0.85 + this.seed) * CONFIG.wobble;
  81. this.vx += wobbleX * 0.03;
  82. this.vy += wobbleY * 0.03;
  83. if (mouse.active) {
  84. const dx = mouse.x - this.x;
  85. const dy = mouse.y - this.y;
  86. const dist = Math.hypot(dx, dy) || 0.0001;
  87. if (dist < CONFIG.mouseInfluence) {
  88. const force = (1 - dist / CONFIG.mouseInfluence) * CONFIG.attractionStrength * 28;
  89. this.vx += (dx / dist) * force;
  90. this.vy += (dy / dist) * force;
  91. }
  92. }
  93. this.vx *= CONFIG.friction;
  94. this.vy *= CONFIG.friction;
  95. this.x += this.vx;
  96. this.y += this.vy;
  97. }
  98. draw(t) {
  99. const pulse = 1 + Math.sin(t * 0.004 + this.seed) * 0.16;
  100. const r = this.radius * pulse;
  101. ctx.beginPath();
  102. ctx.arc(this.x, this.y, r, 0, Math.PI * 2);
  103. ctx.fillStyle = CONFIG.nodeColor;
  104. ctx.shadowColor = CONFIG.nodeColor;
  105. ctx.shadowBlur = 16;
  106. ctx.fill();
  107. ctx.shadowBlur = 0;
  108. ctx.beginPath();
  109. ctx.arc(this.x, this.y, r * 2.1, 0, Math.PI * 2);
  110. ctx.fillStyle = 'rgba(255, 0, 255, 0.05)';
  111. ctx.fill();
  112. }
  113. }
  114. let nodes = [];
  115. function resize() {
  116. width = window.innerWidth;
  117. height = window.innerHeight;
  118. dpr = Math.min(window.devicePixelRatio || 1, 2);
  119. canvas.width = width * dpr;
  120. canvas.height = height * dpr;
  121. canvas.style.width = width + 'px';
  122. canvas.style.height = height + 'px';
  123. ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
  124. nodes = Array.from({ length: CONFIG.nodeCount }, () => new Node());
  125. }
  126. function drawLinks() {
  127. for (let i = 0; i < nodes.length; i++) {
  128. for (let j = i + 1; j < nodes.length; j++) {
  129. const a = nodes[i];
  130. const b = nodes[j];
  131. const dx = b.x - a.x;
  132. const dy = b.y - a.y;
  133. const dist = Math.hypot(dx, dy);
  134. if (dist < CONFIG.linkDistance) {
  135. const alpha = (1 - dist / CONFIG.linkDistance) * 0.65;
  136. ctx.beginPath();
  137. ctx.moveTo(a.x, a.y);
  138. ctx.lineTo(b.x, b.y);
  139. ctx.strokeStyle = `rgba(255, 0, 255, ${alpha * 0.65})`;
  140. ctx.lineWidth = 1;
  141. ctx.stroke();
  142. }
  143. }
  144. }
  145. }
  146. function animate(timestamp) {
  147. time = timestamp;
  148. ctx.clearRect(0, 0, width, height);
  149. const gradient = ctx.createRadialGradient(
  150. mouse.active ? mouse.x : width * 0.5,
  151. mouse.active ? mouse.y : height * 0.5,
  152. 0,
  153. width * 0.5,
  154. height * 0.5,
  155. Math.max(width, height) * 0.7
  156. );
  157. gradient.addColorStop(0, 'rgba(255, 0, 255, 0.06)');
  158. gradient.addColorStop(1, 'rgba(255, 0, 255, 0)');
  159. ctx.fillStyle = gradient;
  160. ctx.fillRect(0, 0, width, height);
  161. nodes.forEach(node => node.update(timestamp));
  162. drawLinks();
  163. nodes.forEach(node => node.draw(timestamp));
  164. requestAnimationFrame(animate);
  165. }
  166. window.addEventListener('mousemove', (event) => {
  167. mouse.x = event.clientX;
  168. mouse.y = event.clientY;
  169. mouse.active = true;
  170. });
  171. window.addEventListener('touchmove', (event) => {
  172. if (event.touches[0]) {
  173. mouse.x = event.touches[0].clientX;
  174. mouse.y = event.touches[0].clientY;
  175. mouse.active = true;
  176. }
  177. }, { passive: true });
  178. window.addEventListener('mouseleave', () => {
  179. mouse.active = false;
  180. });
  181. window.addEventListener('touchend', () => {
  182. mouse.active = false;
  183. });
  184. window.addEventListener('resize', resize);
  185. resize();
  186. requestAnimationFrame(animate);
  187. </script>
  188. </body>
  189. </html>