<!DOCTYPE html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>Gravity</title> <link rel="manifest" href="/site.webmanifest"> <link rel="stylesheet" href="/css/bootstrap.min.css"> <link rel="stylesheet" href="/css/style.css"> <link rel="preconnect" href="https://fonts.googleapis.com"> <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> <link href="https://fonts.googleapis.com/css2?family=Anonymous+Pro:ital,wght@0,400;0,700;1,400;1,700 &family=Noto+Serif+SC:wght@300;400;500;600;700&display=swap" rel="stylesheet"> <script src="https://apps.bdimg.com/libs/jquery/1.10.2/jquery.min.js"></script> <style> * { margin: 0; padding: 0; width: 100%; height: 100%; overflow: hidden; } .btn { width: auto; height: auto; } body { background: radial-gradient(rgb(235, 235, 235), floralwhite); padding: 0; } </style> </head> <body> <a class="btn btn-default" href="/" onmouseenter="alert('单击屏幕添加元素。\n所有元素互相吸引,鼠标亦有引力。');$('.btn').hide()" style="position:fixed"><span class="glyphicon glyphicon-question-sign" aria-hidden="true"></span></a> <canvas id="canvas" onclick='init(10)' width="500" height="500"></canvas> </body> <script> const LOST = 0.3; var canvas = document.getElementById('canvas'); var context = canvas.getContext('2d'); var g = []; const MAX_MASS = 3000; const PLANET_COUNT = 300; const FRAGMENT_COUNT = 100; const FRAGMENT_SPEED = 20; const MAX_SPEED = 8; const G = 0.5; var maxLineLength = 45; const DEFAULT_MASS = 20; const LOCK_TIME = 50; const COLOR = '#333'; const LINE_COLOR = '#2344'; const MOUSE_MASS = 100; var mousepoint = { x: 0, y: 0 }; window.AudioContext = window.AudioContext || window.webkitAudioContext; if (!window.AudioContext) { console.log('当前浏览器不支持Web Audio API'); } var audioCtx = new AudioContext(); 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]; function init(count) { if (g.length > 300) return; var w = canvas.clientWidth; var h = canvas.clientHeight; for (var i = 0; i < count; i++) { 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 }); } } function fillRound(cx, cy, r, color) { context.beginPath(); context.fillStyle = color; context.arc(cx, cy, Math.sqrt(r) / Math.PI, 0 * Math.PI, 2 * Math.PI, true); context.fill(); } function drawLine(x1, y1, x2, y2, color) { context.beginPath(); context.moveTo(x1, y1); context.strokeStyle = color; context.lineTo(x2, y2); context.stroke(); } function collide() { for (var i = 0, count = g.length; i < count; i++) { for (var i1 = 0; i1 < count; i1++) { if (i1 != i && g[i1]) { if (g[i].lock > 0 && g[i1].lock > 0) continue; if ((g[i].lock > 0 || g[i1].lock > 0) && Math.random() > 0.1) continue; var r = Math.sqrt(Math.pow((g[i].x - g[i1].x), 2) + Math.pow((g[i].y - g[i1].y), 2)); if (r <= Math.max(Math.sqrt(g[i1].mass) / Math.PI + Math.sqrt(g[i].mass) / Math.PI) && g[i1].mass >= g[i].mass) { var amplitude = Math.min(g[i1].mass, g[i].mass) / 1000 + (g[i1].mass + g[i].mass) / 2000; g[i1].vy = (g[i1].mass * g[i1].vy + g[i].mass * g[i].vy) / (g[i1].mass + g[i].mass); g[i1].vx = (g[i1].mass * g[i1].vx + g[i].mass * g[i].vx) / (g[i1].mass + g[i].mass); g[i1].mass += g[i].mass; g[i] = null; var frequency = arrFrequency[Math.floor(Math.random() * 15)]; var oscillator = audioCtx.createOscillator(); var gainNode = audioCtx.createGain(); oscillator.connect(gainNode); gainNode.connect(audioCtx.destination); oscillator.type = 'sawtooth'; oscillator.frequency.value = frequency; gainNode.gain.setValueAtTime(0, audioCtx.currentTime); gainNode.gain.linearRampToValueAtTime(amplitude, audioCtx.currentTime + 0.01); oscillator.start(audioCtx.currentTime); gainNode.gain.exponentialRampToValueAtTime(0.001, audioCtx.currentTime + 1); oscillator.stop(audioCtx.currentTime + 1); break; } } } } } function explode() { for (var i = 0, count = g.length; i < count; i++) { if (g[i] && g[i].mass >= MAX_MASS) { for (var i1 = 0; i1 < FRAGMENT_COUNT; i1++) { g.push({ x: g[i].x + Math.random(), y: g[i].y + Math.random(), mass: g[i].mass / FRAGMENT_COUNT, vx: FRAGMENT_SPEED * (Math.random() * 2 - 1), vy: FRAGMENT_SPEED * (Math.random() * 2 - 1), lock: LOCK_TIME * (Math.random() + 1) }); } var frequency = arrFrequency[Math.floor(Math.random() * 10)]; var oscillator = audioCtx.createOscillator(); var gainNode = audioCtx.createGain(); oscillator.connect(gainNode); gainNode.connect(audioCtx.destination); oscillator.type = 'sine'; oscillator.frequency.value = frequency; gainNode.gain.setValueAtTime(0, audioCtx.currentTime); gainNode.gain.linearRampToValueAtTime(1.3, audioCtx.currentTime + 0.01); oscillator.start(audioCtx.currentTime); gainNode.gain.exponentialRampToValueAtTime(0.001, audioCtx.currentTime + 10); oscillator.stop(audioCtx.currentTime + 10); g[i] = null; } } } canvas.onmousemove = function (event) { var e = event || window.event; mousepoint = { 'x': e.clientX, 'y': e.clientY }; } function step() { var w = canvas.clientWidth; var h = canvas.clientHeight; collide(); explode(); for (var i = 0, count = g.length; i < count; i++) { if (g[i]) { if (g[i].vx === NaN) { g[i].vx = 0 } if (g[i].vy === NaN) { g[i].vy = 0 } } } g = g.filter(e => e && e.x !== NaN && e.y !== NaN && e.vx !== NaN && e.vy !== NaN && e.mass !== NaN); for (var i = 0, count = g.length; i < count; i++) { if (g[i].x > w) { g[i].x -= (g[i].x - w) * 2; g[i].vx *= -(1 - LOST); } else if (g[i].x < 0) { g[i].x += g[i].x * -2; g[i].vx *= -(1 - LOST); } if (g[i].y > h) { g[i].y -= (g[i].y - h) * 2; g[i].vy *= -(1 - LOST); } else if (g[i].y < 0) { g[i].y += g[i].y * -2; g[i].vy *= -(1 - LOST); } fillRound(g[i].x, g[i].y, g[i].mass, COLOR) for (var i1 = 0; i1 < count; i1++) { if (i1 != i) { var r = Math.sqrt(Math.pow((g[i].x - g[i1].x), 2) + Math.pow((g[i].y - g[i1].y), 2)); var f = r != 0 ? G * G * g[i1].mass / Math.pow(r, 2) : 0; var dx = g[i1].x - g[i].x; var dy = g[i1].y - g[i].y; if (g[i].mass > 0) { g[i].vy += f / r * dy; g[i].vx += f / r * dx; } if (i1 > i && r <= maxLineLength) { drawLine(g[i].x, g[i].y, g[i1].x, g[i1].y, LINE_COLOR) } } } // var r = Math.max(1, Math.sqrt(Math.pow((g[i].x - mousepoint.x), 2) + Math.pow((g[i].y - mousepoint.y), 2))); if (r > maxLineLength / 8) { var f = r != 0 ? G * G * MOUSE_MASS / Math.pow(r, 2) : 0; var dx = mousepoint.x - g[i].x; var dy = mousepoint.y - g[i].y; if (g[i].mass > 0) { g[i].vy += f / r * dy; g[i].vx += f / r * dx; } if (r <= maxLineLength) { drawLine(g[i].x, g[i].y, mousepoint.x, mousepoint.y, LINE_COLOR); } } // var speedNow = Math.sqrt(Math.pow(g[i].vx, 2) + Math.pow(g[i].vy, 2)) if (speedNow > MAX_SPEED) { g[i].vx = g[i].vx * MAX_SPEED / speedNow; g[i].vy = g[i].vy * MAX_SPEED / speedNow; } g[i].x += g[i].vx; g[i].y += g[i].vy; g[i].lock--; } } // init(PLANET_COUNT); setInterval(function () { var w = document.body.clientWidth; var h = document.body.clientHeight; maxLineLength = Math.max(30, Math.min(w / 10, h / 10)); canvas.setAttribute('width', w); canvas.setAttribute('height', h); canvas.style.width = w + 'px'; canvas.style.height = h + 'px'; step(); }, 30) </script>