index.html 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239
  1. <!DOCTYPE html>
  2. <head>
  3. <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
  4. <title>Gravity</title>
  5. <link rel="manifest" href="/site.webmanifest">
  6. <link rel="stylesheet" href="/css/bootstrap.min.css">
  7. <link rel="stylesheet" href="/css/style.css">
  8. <link rel="preconnect" href="https://fonts.googleapis.com">
  9. <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
  10. <link href="https://fonts.googleapis.com/css2?family=Anonymous+Pro:ital,wght@0,400;0,700;1,400;1,700
  11. &family=Noto+Serif+SC:wght@300;400;500;600;700&display=swap" rel="stylesheet">
  12. <script src="https://apps.bdimg.com/libs/jquery/1.10.2/jquery.min.js"></script>
  13. <style>
  14. * {
  15. margin: 0;
  16. padding: 0;
  17. width: 100%;
  18. height: 100%;
  19. overflow: hidden;
  20. }
  21. .btn {
  22. width: auto;
  23. height: auto;
  24. }
  25. body {
  26. background: radial-gradient(rgb(235, 235, 235), floralwhite);
  27. padding: 0;
  28. }
  29. </style>
  30. </head>
  31. <body>
  32. <a class="btn btn-default" href="/" onmouseenter="alert('单击屏幕添加元素。\n所有元素互相吸引,鼠标亦有引力。');$('.btn').hide()"
  33. style="position:fixed"><span class="glyphicon glyphicon-question-sign" aria-hidden="true"></span></a>
  34. <canvas id="canvas" onclick='init(10)' width="500" height="500"></canvas>
  35. </body>
  36. <script>
  37. const LOST = 0.3;
  38. var canvas = document.getElementById('canvas');
  39. var context = canvas.getContext('2d');
  40. var g = [];
  41. const MAX_MASS = 3000;
  42. const PLANET_COUNT = 300;
  43. const FRAGMENT_COUNT = 100;
  44. const FRAGMENT_SPEED = 20;
  45. const MAX_SPEED = 8;
  46. const G = 0.5;
  47. var maxLineLength = 45;
  48. const DEFAULT_MASS = 20;
  49. const LOCK_TIME = 50;
  50. const COLOR = '#333';
  51. const LINE_COLOR = '#2344';
  52. const MOUSE_MASS = 100;
  53. var mousepoint = { x: 0, y: 0 };
  54. window.AudioContext = window.AudioContext || window.webkitAudioContext;
  55. if (!window.AudioContext) {
  56. console.log('当前浏览器不支持Web Audio API');
  57. }
  58. var audioCtx = new AudioContext();
  59. var arrFrequency = [196.00, 220.00, 246.94, 261.63, 293.66, 329.63, 349.23, 392.00, 440.00, 493.88, 523.25, 587.33, 659.25, 698.46, 783.99, 880.00, 987.77, 1046.50];
  60. function init(count) {
  61. if (g.length > 300) return;
  62. var w = canvas.clientWidth;
  63. var h = canvas.clientHeight;
  64. for (var i = 0; i < count; i++) {
  65. g.push({ x: Math.random() * w, y: Math.random() * h, mass: DEFAULT_MASS, vx: Math.random() * 2 - 1, vy: Math.random() * 2 - 1, lock: 0 });
  66. }
  67. }
  68. function fillRound(cx, cy, r, color) {
  69. context.beginPath();
  70. context.fillStyle = color;
  71. context.arc(cx, cy, Math.sqrt(r) / Math.PI, 0 * Math.PI, 2 * Math.PI, true);
  72. context.fill();
  73. }
  74. function drawLine(x1, y1, x2, y2, color) {
  75. context.beginPath();
  76. context.moveTo(x1, y1);
  77. context.strokeStyle = color;
  78. context.lineTo(x2, y2);
  79. context.stroke();
  80. }
  81. function collide() {
  82. for (var i = 0, count = g.length; i < count; i++) {
  83. for (var i1 = 0; i1 < count; i1++) {
  84. if (i1 != i && g[i1]) {
  85. if (g[i].lock > 0 && g[i1].lock > 0) continue;
  86. if ((g[i].lock > 0 || g[i1].lock > 0) && Math.random() > 0.1) continue;
  87. var r = Math.sqrt(Math.pow((g[i].x - g[i1].x), 2) + Math.pow((g[i].y - g[i1].y), 2));
  88. if (r <= Math.max(Math.sqrt(g[i1].mass) / Math.PI + Math.sqrt(g[i].mass) / Math.PI) && g[i1].mass >= g[i].mass) {
  89. var amplitude = Math.min(g[i1].mass, g[i].mass) / 1000 + (g[i1].mass + g[i].mass) / 2000;
  90. g[i1].vy = (g[i1].mass * g[i1].vy + g[i].mass * g[i].vy) / (g[i1].mass + g[i].mass);
  91. g[i1].vx = (g[i1].mass * g[i1].vx + g[i].mass * g[i].vx) / (g[i1].mass + g[i].mass);
  92. g[i1].mass += g[i].mass;
  93. g[i] = null;
  94. var frequency = arrFrequency[Math.floor(Math.random() * 15)];
  95. var oscillator = audioCtx.createOscillator();
  96. var gainNode = audioCtx.createGain();
  97. oscillator.connect(gainNode);
  98. gainNode.connect(audioCtx.destination);
  99. oscillator.type = 'sawtooth';
  100. oscillator.frequency.value = frequency;
  101. gainNode.gain.setValueAtTime(0, audioCtx.currentTime);
  102. gainNode.gain.linearRampToValueAtTime(amplitude, audioCtx.currentTime + 0.01);
  103. oscillator.start(audioCtx.currentTime);
  104. gainNode.gain.exponentialRampToValueAtTime(0.001, audioCtx.currentTime + 1);
  105. oscillator.stop(audioCtx.currentTime + 1);
  106. break;
  107. }
  108. }
  109. }
  110. }
  111. }
  112. function explode() {
  113. for (var i = 0, count = g.length; i < count; i++) {
  114. if (g[i] && g[i].mass >= MAX_MASS) {
  115. for (var i1 = 0; i1 < FRAGMENT_COUNT; i1++) {
  116. g.push({
  117. x: g[i].x + Math.random(),
  118. y: g[i].y + Math.random(),
  119. mass: g[i].mass / FRAGMENT_COUNT,
  120. vx: FRAGMENT_SPEED * (Math.random() * 2 - 1),
  121. vy: FRAGMENT_SPEED * (Math.random() * 2 - 1),
  122. lock: LOCK_TIME * (Math.random() + 1)
  123. });
  124. }
  125. var frequency = arrFrequency[Math.floor(Math.random() * 10)];
  126. var oscillator = audioCtx.createOscillator();
  127. var gainNode = audioCtx.createGain();
  128. oscillator.connect(gainNode);
  129. gainNode.connect(audioCtx.destination);
  130. oscillator.type = 'sine';
  131. oscillator.frequency.value = frequency;
  132. gainNode.gain.setValueAtTime(0, audioCtx.currentTime);
  133. gainNode.gain.linearRampToValueAtTime(1.3, audioCtx.currentTime + 0.01);
  134. oscillator.start(audioCtx.currentTime);
  135. gainNode.gain.exponentialRampToValueAtTime(0.001, audioCtx.currentTime + 10);
  136. oscillator.stop(audioCtx.currentTime + 10);
  137. g[i] = null;
  138. }
  139. }
  140. }
  141. canvas.onmousemove = function (event) {
  142. var e = event || window.event;
  143. mousepoint = { 'x': e.clientX, 'y': e.clientY };
  144. }
  145. function step() {
  146. var w = canvas.clientWidth;
  147. var h = canvas.clientHeight;
  148. collide();
  149. explode();
  150. for (var i = 0, count = g.length; i < count; i++) {
  151. if (g[i]) {
  152. if (g[i].vx === NaN) {
  153. g[i].vx = 0
  154. }
  155. if (g[i].vy === NaN) {
  156. g[i].vy = 0
  157. }
  158. }
  159. }
  160. g = g.filter(e => e && e.x !== NaN && e.y !== NaN && e.vx !== NaN && e.vy !== NaN && e.mass !== NaN);
  161. for (var i = 0, count = g.length; i < count; i++) {
  162. if (g[i].x > w) {
  163. g[i].x -= (g[i].x - w) * 2;
  164. g[i].vx *= -(1 - LOST);
  165. } else if (g[i].x < 0) {
  166. g[i].x += g[i].x * -2;
  167. g[i].vx *= -(1 - LOST);
  168. }
  169. if (g[i].y > h) {
  170. g[i].y -= (g[i].y - h) * 2;
  171. g[i].vy *= -(1 - LOST);
  172. } else if (g[i].y < 0) {
  173. g[i].y += g[i].y * -2;
  174. g[i].vy *= -(1 - LOST);
  175. }
  176. fillRound(g[i].x, g[i].y, g[i].mass, COLOR)
  177. for (var i1 = 0; i1 < count; i1++) {
  178. if (i1 != i) {
  179. var r = Math.sqrt(Math.pow((g[i].x - g[i1].x), 2) + Math.pow((g[i].y - g[i1].y), 2));
  180. var f = r != 0 ? G * G * g[i1].mass / Math.pow(r, 2) : 0;
  181. var dx = g[i1].x - g[i].x;
  182. var dy = g[i1].y - g[i].y;
  183. if (g[i].mass > 0) {
  184. g[i].vy += f / r * dy;
  185. g[i].vx += f / r * dx;
  186. }
  187. if (i1 > i && r <= maxLineLength) {
  188. drawLine(g[i].x, g[i].y, g[i1].x, g[i1].y, LINE_COLOR)
  189. }
  190. }
  191. }
  192. //
  193. var r = Math.max(1, Math.sqrt(Math.pow((g[i].x - mousepoint.x), 2) + Math.pow((g[i].y - mousepoint.y), 2)));
  194. if (r > maxLineLength / 8) {
  195. var f = r != 0 ? G * G * MOUSE_MASS / Math.pow(r, 2) : 0;
  196. var dx = mousepoint.x - g[i].x;
  197. var dy = mousepoint.y - g[i].y;
  198. if (g[i].mass > 0) {
  199. g[i].vy += f / r * dy;
  200. g[i].vx += f / r * dx;
  201. }
  202. if (r <= maxLineLength) {
  203. drawLine(g[i].x, g[i].y, mousepoint.x, mousepoint.y, LINE_COLOR);
  204. }
  205. }
  206. //
  207. var speedNow = Math.sqrt(Math.pow(g[i].vx, 2) + Math.pow(g[i].vy, 2))
  208. if (speedNow > MAX_SPEED) {
  209. g[i].vx = g[i].vx * MAX_SPEED / speedNow;
  210. g[i].vy = g[i].vy * MAX_SPEED / speedNow;
  211. }
  212. g[i].x += g[i].vx;
  213. g[i].y += g[i].vy;
  214. g[i].lock--;
  215. }
  216. }
  217. // init(PLANET_COUNT);
  218. setInterval(function () {
  219. var w = document.body.clientWidth;
  220. var h = document.body.clientHeight;
  221. maxLineLength = Math.max(30, Math.min(w / 10, h / 10));
  222. canvas.setAttribute('width', w);
  223. canvas.setAttribute('height', h);
  224. canvas.style.width = w + 'px';
  225. canvas.style.height = h + 'px';
  226. step();
  227. }, 30)
  228. </script>