Позиционирование divs в круге с использованием JavaScript

Я пытаюсь позиционировать 15 div элементов равномерно по кругу с радиусом 150px. Я использую следующий код, который, кажется, дает странно эксцентричный эллипс, который перекрывается.

Fiddle

// Hold a global reference to the div#main element.  Initially assign it ... somewhere useful :)
var main = document.getElementById('main');
var circleArray = [];

// Move a circle based on the distance of the approaching mouse
var moveCircle = function(circle, dx, dy) {

};

// Look at all the circle elements, and figure out if any of them have to move.
var checkMove = function() {

};
var setup = function() {
  for (var i = 0; i < 15; i++) {
    //create element, add it to the array, and assign it coordinates trigonometrically.
    //Then add it to the "main" div
    var circle = document.createElement('div');
    circle.className = 'circle number' + i;
    circleArray.push(circle);
    circleArray[i].posx = Math.round((150 * Math.cos(i * (2 * Math.PI / 15)))) + 'px';
    circleArray[i].posy = Math.round((150 * Math.sin(i * (2 * Math.PI / 15)))) + 'px';
    circleArray[i].style.position = "relative";
    circleArray[i].style.top = circleArray[i].posy;
    circleArray[i].style.left = circleArray[i].posx;
    main.appendChild(circleArray[i]);
  }
};
setup();
window.addEventListener('load', function() {

});
div {
  box-sizing: border-box;
}
div#main {
  position: absolute;
  left: 50%;
  top: 50%;
}
div.circle {
  position: absolute;
  width: 20px;
  height: 20px;
  border: 2px solid black;
  border-radius: 10px;
  -webkit-border-radius: 10px;
  -moz-border-radius: 10px;
}
<div id="main"></div>

Ответ 1

Прежде всего, уравнение для координаты на окружности просто:

(x, y) = (r * cos(θ), r * sin(θ))

где r - радиус окружности, а θ - угол в радианах.


Причина, по которой ваш код создает эксцентричный эллипс, заключается в том, что, когда вы назначаете значения CSS .top и .left, вы не считаете, что он фактически возьмет верхний левый угол в качестве ссылки. Я исправил ваш код, и теперь он создает идеальный круг.

Изменения, внесенные в ваш код:

  • Добавлен массив theta, который содержит все углы.

    var theta = [0, Math.PI / 6, Math.PI / 4, Math.PI / 3, Math.PI / 2, 2 * (Math.PI / 3), 3 * (Math.PI / 4), 5 * (Math.PI / 6), Math.PI, 7 * (Math.PI / 6), 5 * (Math.PI / 4), 4 * (Math.PI / 3), 3 * (Math.PI / 2), 5 * (Math.PI / 3), 7 * (Math.PI / 4), 11 * (Math.PI / 6)];
    

    На рисунке ниже показаны все углы, которые я использовал.

    enter image description here

  • Добавлен массив colors, который содержит разные цвета.

    var colors = ['red', 'green', 'purple', 'black', 'orange', 'yellow', 'maroon', 'grey', 'lightblue', 'tomato', 'pink', 'maroon', 'cyan', 'magenta', 'blue', 'chocolate', 'DarkSlateBlue'];
    
  • Сделаны изменения в ваших тригонометрических уравнениях.

    circleArray[i].posx = Math.round(radius * (Math.cos(theta[i]))) + 'px';
    circleArray[i].posy = Math.round(radius * (Math.sin(theta[i]))) + 'px';
    
  • Изменен способ назначения .top и .left.

    circleArray[i].style.top = ((mainHeight / 2) - parseInt(circleArray[i].posy.slice(0, -2))) + 'px';
    circleArray[i].style.left = ((mainHeight / 2) + parseInt(circleArray[i].posx.slice(0, -2))) + 'px';
    

    где mainHeight - высота #main div.


[1] 16 div s

Демо на Fiddle

