Creating a circular menu with CSS

The following is a way to do it with HTML canvas, and it detects where the mouse is perfectly. It doesn't look the exact same as yours though, and I didn't add the icons or dividing lines (although anti-aliasing allows the background to show through a little between regions creating the illusion of lines being drawn).

http://jsfiddle.net/jcubed111/xSajL/

Edit - Bug Fix: http://jsfiddle.net/jcubed111/xSajL/2/

With more work you could make the canvas version look the same as your mock-up, my version is only to get the functionality down.

You could also make it look right with css, then overlay a clear a to detect mouse position and provide linking functionality. Of course, then you couldn't use :hover to change the look of the regions.

I've tested in Chrome 19 only.

Here's the full code below in case the link goes down:

HTML:

<a id='link'><canvas id='c' width='224' height='224' onmousemove="update(event);"></canvas></a>
<input id='i' />​​​​​​​​

CSS:

#c{
    width:224px;
    height:224px;
}​

JS (run on page load and uses jquery):

ctx = $('#c')[0].getContext('2d');


function update(E) {
    ctx.clearRect(0, 0, 224, 224);
    if (E === false) {
        mx = 112;
        my = 112;
    } else {
        mx = E.clientX;
        my = E.clientY;
    }

    mangle = (-Math.atan2(mx-112, my-112)+Math.PI*2.5)%(Math.PI*2);
    mradius = Math.sqrt(Math.pow(mx - 112, 2) + Math.pow(my - 112, 2));

    $('#i').val("Not over any region");
    $('#link').attr('href', '');
    for (i = 0; i < 8; i++) {
        angle = -Math.PI / 8 + i * (Math.PI / 4);

        if (((mangle > angle && mangle < (angle + Math.PI / 4)) || (mangle > Math.PI*15/8 && i==0)) && mradius<=112 && mradius>=69) {
            ctx.fillStyle="#5a5a5a";
            $('#i').val("In region "+i);
            $('#link').attr('href', '#'+i);
        } else {
            ctx.fillStyle="#4c4c4c";
        }

        ctx.beginPath();
        ctx.moveTo(112, 112);
        //ctx.lineTo(112+Math.cos(angle)*112, 112+Math.sin(angle)*112);
        ctx.arc(112, 112, 112, angle, angle + Math.PI / 4, false);
        ctx.lineTo(112, 112);
        ctx.fill();


    }

    ctx.fillStyle = "#f2f2f2";
    ctx.beginPath();
    ctx.arc(112, 112, 69, 0, 2 * Math.PI, false);
    ctx.fill();
}

update(false);​

It could be done with regular HTML+CSS, but for the sake of your sanity, don't even try. It's not worth it.

You'd be far better off doing stuff like this in Canvas or SVG. Especially if you don't need to support older versions of IE.

For both Canvas and SVG solutions, I recommend finding a suitable library. In the SVG world, I strongly recommend Raphael. For Canvas, mayby try Paper. You could get the basics up and running with either of these libraries in just a handful of lines of code.

If you must do this in CSS (maybe that's the criteria for your project, or maybe you're just a glutton for punishment), you'll need to start with using border-radius to make the circle. Then draw the segmentation line using 1px-wide boxes, and rotate them using transform. You get the picture; it's not easy, as you've probably already discovered. And getting the thing animated is going to be complete nightmare. It can be done, and as a demonstration piece of what can be achieved in CSS, it would be very clever. But when all your target browsers support SVG and Canvas, doing stuff like this in CSS really is just being clever for the sake of being clever.


HTML Code

<a class='button ctrl' href='#' tabindex='1'>★</a>
<ul class='tip ctrl'>
    <li class='slice'><div>✦</div></li>
    <li class='slice'><div>✿</div></li>
    <li class='slice'><div>✵</div></li>
    <li class='slice'><div>✪</div></li>
    <li class='slice'><div>☀</div></li>
</ul>

CSS Code

    .ctrl {
        position: absolute;
        top: 75%; left: 50%;
        font: 1.5em/1.13 Verdana, sans-serif;
        transition: .5s;
    }

    a.ctrl, .ctrl div {
        display: block;
        opacity: .56;
        background: #c9c9c9;
        color: #7a8092;
        text-align: center;
        text-decoration: none;
        text-shadow: 0 -1px dimgrey;
      cursor: pointer;
    }

    .button {
        z-index: 2;
        margin: -.625em;
        width: 1.25em; height: 1.25em;
        border-radius: 50%;
        box-shadow: 0 0 3px 1px white;
    }

    .tip {
        z-index: 1;
        /**outline: dotted 1px white;/**/
        margin: -5em;
        width: 10em; height: 10em;
        transform: scale(.001);
        list-style: none;
        opacity: 0;
    }

.slice {
    overflow: hidden;
    position: absolute;
    /**outline: dotted 1px yellow;/**/
    width: 50%; height: 50%;
    transform-origin: 100% 100%;
}

Full code : CSSCodeLab

Tags:

Css