
| <!DOCTYPE html> |
| <html lang="en"> |
| <head> |
| <meta charset="UTF-8" /> |
| <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0" /> |
| <title>Document</title> |
| <style> |
| .canvas { |
| width: 100%; |
| height: calc(100vh - 24px); |
| border: 1px solid black; |
| box-sizing: border-box; |
| } |
| </style> |
| </head> |
| <body> |
| <canvas class="canvas"></canvas> |
| |
| <script> |
| |
| const canvas = document.querySelector('.canvas'); |
| const ctx = canvas.getContext('2d'); |
| |
| const pointMap = new Map(); |
| |
| canvas.width = canvas.clientWidth * window.devicePixelRatio; |
| canvas.height = canvas.clientHeight * window.devicePixelRatio; |
| |
| canvas.addEventListener('mousemove', (e) => { |
| const rect = canvas.getBoundingClientRect(); |
| let x = e.clientX - rect.left, |
| y = e.clientY - rect.top; |
| x = (x * window.devicePixelRatio).toFixed(2); |
| y = (y * window.devicePixelRatio).toFixed(2); |
| |
| ctx.clearRect(0, 0, canvas.width, canvas.height); |
| renderPoints(); |
| computedPosition(x, y); |
| }); |
| |
| randomPoints(canvas.clientWidth, canvas.clientHeight, 20); |
| renderPoints(); |
| |
| |
| function computedPosition(x, y) { |
| |
| for (const [key, { x: pointX, y: pointY, color }] of pointMap) { |
| const distance = Math.sqrt((x - pointX) ** 2 + (y - pointY) ** 2); |
| if (distance < 200) { |
| |
| ctx.beginPath(); |
| ctx.moveTo(x, y); |
| ctx.lineTo(pointX, pointY); |
| ctx.strokeStyle = color; |
| ctx.stroke(); |
| } |
| } |
| } |
| |
| |
| function renderPoints() { |
| for (const { x, y, color } of pointMap.values()) { |
| |
| drawCircle(x, y, color); |
| } |
| } |
| |
| |
| function randomPoints(boundWidth, boundHeight, count = 0) { |
| if (!count) return; |
| |
| count = parseInt(count); |
| |
| const minWidth = 0, |
| minHeight = 0, |
| maxWidth = boundWidth, |
| maxHeight = boundHeight; |
| |
| for (let i = 0; i < count; i++) { |
| let x = randomPoint(minWidth, maxWidth), |
| y = randomPoint(minHeight, maxHeight); |
| |
| x = Math.floor(x / 10) * 10; |
| y = Math.floor(y / 10) * 10; |
| |
| pointMap.set(i, { x, y, color: randomColor() }); |
| } |
| } |
| |
| |
| function randomPoint(min, max) { |
| return Math.floor(Math.random() * (max - min + 1)) + min; |
| } |
| |
| |
| function randomColor() { |
| const letters = '0123456789ABCDEF'; |
| let color = '#'; |
| for (let i = 0; i < 6; i++) { |
| color += letters[Math.floor(Math.random() * 16)]; |
| } |
| return color; |
| } |
| |
| |
| function drawCircle(x, y, color = 'black') { |
| ctx.beginPath(); |
| ctx.arc(x, y, 10, 0, 2 * Math.PI); |
| ctx.fillStyle = color; |
| ctx.fill(); |
| } |
| </script> |
| </body> |
| </html> |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!