如何用SVG绘制与大六边形各边对齐的每组16个小六边形?
六边形布局问题修复方案
问题概述
尝试绘制一个大六边形,内部放置96个小六边形(6组,每组16个,与大六边形各边对齐),要求大六边形与小六边形间有1px间距,小六边形间也有小间距,但当前布局存在对齐错误,大六边形边线会穿过小六边形。
现有代码
JavaScript
const PADDING = 4; const HEX_SIZE = 9 - PADDING; // 用内边距值减小六边形尺寸 const sqrt3 = Math.sqrt(3); function createHexagon(x, y, radius) { const points = []; for (let i = 0; i < 6; i++) { const angle = 2 * Math.PI / 6 * i; const pointX = x + radius * Math.cos(angle); const pointY = y + radius * Math.sin(angle); points.push([pointX, pointY]); } return points; } const svg = d3.select('#hexagonSVG'); function createHexagonLine(startX, startY, angle, count) { // 绘制参考线 svg.append('line') .attr('x1', startX) .attr('y1', startY) .attr('x2', startX + (sqrt3 * HEX_SIZE + PADDING) * count * Math.cos(angle)) .attr('y2', startY + (sqrt3 * HEX_SIZE + PADDING) * count * Math.sin(angle)) .attr('stroke', 'black') .attr('stroke-width', 1); for (let i = 0; i < count; i++) { const x = startX + (sqrt3 * HEX_SIZE + PADDING) * i * Math.cos(angle); const y = startY + (sqrt3 * HEX_SIZE + PADDING) * i * Math.sin(angle); const hexagonPoints = createHexagon(x, y, HEX_SIZE); svg.append('polygon') .attr('points', hexagonPoints.join(' ')) .attr('class', 'small'); } return [startX + (sqrt3 * HEX_SIZE + PADDING) * 16 * Math.cos(angle), startY + (sqrt3 * HEX_SIZE + PADDING) * 16 * Math.sin(angle)] } l = createHexagonLine(260, 140 + 2 * HEX_SIZE, 0, 16) x = l[0] y = l[1] for (let i = 1; i < 6; i++) { const angle = 2 * Math.PI / 6 * i; l = createHexagonLine(x, y + 2 * HEX_SIZE, angle, 16); x = l[0] y = l[1] }
CSS
svg { background: #ddd; } .small { fill: #666; stroke: #333; stroke-width: 1px; }
HTML
<script src="https://d3js.org/d3.v5.min.js"></script> <svg id="hexagonSVG" width="800" height="800"></svg>
当前错误效果

问题原因与修复
关键问题点
- 间距计算错误:正六边形相邻中心的横向间距应为
2 * HEX_SIZE + PADDING,纵向(斜向)间距应为sqrt(3) * HEX_SIZE + PADDING,原代码混用了这两个值。 - 起始位置与角度偏移错误:每组小六边形的起始点没有根据大六边形的边正确偏移,导致对齐失效。
- 大六边形未适配小六边形布局:原代码未绘制正确尺寸的大六边形,且未考虑与小六边形的间距。
修正后的代码
JavaScript
const PADDING = 1; // 小六边形间及与大六边形的间距 const HEX_SIZE = 9; // 小六边形的外接圆半径 const sqrt3 = Math.sqrt(3); const GROUP_COUNT = 16; // 每组小六边形数量 function createHexagon(x, y, radius) { const points = []; // 调整起始角度为-60度,让六边形的边与坐标轴对齐(方便布局) for (let i = 0; i < 6; i++) { const angle = (2 * Math.PI / 6 * i) - Math.PI / 3; const pointX = x + radius * Math.cos(angle); const pointY = y + radius * Math.sin(angle); points.push([pointX, pointY]); } return points; } const svg = d3.select('#hexagonSVG'); const centerX = 400; const centerY = 400; // 计算大六边形的半径:小六边形组的总长度 + 间距 + 小六边形半径 const bigHexRadius = (GROUP_COUNT * (2 * HEX_SIZE + PADDING)) / 2 + HEX_SIZE + PADDING; // 绘制大六边形 const bigHexPoints = createHexagon(centerX, centerY, bigHexRadius); svg.append('polygon') .attr('points', bigHexPoints.join(' ')) .attr('fill', 'none') .attr('stroke', 'black') .attr('stroke-width', 1); // 生成6组小六边形,每组对应大六边形的一条边 for (let edge = 0; edge < 6; edge++) { // 每组的起始角度:对应大六边形边的中点方向 const baseAngle = (2 * Math.PI / 6 * edge) - Math.PI / 3; // 起始点:大六边形内侧,距离大六边形边PADDING + HEX_SIZE的位置 const startX = centerX + (bigHexRadius - HEX_SIZE - PADDING) * Math.cos(baseAngle); const startY = centerY + (bigHexRadius - HEX_SIZE - PADDING) * Math.sin(baseAngle); // 每组小六边形排列的方向:与大六边形当前边平行的方向 const lineAngle = baseAngle + Math.PI / 2; for (let i = 0; i < GROUP_COUNT; i++) { // 计算每个小六边形的中心位置 const offset = i * (2 * HEX_SIZE + PADDING); const x = startX + offset * Math.cos(lineAngle); const y = startY + offset * Math.sin(lineAngle); const hexPoints = createHexagon(x, y, HEX_SIZE); svg.append('polygon') .attr('points', hexPoints.join(' ')) .attr('class', 'small'); } }
CSS(保持不变)
svg { background: #ddd; } .small { fill: #666; stroke: #333; stroke-width: 1px; }
HTML(保持不变)
<script src="https://d3js.org/d3.v5.min.js"></script> <svg id="hexagonSVG" width="800" height="800"></svg>
修复说明
- 调整六边形绘制角度:将六边形的起始角度设为-60度,让其边与坐标轴平行,简化布局计算。
- 正确计算间距:横向(平行于边)的小六边形中心间距为
2 * HEX_SIZE + PADDING,确保小六边形间的间距一致。 - 大六边形尺寸适配:根据小六边形组的总长度、间距和小六边形半径,计算出大六边形的半径,确保大六边形在小六边形外侧,间距符合要求。
- 分组起始位置校准:每组小六边形的起始点位于大六边形内侧,与大六边形边保持
PADDING + HEX_SIZE的距离,排列方向与对应大六边形边平行,完美对齐。
内容的提问来源于stack exchange,提问作者Andre Hissa
相关产品推荐
相关产品推荐

