如何从中心圆的顶部开始,围绕中心圆绘制 x 个圆圈

How do I draw x number of circles around a central circle, starting at the top of the center circle?

本文关键字:绘制 顶部 开始      更新时间:2023-09-26

我正在尝试创建一个在圆圈中有很多项目的UI。 有时这些圆圈会有相关的圆圈,应该在它们周围显示。

我能够在这里拼凑出一些有用的东西。

问题是外圈开始接近 0 度,我希望它们以函数/库的使用者提供的角度开始。 我从来都不是三角学或几何学的明星,所以我可以使用一点帮助。

正如您在使用代码中看到的那样,有一个设置:getPosition应该遵循的函数startingDegree: 270,但我无法弄清楚如何。

更新 04

/02/2014:

正如我在对Salix alba的评论中提到的,我在上面并不清楚,但我需要的是能够指定卫星圆的半径,并且让它们只部分地绕行。 Salix给出了一个解决方案,可以计算卫星需要的大小才能均匀地围绕中心圆圈。

使用Salix答案中的一些提示,我能够达到预期的结果......并在未来多亏了Salix,有了额外的"模式"。

尽管仍然很粗糙,但有效的解决方案在这里:http://jsfiddle.net/RD4RZ/11/。 这是整个代码(只是为了让它全部在SO上):

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <title></title>
    <script type="text/javascript" src="//code.jquery.com/jquery-1.10.1.js"></script>
    <style type="text/css">
    .circle
        {
            position: absolute;
            width: 100px;
            height: 100px;
        background-repeat: no-repeat;background-position: center center;
        border: 80px solid #a19084;
        border-radius: 50%;
        -moz-border-radius: 50%;
        }
    .sm
        {
        border: 2px solid #a19084;
        }
  </style>
    <script type="text/javascript">//<![CDATA[ 
        $(function () {
            function sind(x) {
                return Math.sin(x * Math.PI / 180);
            }
            /*the law of cosines: 
            cc = aa + bb - 2ab cos(C), where c is the satellite diameter a and b are the legs
            solving for cos C, cos C = ( aa + bb - cc ) / 2ab
            Math.acos((a * a + b * b - c * c) / (2 * a * b)) = C
            */
            function solveAngle(a, b, c) {  // Returns angle C using law of cosines
                var temp = (a * a + b * b - c * c) / (2 * a * b);
                if (temp >= -1 && temp <= 1)
                    return radToDeg(Math.acos(temp));
                else
                    throw "No solution";
            }
            function radToDeg(x) {
                return x / Math.PI * 180;
            }
            function degToRad(x) {
                return x * (Math.PI / 180);
            }
            var satellite = {
                //settings must have: collection (array), itemDiameter (number), minCenterDiameter (number), center (json with x, y numbers)
                //optional: itemPadding (number), evenDistribution (boolean), centerPadding (boolean), noOverLap (boolean)
                getPosition: function (settings) {
                    //backwards compat
                    settings.centerPadding = settings.centerPadding || settings.itemPadding;
                    settings.noOverLap = typeof settings.noOverLap == 'undefined' ? true : settings.noOverLap;
                    settings.startingDegree = settings.startingDegree || 270;
                    settings.startSatellitesOnEdge = typeof settings.startSatellitesOnEdge == 'undefined' ? true : settings.startSatellitesOnEdge;
                    var itemIndex = $.inArray(settings.item, settings.collection);
                    var itemCnt = settings.collection.length;
                    var satelliteSide = settings.itemDiameter + (settings.itemSeparation || 0) + (settings.itemPadding || 0);
                    var evenDistribution = typeof settings.evenDistribution == 'undefined' ? true : settings.evenDistribution;
                    var degreeOfSeparation = (360 / itemCnt);
                    /*
                    we know all three sides:
                    one side is the diameter of the satellite itself (plus any padding). the other two
                    are the parent radius + the radius of the satellite itself (plus any padding).
                    given that, we need to find the angle of separation using the law of cosines (solveAngle)
                    */
                    //if (!evenDistribution) {
                        var side1 = ((satelliteSide / 2)) + ((settings.minCenterDiameter + (2 * settings.centerPadding)) / 2);
                        var side2 = satelliteSide;;

                        var degreeOfSeparationBasedOnSatellite = solveAngle(side1, side1, side2); //Math.acos(((((side1 * side1) + (side2 * side2)) - (side2 * side2)) / (side2 * side2 * 2)) / 180 * Math.PI) * Math.PI;
                        degreeOfSeparation = evenDistribution? degreeOfSeparation: settings.noOverLap ? Math.min(degreeOfSeparation, degreeOfSeparationBasedOnSatellite) : degreeOfSeparationBasedOnSatellite;
                    //}
                    //angle-angle-side
                    //a-A-B
                    var a = satelliteSide;
                    var A = degreeOfSeparation;
                    /*
                    the three angles of any triangle add up to 180.  We know one angle (degreeOfSeparation)
                    and we know the other two are equivalent to each other, so...
                    */
                    var B = (180 - A) / 2;
                    //b is length necessary to fit all satellites, might be too short to be outside of base circle
                    var b = a * sind(B) / sind(A);
                    var offset = (settings.itemDiameter / 2) + (settings.itemPadding || 0); // 1; //
                    var onBaseCircleLegLength = ((settings.minCenterDiameter / 2) + settings.centerPadding) + offset;
                    var offBase = false;
                    if (b > onBaseCircleLegLength) {
                        offBase = true;
                    }
                    b = settings.noOverLap ? Math.max(b, onBaseCircleLegLength) : onBaseCircleLegLength;
                    var radianDegree = degToRad(degreeOfSeparation);
                    //log('b=' + b);
                    //log('settings.center.x=' + settings.center.x);
                    //log('settings.center.y=' + settings.center.y);
                    var degreeOffset = settings.startingDegree;
                    if (settings.startSatellitesOnEdge) {
                        degreeOffset += ((offBase ? degreeOfSeparation : degreeOfSeparationBasedOnSatellite) / 2);
                    }
                    var i = ((Math.PI * degreeOffset) / 180) + (radianDegree * itemIndex);// + (degToRad(degreeOfSeparationBasedOnSatellite) / 2); //(radianDegree) * (itemIndex);
                    var x = (Math.cos(i) * b) + (settings.center.x - offset);
                    var y = (Math.sin(i) * b) + (settings.center.y - offset);
                    return { 'x': Math.round(x), 'y': Math.round(y) };
                }
                ,
                /* if we ever want to size satellite by how many need to fit tight around the base circle:
                x: function calcCircles(n) {
                    circles.splice(0); // clear out old circles
                    var angle = Math.PI / n;
                    var s = Math.sin(angle);
                    var r = baseRadius * s / (1 - s);
                    console.log(angle);
                    console.log(s);
                    console.log(r);
                    console.log(startAngle);
                    console.log(startAngle / (Math.PI * 2));
                    for (var i = 0; i < n; ++i) {
                        var phi = ((Math.PI * startAngle) / 180) + (angle * i * 2);
                        var cx = 150 + (baseRadius + r) * Math.cos(phi);
                        var cy = 150 + (baseRadius + r) * Math.sin(phi);
                        circles.push(new Circle(cx, cy, r));
                    }
                },
                */
                //settings must have: collection (array), itemDiameter (number), minCenterDiameter (number), center (json with x, y numbers)
                //optional: itemPadding (number), evenDistribution (boolean), centerPadding (boolean), noOverLap (boolean)
                getAllPositions: function (settings) {
                    var point;
                    var points = [];
                    var collection = settings.collection;
                    for (var i = 0; i < collection.length; i++) {
                        settings.item = collection[i]
                        points.push(satellite.getPosition(settings));
                    }
                    return points;
                }
            };
            var el = $("#center"), cnt = 10, arr = [], itemDiameter= 100;
            for (var c = 0; c < cnt; c++) {
                arr.push(c);
            }

            var settings = {
                collection: arr,
                itemDiameter: itemDiameter,
                minCenterDiameter: el.width(),
                center: { x: el.width() / 2, y: el.width() / 2 },
                itemPadding: 2,
                evenDistribution: false,
                centerPadding: parseInt(el.css("border-width")),
                noOverLap: false,
                startingDegree: 270
            };
            var points = satellite.getAllPositions(settings);
            for (var i = 0; i < points.length; i++) {
                var $newdiv1 = $("<div></div>");
                var div = el.append($newdiv1);
                $newdiv1.addClass("circle").addClass("sm");
                $newdiv1.text(i);
                $newdiv1.css({ left: points[i].x, top: points[i].y, width: itemDiameter +'px', height: itemDiameter +'px' });
            }
        });//]]>  
</script>
</head>
<body>
    <div id="center" class="circle" style="left:250px;top:250px"   >
</div>
</body>
</html>

您需要计算的中心位是小圆的半径。如果您R中心圆的半径,并且想要在其周围安装n较小的圆。让小圆圈的未知半径r。我们可以构造一个直角三角形,其中一个角在大圆的中心,一个在小圆的中心,另一个是一条从中心的线与小圆相切的地方。这将是一个直角。中心的角度是a斜边有长度R+r相反的角度是r的,我们不需要相邻的。使用三角

sin(a) = op / hyp = r / (R + r)

重排

(R+r) sin(a) = r
R sin(a) + r sin(a) = r
R sin(a) = r - r sin(a)
R sin(a) = (1 - sin(a)) r
r = R sin(a) / ( 1 - sin(a))

一旦我们有了r我们就差不多完成了。

你可以把这看作是一个小提琴 http://jsfiddle.net/SalixAlba/7mAAS/

// canvas and mousedown related variables
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
var $canvas = $("#canvas");
var canvasOffset = $canvas.offset();
var offsetX = canvasOffset.left;
var offsetY = canvasOffset.top;
var scrollX = $canvas.scrollLeft();
var scrollY = $canvas.scrollTop();
// save canvas size to vars b/ they're used often
var canvasWidth = canvas.width;
var canvasHeight = canvas.height;
var baseRadius = 50;
var baseCircle = new Circle(150,150,50);
var nCircles = 7;
var startAngle = 15.0;
function Circle(x,y,r) {
    this.x = x;
    this.y = y;
    this.r = r;
}
Circle.prototype.draw = function() {
    ctx.beginPath();
    ctx.arc(this.x,this.y,this.r, 0, 2 * Math.PI, false);
    ctx.stroke();
}
var circles = new Array();
function calcCircles(n) {
    circles.splice(0); // clear out old circles
    var angle = Math.PI / n;
    var s = Math.sin(angle);
    var r = baseRadius * s / (1-s);
    console.log(angle);
    console.log(s);
    console.log(r);

    for(var i=0;i<n;++i) {
        var phi = startAngle + angle * i * 2;
        var cx = 150+(baseRadius + r) * Math.cos(phi);
        var cy = 150+(baseRadius + r) * Math.sin(phi);
        circles.push(new Circle(cx,cy,r));
    }
}
function draw() {
    baseCircle.draw();
    circles.forEach(function(ele){ele.draw()});
}
calcCircles(7);
draw();