var setup = function() {
  var radius = 150;
  var main = document.getElementById('main');
  var mainHeight = parseInt(window.getComputedStyle(main).height.slice(0, -2));
  var theta = [0, Math.PI / 6, Math.PI / 4, Math.PI / 3, Math.PI / 2, 2 * (Math.PI / 3), 3 * (Math.PI / 4), 5 * (Math.PI / 6), Math.PI, 7 * (Math.PI / 6), 5 * (Math.PI / 4), 4 * (Math.PI / 3), 3 * (Math.PI / 2), 5 * (Math.PI / 3), 7 * (Math.PI / 4), 11 * (Math.PI / 6)];
  var circleArray = [];
  var colors = ['red', 'green', 'purple', 'black', 'orange', 'yellow', 'maroon', 'grey', 'lightblue', 'tomato', 'pink', 'maroon', 'cyan', 'magenta', 'blue', 'chocolate', 'DarkSlateBlue'];
  for (var i = 0; i < 16; i++) {
    var circle = document.createElement('div');
    circle.className = 'circle number' + i;
    circleArray.push(circle);
    circleArray[i].posx = Math.round(radius * (Math.cos(theta[i]))) + 'px';
    circleArray[i].posy = Math.round(radius * (Math.sin(theta[i]))) + 'px';
    circleArray[i].style.position = "absolute";
    circleArray[i].style.backgroundColor = colors[i];
    circleArray[i].style.top = ((mainHeight / 2) - parseInt(circleArray[i].posy.slice(0, -2))) + 'px';
    circleArray[i].style.left = ((mainHeight / 2) + parseInt(circleArray[i].posx.slice(0, -2))) + 'px';
    main.appendChild(circleArray[i]);
  }
};
setup();
div#main {
  height: 300px;
  width: 300px;
  position: absolute;
  margin: 0 auto;
  transform: translate(-50%, -50%);
  top: 50%;
  left: 50%;
}
div.circle {
  position: absolute;
  width: 20px;
  height: 20px;
  border: 2px solid black;
  border-radius: 50%;
}
body {
  margin: 0 auto;
  background: papayawhip;
}
<div id="main"></div>

Ответ 2

Другой подход без JS

chipChocolate.py anser довольно завершен, но есть другой способ достижения вашей цели. Это проще и не требует JS.

Цель состоит в том, чтобы думать "круг" и вращение, а не полагаться на координаты [x,y]:

Вам нужно вставить все элементы и применить к ним поворот. Поскольку они вложены, элемент n + 1 будет вращаться в соответствии с ним прямым родительским вращением. Вот DEMO:

.circle, .circle div {
    width:24px; height:300px;
    position:absolute;
    left:50%; top:50px;
}
.circle:before, .circle div:before {
    content:'';
    display:block;
    width:20px; height:20px;
    border: 2px solid black;
    border-radius: 100%;
}
.circle div {
    top:0; left:0;
    -webkit-transform : rotate(24deg);
    -ms-transform : rotate(24deg);
    transform : rotate(24deg);
}
<div class="circle">
    <div><div><div><div><div><div><div><div><div><div><div><div><div><div><div>
    </div></div></div></div></div></div></div></div></div></div></div></div></div></div></div>
</div>

Ответ 3

  • Установите положение "абсолютное". Это позволит "сверху" и "левому" поместить divs из (0, 0). Использование "relative" будет позиционировать divs там, где они обычно будут выложены.

  • Измените центральную точку вашего круга от (0, 0) до другого, например (250, 250).

    circleArray[i].posx = 250 + Math.round((150*Math.cos(i*(2*Math.PI/15)))) + 'px';
    circleArray[i].posy = 250 + Math.round((150*Math.sin(i*(2*Math.PI/15)))) + 'px';
    circleArray[i].style.position = "absolute";
    

Ответ 4

Еще одно решение, основанное на идеях других решений, которые я видел

http://jsfiddle.net/0hr1n7a2/6/

(function() {
  var radians, radius;

  radius = 150;
    
  var totalItems = 48
  var item = 0;
  function positionTarget() 
  {
    var x, y, angle = 0, step = (2*Math.PI) / totalItems;
    var width = $('#container').width()/2;
    var height = $('#container').height()/2;
    var itemW = 20, itemH = 2;
    var deg = 0;    
 while(item <= totalItems)
 {        
  x = Math.round(width + radius * Math.cos(angle) - itemW/2);
  y = Math.round(height + radius * Math.sin(angle) - itemH/2);        
        //console.log(x + "," + y);
     
  $('#container').append('<div id="'+ item +'"/>')
  $('div#'+item).css('position', 'absolute')
        .css('width', itemW+'px').css('height', itemH+'px')      
        .css('left', x+'px').css('top', y+'px')
        .css('background-color', 'blue')
        .css('transform-origin', x+'px' -y+'px')        
        .css('transform', 'rotate('+ deg +'deg)')
        .css('border', 'solid 1px #000');
     
        angle += step;
        ++item;
        deg += 360/totalItems;
     
        //console.log(deg)
    }
  }

  $('#theButton').on('click', function()
  {
        positionTarget();
  })
    
})();
#container { width: 600px; height: 600px; border: 1px solid #000; position: relative; }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<input type="button" id="theButton" value="Draw">    
<div id="container">
</div>