Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Radial Pie Menu With Raphael JS

Thanks for looking at my dilemma. I'm trying to make an SVG menu with raphael, and I'm terrible with geometry.

The below image shows what I'm making. I already created the blue / center part with CSS but it seems that there's really no other good way to get the white / outer part. Images & CSS kind of fail in this regard due to the block nature of these elements. I would like to generate a series of arcs that could range in size depending on the number of elements.

Radial Menu Example

So, any advice on how to go about getting a series of clickable arcs that form a quarter circle and can have hover effects? I'm going to want to place icons on these things as well, if that matters.

I have seen a few examples of using the pie chart in raphael, and I just don't see how to adapt it. Ugh. I should have paid attention in geometry.

Thanks for your time.

like image 565
apttap Avatar asked Sep 14 '12 18:09

apttap


1 Answers

The geometry isn't really that bad, at least it's not as annoying as the elliptical arc path syntax in SVG. Both are manageable.

Here's a function that will generate a series of arc segments as paths, handing each one (along with arbitrary metadata for that item) to a callback function:

function generateRadialNav( paper,        // Raphael's paper object
                            cx, cy,       // x and y coordinates of the center of the circle from which the arcs will be calculated
                            inner_radius, // pixels from origin to interior arc
                            radial_thickness,  // width of navigation area in pixels
                            menu_items,   // an object with one key per menu item; the value of each key is arbitrary
                            callback,     // a finalization callback for menu items which should accept three arguments: the menu_items key, the path element, and the menu_items value.  The first and third parameters are taken directly from the menu_items object.
                            options )     // Unused but added out of habit.
{
    options = options || {};

    var start_radians = options.start_radians || 0;
    var end_radians = options.end_radians || Math.PI / 2;
    var menu_item_count = 0;
    for ( var k in menu_items )
        menu_item_count++;

    var slice_radians = ( end_radians - start_radians ) / menu_item_count;

    var index = 0;
    for ( var k in menu_items )
    {
        var path = [];
        path.push( "M", cx + Math.cos( start_radians + slice_radians * index ) * inner_radius, cy + Math.sin( start_radians + slice_radians * index ) * inner_radius );
        path.push( "L", cx + Math.cos( start_radians + slice_radians * index ) * ( inner_radius + radial_thickness ), cy + Math.sin( start_radians + slice_radians * index ) * ( inner_radius + radial_thickness ) );
        path.push( "A", 
                inner_radius + radial_thickness, inner_radius + radial_thickness,
                slice_radians, 0, 1,
                cx + Math.cos( start_radians + slice_radians * ( index + 1 ) ) * ( inner_radius + radial_thickness ), cy + Math.sin( start_radians + slice_radians * ( index + 1 ) ) * ( inner_radius + radial_thickness ) );
        path.push( "L", cx + Math.cos( start_radians + slice_radians * ( index + 1 ) ) * inner_radius, cy + Math.sin( start_radians + slice_radians * ( index + 1 ) ) * inner_radius );
        path.push( "A",
                    inner_radius, inner_radius,
                    slice_radians, 0, 0, 
                    cx + Math.cos( start_radians + slice_radians * index ) * inner_radius, cy + Math.sin( start_radians + slice_radians * index ) * inner_radius );
        path.push( "Z" );

        var element = paper.path( path ).attr( { fill: 'none', stroke: 'none' } );
        callback( k, element, menu_items[k] );

        index++;
    }
}

This function will essentially calculate the region for each navigational item, generate a path with that shape, and then pass the menu definition to a callback. Any support for icons would have to be added separately, but I would suggest that the data in the menu_items definition could easily be augmented to support it. For an example of this method in use, check out my test page -- pardoning the rough edges of haste, of course!

like image 148
Kevin Nielsen Avatar answered Oct 18 '22 09:10

Kevin Nielsen