Draw rectangles along a circle using html5, css3 and js

Problem

I have to draw 32 rectangles along a circle using html5, css3 and js. You can see my goal with this image:

myGoal http://i50.tinypic.com/mc8z1f.png.

My designed solution was to create "a for" in js that would generate 32 rectangles each time increasing rotation, coordinates and z-index. I tried but it is too intricate.

Please help me.

Problem courtesy of: Riccardo Giacomini

Solution

Here's a new version, updated to show the OP's requirement to have an actual DOM element for each rectangle, and not a single canvas:

var n = 32;

for (var i = 0; i < n; ++i) {
    var d = document.createElement('div');
    var r = 360 * i / n;
    var s = 'translate(200px,200px) rotate(' + r + 'deg) translate(0px, -180px)';

    d.setAttribute('class', 'box');
    d.setAttribute('style', '-webkit-transform:' + s);

    var t = document.createTextNode(i);
    d.appendChild(t);

    document.body.appendChild(d);
}​

working demo at http://jsfiddle.net/alnitak/CFAyf/ which contains some additional (and necessary) CSS

NB: you'll need to use browser detection to set the right -transform style attribute. The sample shown is correct for Chrome and Safari.

Solution courtesy of: Alnitak

Discussion

This creates a canvas like your picture - tune the variables as desired:

var canvas = document.getElementById('c');
var ctx = canvas.getContext('2d');

var w = canvas.width;
var h = canvas.height;

var r1 = Math.min(w, h) * 0.4;    // outer radius
var r0 = r1 - 40;                 // inner radius

var n = 32;                       // number of blocks

var theta = 2 * Math.PI / n;
var phi = theta * 0.45;           // relative half-block width

ctx.save();
ctx.fillStyle = '#c0c0c0';
ctx.translate(w / 2, h / 2);      // move to center of circle

for (var i = 0; i < n; ++i) {
    ctx.beginPath();
    ctx.arc(0, 0, r0, -phi, phi);
    ctx.arc(0, 0, r1, phi, -phi, true);
    ctx.fill();
    ctx.rotate(theta);            // rotate the coordinates by one block
}

ctx.restore();
​

working sample at http://jsfiddle.net/alnitak/qxZ5b/

EDIT this was incorrect (the OP wanted separate DOM elements, not a canvas) but is left here for reference. See my other answer for a DOM-based method.

Discussion courtesy of: Alnitak

You can do this with only CSS. Did it for another project and it works for ie9+ and all modern browsers.

This creates a circle with seperate DOM elements, clickable and what not.

<div class="wrapper" id="wrapper">
             <ul>
            <li><a href="#"><span class=""></span></a></li>
            <li><a href="#"><span class=""></span></a></li>
            <li><a href="#"><span class=""></span></a></li>
            <li><a href="#"><span class=""></span></a></li>
            <li><a href="#"><span class=""></span></a></li>
            <li><a href="#"><span class=""></span></a></li>
            <li><a href="#"><span class=""></span></a></li>
            <li><a href="#"><span class=""></span></a></li>
            <li><a href="#"><span class=""></span></a></li>
            <li><a href="#"><span class=""></span></a></li>
            <li><a href="#"><span class=""></span></a></li>
            <li><a href="#" ><span class=""></span></a></li>
             </ul>
            <div class="content"><p id="timeFirst"></p><br /><p>till</p><br /><p class="timeLast"></p></div>
            </div>
            </div>

and the css

.csstransforms .wrapper {
      display: block;
      font-size:1em;
      width: 10em;
          height: 10em;
          border-radius: 50%; 
          position: relative;
      overflow: hidden;
      margin: 0 auto;
      z-index: 99;
 }
.csstransforms .wrapper li
{
    list-style: none;
    position:absolute;
    overflow:hidden;
    -webkit-transform-origin: 100% 100%;
    -moz-transform-origin: 100% 100%;
    -ms-transform-origin: 100% 100%;
    -o-transform-origin: 100% 100%;
    transform-origin: 100% 100%;
    width: 10em;
    height: 10em;
    top: 50%;
    left: 50%;
    margin-top: -10em;
    margin-left: -10em;
    border-bottom: 1px solid white; 
}
.csstransforms .wrapper li a
{
    display: block;
    width: 10.5em;
    height: 10.5em;
    position: absolute;
    top:50%;
    left:50%;
    bottom: -7.5em;
    right: -7.5em;
    background-color: #D9D9D9; 
    text-align: center;
    -webkit-transform: skew(-60deg) rotate(-90deg) scale(2);
    -moz-transform: skew(-60deg) rotate(-90deg) scale(2);
    -ms-transform: skew(-60deg) rotate(-90deg) scale(2);
    -o-transform: skew(-60deg) rotate(-90deg) scale(2);
    transform: skew(-60deg) rotate(-90deg) scale(2);
}
.wrapper .content
{   
    position: absolute;
    width: 8em;
    height: 8em;
    border-radius: 50%;
    background-color:#FFFFFF;
    top:10%;
    left:10%;
    z-index: 25;
}
.csstransforms .wrapper li a:hover,
.csstransforms .wrapper li a:focus,
.csstransforms .wrapper li a:active
{
    background-color: #919191;
    cursor: pointer;
}
.csstransforms .wrapper li:nth-child(9) a
{
    background-color: #F9F9F9;
    cursor: default;

}
.csstransforms .wrapper li:nth-child(9) a:hover,
.csstransforms .wrapper li:nth-child(9) a:focus,
.csstransforms .wrapper li:nth-child(9) a:active
{
    background-color: #F9F9F9;
    cursor: default;

}
.active /*Later for use with JS */
{
    background-color:#32ADCF;
}
.csstransforms .wrapper li:first-child {
  -webkit-transform: rotate(30deg) skew(60deg);
  -moz-transform: rotate(30deg) skew(60deg);
  -ms-transform: rotate(30deg) skew(60deg);
  -o-transform: rotate(30deg) skew(60deg);
  transform: rotate(30deg) skew(60deg);

}

