如何避免HTML5 Canvas中随机生成的坐标出现重叠问题
解决方法
你当前的场景只需要生成少量文本,最直接高效的方案是增加已生成坐标的距离校验逻辑,步骤如下:
- 新增数组存储已生成文本的坐标、宽高信息
- 每次生成符合三角形边界要求的新坐标后,校验该坐标和所有已存储坐标的间距是否大于预设的最小安全距离
- 校验通过后再绘制文本,同时把当前文本的信息存入数组
你可以直接使用修改后的代码:
JS代码
function randomIntFromInterval(min, max) { return Math.floor(Math.random() * (max - min + 1) + min) } function area(x1, y1, x2, y2, x3, y3) { return Math.abs((x1 * (y2 - y3) + x2 * (y3 - y1) + x3 * (y1 - y2)) / 2.0); } function isInside(x1, y1, x2, y2, x3, y3, x, y) { let A = area(x1, y1, x2, y2, x3, y3); let A1 = area(x, y, x2, y2, x3, y3); let A2 = area(x1, y1, x, y, x3, y3); let A3 = area(x1, y1, x2, y2, x, y); return (A == A1 + A2 + A3); } // 新增:校验新坐标和已有文本是否重叠 function checkOverlap(newText, existingTexts, minGapX = 8, minGapY = 4) { for (let text of existingTexts) { // 横向重叠判断:两个文本的水平区间有交集 const xOverlap = !(newText.x + newText.width + minGapX < text.x || text.x + text.width + minGapX < newText.x) // 纵向重叠判断:两个文本的垂直区间有交集 const yOverlap = !(newText.y - newText.height + minGapY < text.y || text.y - text.height + minGapY < newText.y) if (xOverlap && yOverlap) return true } return false } let canvas = document.querySelector('canvas') const CANVAS_SIZE = 300 canvas.width = CANVAS_SIZE canvas.height = CANVAS_SIZE const ctx = canvas.getContext('2d'); let arrayOfText = ['A', 'B', 'C', 'D'] // 新增:存储已生成的文本信息 let placedTexts = [] // 字体大小固定,提前算文本高度(行高一般是字体大小的1.1-1.2倍) const FONT_SIZE = 14 const TEXT_HEIGHT = FONT_SIZE * 1.2 ctx.beginPath(); ctx.moveTo(25, 25); ctx.lineTo(225, 25); ctx.lineTo(225, 225); ctx.lineTo(25, 25); ctx.strokeStyle = "#bb0874" ctx.stroke() ctx.font = `600 ${FONT_SIZE}px Arial`; for (let i = 0; i < arrayOfText.length; i++) { const currentText = arrayOfText[i] // 先算当前文本的宽度 const textWidth = ctx.measureText(currentText).width while (true) { let x = randomIntFromInterval(40, 180) let y = randomIntFromInterval(15, 160) if (isInside(25, 25, 225, 25, 225, 225, x, y)) { const newTextInfo = { x: x, y: y, width: textWidth, height: TEXT_HEIGHT } // 校验和已有文本是否重叠 if (!checkOverlap(newTextInfo, placedTexts)) { ctx.fillText(currentText, x, y); placedTexts.push(newTextInfo) break; } } } }
CSS代码
canvas { border: 1px solid black; }
HTML代码
<body> <canvas></canvas> </body>
如果后续需要生成数十个以上的文本,可改用网格预划分方案:将三角形区域按最小文本宽高划分为网格,提前标记所有落在三角形内的网格,每次随机抽取未使用的网格放置文本,不需要反复做重叠校验,性能更高。
内容的提问来源于stack exchange,提问作者Hasala
相关产品推荐
相关产品推荐

