Reputation: 468
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.
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.
Upvotes: 6
Views: 9689
Reputation: 4433
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!
Upvotes: 9