Skip to content

Instantly share code, notes, and snippets.

@RMKD
Created October 24, 2016 02:56
Show Gist options
  • Save RMKD/7a99f89b2b7a73a33c298c6635fae926 to your computer and use it in GitHub Desktop.
Save RMKD/7a99f89b2b7a73a33c298c6635fae926 to your computer and use it in GitHub Desktop.
d3 radial menu prototype
/*
requirements: d3, FontAwesome
usage example:
var radialMenuItems = [
{"label": "A", "action":function(){return alert("hello menu A")}, "faIcon":"share-alt"},
{"label": "B", "action":function(){return alert("hello menu B")}, "faIcon":"line-chart"},
{"label": "C", "action":function(){return alert("hello menu C")}, "faIcon":"credit-card"},
{"label": "D", "action":function(){return alert("hello menu D")}, "faIcon":"arrows-alt"},
{"label": "E", "action":function(){return alert("hello menu E")}, "faIcon":"external-link"},
]
var radialMenuOptions = {
"inner": 52,
"outer": 66,
"padding": 2,
"fractionToCover": Math.PI*.75
}
d3.selectAll(".node")
.radialMenu(radialMenuItems, radialMenuOptions);
*/
d3.selection.prototype.radialMenu = function(items, options){
/*
pass in items in the form:
[
{label:"A", faIcon:"font-awesome-icon-name", action:function(){}},
{label:"B", faIcon:"font-awesome-icon-name", action:function(){}}
]
pass in options in the form:
{"inner":30, "outer": 50, "fractionToCover": 0.6}
*/
var fullCircle = Math.PI*2;
function destroyMenu(){
d3.selectAll(".radial-menu-group").remove();
}
//turn off the context menue for all svg elements
d3.selectAll("svg").on("contextmenu", function(data, index) {
d3.event.preventDefault();
});
var inner = (options.inner == undefined) ? 20 : options.inner;
var outer = (options.outer == undefined) ? 40 : options.outer;
var padding = (options.padding == undefined) ? fullCircle / 360 * 2 : fullCircle / 360 * options.padding;
var startAt = (options.startAt == undefined) ? fullCircle * -0.10 : options.startAt;
var fractionToCover = (options.fractionToCover == undefined) ? fullCircle * 0.33 : options.fractionToCover;
var trigger = (options.trigger == undefined) ? "contextmenu" : options.trigger;
var cursor = (options.cursor == undefined) ? "pointer" : options.cursor;
var segmentSize = fractionToCover / items.length;
this.on(trigger, function(event){
destroyMenu();
options = (options == undefined) ? {} : options;
var menuArc = d3.arc()
.startAngle(function(d,i) {return startAt + i * segmentSize + padding; })
.endAngle(function(d,i) { return startAt + (i+1) * segmentSize - padding; })
.innerRadius(inner)
.outerRadius(outer)
var parent = d3.select(this.parentNode);
parent.on("mouseleave", destroyMenu);
var menus = parent.selectAll(".radial-menu-group")
.data(items).enter();
//this transparent circle catches other clicks
menus.append("circle")
.attr("class","radial-menu-group")
.attr("r", outer * 1.5)
.style("opacity",0);
menus.append("path")
.attr("class","radial-menu-group")
.attr("d", menuArc)
.style("cursor", cursor)
.on("click",function(d){d.action();})
var labels = menus.append("g")
.attr("class","radial-menu-group")
.attr("transform", function(d, i){
var angle = (startAt + (i+0.5)* segmentSize)*180/Math.PI -90;
return "rotate("+ angle +") translate("+((inner + outer)/2)+") "
})
.style("cursor", cursor)
.on("click",function(d){d.action();});
labels.append("svg:foreignObject")
.attr("class", function(d){return "radial-menu-group fa fa-" + d.faIcon})
.attr("transform", "rotate(90)")
.attr("x",-5)
.attr("y",-5)
.attr("width", 50)
.attr("height", 50)
.html("<i></i>");
labels.append("text")
.attr("class", "radial-menu-group")
.attr("dx", outer - (inner + outer)/2 + 5)
.attr("dy", 5)
.text(function(d){return d.label;})
});
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment