6

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.

apttap
  • 468
  • 2
  • 7
  • 17
  • does this help? http://stackoverflow.com/questions/5061318/drawing-centered-arcs-in-raphael-js – Spudley Sep 14 '12 at 22:15
  • have you tried anything? show some effort solving your own issue. stating that you're 'terrible with geometry' will not cut you any slack. please refer to the [FAQ entry on the kind of questions expected here](http://stackoverflow.com/faq#dontask). and welcome aboard. – Eliran Malka Sep 14 '12 at 23:02

1 Answers1

9

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!

Kevin Nielsen
  • 4,413
  • 21
  • 26