.csstransforms .wrapper li:nth-child(2) {
  -webkit-transform: rotate(60deg) skew(60deg);
  -moz-transform: rotate(60deg) skew(60deg);
  -ms-transform: rotate(60deg) skew(60deg);
  -o-transform: rotate(60deg) skew(60deg);
  transform: rotate(60deg) skew(60deg);

}

.csstransforms .wrapper li:nth-child(3) {
  -webkit-transform: rotate(90deg) skew(60deg);
  -moz-transform: rotate(90deg) skew(60deg);
  -ms-transform: rotate(90deg) skew(60deg);
  -o-transform: rotate(90deg) skew(60deg);
  transform: rotate(90deg) skew(60deg);
}
.csstransforms .wrapper li:nth-child(4) {
  -webkit-transform: rotate(120deg) skew(60deg);
  -moz-transform: rotate(120deg) skew(60deg);
  -ms-transform: rotate(120deg) skew(60deg);
  -o-transform: rotate(120deg) skew(60deg);
  transform: rotate(120deg) skew(60deg);
}
.csstransforms .wrapper li:nth-child(5) {
  -webkit-transform: rotate(150deg) skew(60deg);
  -moz-transform: rotate(150deg) skew(60deg);
  -ms-transform: rotate(150deg) skew(60deg);
  -o-transform: rotate(150deg) skew(60deg);
  transform: rotate(150deg) skew(60deg);
}

.csstransforms .wrapper li:nth-child(6) {
  -webkit-transform: rotate(180deg) skew(60deg);
  -moz-transform: rotate(180deg) skew(60deg);
  -ms-transform: rotate(180deg) skew(60deg);
  -o-transform: rotate(180deg) skew(60deg);
  transform: rotate(180deg) skew(60deg);
}
.csstransforms .wrapper li:nth-child(7) {
  -webkit-transform: rotate(210deg) skew(60deg);
  -moz-transform: rotate(210deg) skew(60deg);
  -ms-transform: rotate(210deg) skew(60deg);
  -o-transform: rotate(210deg) skew(60deg);
  transform: rotate(210deg) skew(60deg);
}
.csstransforms .wrapper li:nth-child(8) {
  -webkit-transform: rotate(240deg) skew(60deg);
  -moz-transform: rotate(240deg) skew(60deg);
  -ms-transform: rotate(240deg) skew(60deg);
  -o-transform: rotate(240deg) skew(60deg);
  transform: rotate(240deg) skew(60deg);
}
.csstransforms .wrapper li:nth-child(9) {
  -webkit-transform: rotate(270deg) skew(60deg);
  -moz-transform: rotate(270deg) skew(60deg);
  -ms-transform: rotate(270deg) skew(60deg);
  -o-transform: rotate(270deg) skew(60deg);
  transform: rotate(270deg) skew(60deg);
}
.csstransforms .wrapper li:nth-child(10) {
  -webkit-transform: rotate(300deg) skew(60deg);
  -moz-transform: rotate(300deg) skew(60deg);
  -ms-transform: rotate(300deg) skew(60deg);
  -o-transform: rotate(300deg) skew(60deg);
  transform: rotate(300deg) skew(60deg);
}
.csstransforms .wrapper li:nth-child(11) {
  -webkit-transform: rotate(330deg) skew(60deg);
  -moz-transform: rotate(330deg) skew(60deg);
  -ms-transform: rotate(330deg) skew(60deg);
  -o-transform: rotate(330deg) skew(60deg);
  transform: rotate(330deg) skew(60deg);
}
.csstransforms .wrapper li:nth-child(12) {
  -webkit-transform: rotate(360deg) skew(60deg);
  -moz-transform: rotate(360deg) skew(60deg);
  -ms-transform: rotate(360deg) skew(60deg);
  -o-transform: rotate(360deg) skew(60deg);
  transform: rotate(360deg) skew(60deg);
   z-index:-1; /* Last element index set to minus to fix a bug in IE9+ */

}

And the formula for rotating for you using 32 rectangles would be 360/32 = 11.25 as a central angle. Then you need to skew the rectangles to fit your central angle using the formula 90 - x( where X is your central angle) so 78,75. If you want content inside your rectangles you need to skew them back with the oposite value of that you used to skew the list items. Rotate back the content of the list items using the formula 90 -(x/2) where x is central angle. so for you it would be 90 -(11,25/2) = 84.4

Edited

Added a working JS fiddle so people can see it works. http://jsfiddle.net/AaJ94/

Discussion courtesy of: Björn Grunde

This isn't perfect, but it gets something very similar...

div {
   width: 200px;
   height: 200px;
   border: 10px dotted #E5E5E5;
   border-radius: 200px;    
}​

jsFiddle.

Example

Discussion courtesy of: alex

This recipe can be found in it's original form on Stack Over Flow.