You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

用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);
    }
});

目标效果

目标生命之花效果


问题修复与完整实现

核心问题点

  1. 混用transform: translate和left/top定位,导致坐标计算混乱
  2. 依赖页面绝对位置getBoundingClientRect(),受容器居中布局干扰
  3. 后续圆的生成逻辑错误,未基于已有圆的交点迭代扩展

修改后的完整代码

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.21 05:39:24