用JavaScript交点公式绘制生命之花遇阻,求技术解决方案
绘制生命之花(Flower of Life)的问题与解决方法
我参考圆交点公式尝试用JavaScript绘制生命之花,目前仅完成前7个初始花瓣圆,无法绘制剩余19个圆,数学基础薄弱找不到问题。当前代码只支持绘制8个圆,如下:
原代码
HTML
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>Flower of life</title> </head> <body> <div class="background"> <div id="fol" class="container"> </div> </div> </body> </html>
CSS
body { margin: 0; background-color: black; } .container { width: 100vw; height: 100vh; display: flex; justify-content: center; align-items: center; } .circle { width: 150px; height: 150px; position: absolute; border-radius: 100%; background-color: rgba(255, 255, 255, 0.1); box-shadow: 0 0 10px rgba(255, 255, 255, 1); }
JavaScript
window.addEventListener('load', function () { let distanceX = 75, distanceY = 75, angle = 60, circles = 8; for (var i = 0; i < circles; i++) { let div = document.createElement('div'); div.id = 'c' + i; div.className = 'circle'; document.getElementById('fol').appendChild(div); let circle = document.getElementById('c' + i); if (i == 0) { circle.style.transform = "translate(" + 0 + "px," + 0 + "px)"; } else { if (i > 6) { let j = i - 2, k = i - 1; let previouscircle = document.getElementById('c' + j).getBoundingClientRect(); let lastcircle = document.getElementById('c' + k).getBoundingClientRect(); var x0 = previouscircle.left; var y0 = previouscircle.top; var x1 = lastcircle.left; var y1 = lastcircle.top; console.log("previous (" + j + ") " + x0 + " " + y0); console.log("last (" + k + ") " + x1 + " " + y1); function intersection(x0, y0, r0, x1, y1, r1) { var a, dx, dy, d, h, rx, ry; var x2, y2; dx = x1 - x0; dy = y1 - y0; d = Math.sqrt((dy * dy) + (dx * dx)); a = ((r0 * r0) - (r1 * r1) + (d * d)) / (2.0 * d); x2 = x0 + (dx * a / d); y2 = y0 + (dy * a / d); h = Math.sqrt((r0 * r0) - (a * a)); rx = -dy * (h / d); ry = dx * (h / d); var xi = x2 + rx; var xi_prime = x2 - rx; var yi = y2 + ry; var yi_prime = y2 - ry; return [xi, xi_prime, yi, yi_prime]; } let result = intersection(x0, y0, distanceX, x1, y1, distanceY); let resultX = result[1]; let resultY = result[3]; circle.style.left = resultX + "px"; circle.style.top = resultY + "px"; } circle.style.transform = "translate(" + distanceX * Math.cos(Math.PI / 180 * angle * i) + "px," + distanceY * Math.sin(Math.PI / 180 * angle * i) + "px)"; } let limits = circle.getBoundingClientRect(); console.log("c" + i + " " + limits.left + " " + limits.top); } });
目标效果

问题修复与完整实现
核心问题点
- 混用
transform: translate和left/top定位,导致坐标计算混乱 - 依赖页面绝对位置
getBoundingClientRect(),受容器居中布局干扰 - 后续圆的生成逻辑错误,未基于已有圆的交点迭代扩展
修改后的完整代码
HTML(仅新增容器定位属性)
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>Flower of life</title> </head> <body> <div class="background"> <div id="fol" class="container"> </div> </div> </body> </html>
CSS(调整坐标对齐逻辑)
body { margin: 0; background-color: black; } .container { width: 100vw; height: 100vh; display: flex; justify-content: center; align-items: center; position: relative; /* 让子元素绝对定位相对容器 */ } .circle { width: 150px; height: 150px; position: absolute; border-radius: 100%; background-color: rgba(255, 255, 255, 0.1); box-shadow: 0 0 10px rgba(255, 255, 255, 1); transform: translate(-50%, -50%); /* 让元素中心对齐left/top坐标 */ }
JavaScript(重构生成逻辑)
window.addEventListener('load', function () { const radius = 75; // 圆的半径(对应直径150px) const container = document.getElementById('fol'); const circles = []; const existingCenters = new Set(); // 去重,避免重复绘制同一圆心 // 生成圆心唯一标识(解决浮点精度问题) function getCenterKey(x, y) { return `${Math.round(x)},${Math.round(y)}`; } // 绘制单个圆并记录圆心 function drawCircle(x, y) { const key = getCenterKey(x, y); if (existingCenters.has(key)) return; existingCenters.add(key); const div = document.createElement('div'); div.className = 'circle'; div.style.left = `${x}px`; div.style.top = `${y}px`; container.appendChild(div); circles.push({ x, y }); } // 计算两个等半径圆的交点 function getIntersections(x0, y0, x1, y1) { const dx = x1 - x0; const dy = y1 - y0; const d = Math.sqrt(dx*dx + dy*dy); // 两圆不相交或重合时返回空数组 if (d > 2*radius || d === 0) return []; const a = d / 2; const h = Math.sqrt(radius*radius - a*a); const midX = (x0 + x1) / 2; const midY = (y0 + y1) / 2; // 计算垂直平分线方向的偏移量 const rx = -dy * (h / d); const ry = dx * (h / d); return [ { x: midX + rx, y: midY + ry }, { x: midX - rx, y: midY - ry } ]; } // 1. 绘制中心圆 drawCircle(0, 0); // 2. 绘制周围6个正六边形圆 const angleStep = (Math.PI * 2) / 6; for (let i = 0; i < 6; i++) { const angle = i * angleStep; const x = radius * Math.cos(angle); const y = radius * Math.sin(angle); drawCircle(x, y); } // 3. 迭代生成剩余所有圆 let index = 0; while (index < circles.length) { const current = circles[index]; // 和后续每个圆计算交点,生成新圆 for (let j = index + 1; j < circles.length; j++) { const target = circles[j]; const intersections = getIntersections(current.x, current.y, target.x, target.y); intersections.forEach(pt => drawCircle(pt.x, pt.y)); } index++; } });
关键优化说明
- 统一使用容器相对坐标,通过
left/top+transform: translate(-50%,-50%)精准对齐圆心 - 加入去重机制,避免重复绘制同一位置的圆
- 采用迭代生成逻辑:从初始7个圆开始,两两计算交点并绘制新圆,直到覆盖生命之花全部26个圆
内容的提问来源于stack exchange,提问作者Claude Dumont
相关产品推荐
相关产品推荐

