D3
OG
Old school D3 from simpler times
All examples
By author
By category
About
mforando
Full window
Github gist
Multi-Dimension Project Scoring Mock Up
Built with
blockbuilder.org
<!DOCTYPE html> <head> <meta charset="utf-8"> <script src="https://d3js.org/d3.v4.min.js"></script> <style> body { margin:0;position:fixed;top:0;right:0;bottom:0;left:0; } .goallabels{ font-family:Helvetica; font-size:0.9rem; } .dimensionheaders{ font-family:Helvetica; font-size:1.5rem; dominant-baseline:central; } .numlabels{ pointer-events:none; cursor:none; } .projectCircles{ cursor:pointer; } </style> </head> <body> <script> var goals = [{"dimension":"Financial","project":"Increase 401k contribution to 8%","relativeCost":null,"relativeValue":null} ,{"dimension":"Financial","project":"Offer financial consulting services","relativeCost":null,"relativeValue":null} ,{"dimension":"Financial","project":"Offer an retirement educational session","relativeCost":null,"relativeValue":null} ,{"dimension":"Financial","project":"Increase participation in 401k","relativeCost":null,"relativeValue":null} ,{"dimension":"Financial","project":"Adjust XXXX positions to market value","relativeCost":null,"relativeValue":null} ,{"dimension":"Health","project":"Offer a yoga program","relativeCost":null,"relativeValue":null} ,{"dimension":"Health","project":"Offer healthy food prep solutions","relativeCost":null,"relativeValue":null} ,{"dimension":"Health","project":"Have a wellness committee","relativeCost":null,"relativeValue":null} ,{"dimension":"Health","project":"Offer a smoking cessation program","relativeCost":null,"relativeValue":null} ] var dimensions = d3.nest().key(function(d){return d.dimension}).entries(goals) var maxLength = d3.max(dimensions.map(function(d){return d.values.length})) //set up a scaleband that works for the longest dimension var scalePoint = d3.scalePoint() .domain(d3.range(maxLength)) .range([100,400]) var colors = d3.scaleOrdinal() .domain(["Financial","Health"]) .range(["rgb(183,32,51)","rgb(0,62,126)"]) var svg = d3.select("body").selectAll("svg").data(dimensions) svg.enter() .append("svg") .attr("width", 900) .attr("height", 500) .each(function(d){ d3.select(this) .append("text") .attr("class","dimensionheaders") .text(d.key + " Dimension") .attr("x",5) .attr("y",15) .style("fill",colors(d.key)) }) var numlabelsBckgrnd = d3.selectAll("svg").selectAll(".numlabelsBckgrnd") .data(function(d){return d.values}) numlabelsBckgrnd.enter() .append("text") .attr("class","numlabelsBckgrnd") .attr("x",20) .attr("y",function(d,i){return scalePoint(i)}) .text(function(d,i){return (i+1)}) .style("text-anchor","middle") .style("dominant-baseline","central") .style("fill","black") var circles = d3.selectAll("svg").selectAll("circles") .data(function(d){return d.values}) circles.enter() .append("circle") .attr("class","projectCircles") .attr("cx",20) .attr("cy",function(d,i){return scalePoint(i)}) .attr("r",10) .style("fill",function(d){ return colors(d.dimension) }) .style("stroke","black") .call(d3.drag() .on("start", dragstarted) .on("drag", dragged) .on("end", dragended)) var labels = d3.selectAll("svg").selectAll(".goallabels") .data(function(d){return d.values}) labels.enter() .append("text") .attr("class","goallabels") .attr("x",35) .attr("y",function(d,i){ d.x = d3.select(this).attr("cx"); d.y = scalePoint(i); return scalePoint(i)}) .text(function(d){return d.project}) .style("dominant-baseline","central") var numlabels = d3.selectAll("svg").selectAll(".numlabels") .data(function(d){return d.values}) numlabels.enter() .append("text") .attr("class","numlabels") .attr("x",20) .attr("y",function(d,i){return scalePoint(i)}) .text(function(d,i){return (i+1)}) .style("text-anchor","middle") .style("dominant-baseline","central") .style("fill","white") var scalePointVertical = d3.scalePoint() .range([450,50]) .domain(['Lowest Cost','Average Cost','Largest Cost']) var scalePointHorizontal = d3.scalePoint() .range([450,900]) .domain(['Least Value','Average Value','Most Value']) d3.selectAll("svg") .append("g") .attr("transform","translate(450,0)") .call(d3.axisLeft(scalePointVertical)) .selectAll("text") .style("font-size","1rem") d3.selectAll("svg") .append("g") .attr("class","xAxis") .attr("transform","translate(0,450)") .call(d3.axisBottom(scalePointHorizontal)) .selectAll("text") .style("font-size","1rem") .style("text-anchor",function(d,i){ if(i==0){return "start"} if(i==2){ return "end" } }) function dragstarted(d) { //capture original location in case the end location is invalid. d3.select(this).each(function(d){ d.orig_x = d3.select(this).attr("cx"); d.orig_y = d3.select(this).attr("cy"); }) } function dragged(d) { d3.select(this).attr("cx",d3.event.x).attr("cy",d3.event.y); d3.select(this.parentNode) .selectAll(".numlabels") .filter(function(z){ return z.project == d.project }) .attr("x",d3.event.x) .attr("y",d3.event.y); } function dragended(d) { //evaluate if the circle is in the charting region. var svgcoords = d3.mouse(this); if(svgcoords[0]>scalePointHorizontal.range()[0] && svgcoords[0]<scalePointHorizontal.range()[1]) { d3.select(this) .each(function(d){ d.scored = true; d.x = d3.select(this).attr("cx") d.y = d3.select(this).attr("cy") }) //node is in the chart range. save results to whats bounded to the SVG. d3.select(this.parentNode) .each(function(d){ d.results = d3.select(this).selectAll("circle").data() .filter(function(d){return d.scored == true}) .map(function(d){ //calculate relative cost var minval = scalePointVertical.range()[1]; var maxval = scalePointVertical.range()[0]; d.relativeCost = (maxval-d.y)/(maxval-minval); var minval = scalePointHorizontal.range()[1]; var maxval = scalePointHorizontal.range()[0]; d.relativeValue = (maxval-d.x)/(maxval-minval); return d}) }) updateBenchmarks() } else { //node is not in the chart range. reset. d3.select(this) .transition() .attr("cx",function(d){return d.orig_x}) .attr("cy",function(d){return d.orig_y}) d3.select(this.parentNode) .selectAll(".numlabels") .filter(function(z){return z.project == d.project}) .transition() .attr("x",function(d){return d.orig_x}) .attr("y",function(d){return d.orig_y}) } } function updateBenchmarks(){ //walk down all SVGs and find the max value in terms of cost/value to provide as a benchmark. Allow adjustment of this //to adjust for new cost/value information. var allresults = [] d3.selectAll("svg") .each(function(d,i){ var benchmarkgoal = null; if(d.results){ //go through results, normalize them against both dimensions, and find the largest node to use as a benchmark. var sorted = d.results.sort(function(a,b){ a.totalIndex = a.relativeCost + a.relativeValue; b.totalIndex = b.relativeCost + b.relativeValue; return d3.descending(a.totalIndex,b.totalIndex) }) benchmarkgoal = sorted[0] } allresults.push({"dimension":d.key,"results":d.results,"benchmark":benchmarkgoal}) }) //figure out the benchmark with the max value on all other charts. d3.selectAll("svg") .each(function(d,i){ //find valid goals in other dimensions. var contains_goals = allresults.filter(function(z){return z.benchmark && (z.dimension != d.key)}).map(function(d){return d.benchmark}) if(contains_goals.length>0){ //valid project/goal found, go render it. var bnchCircle = d3.select(this) .selectAll(".benchmarknode") .data([contains_goals[0]]) bnchCircle.enter() .append("circle") .attr("class","benchmarknode") .merge(bnchCircle) .attr("cx",function(d){ return d3.interpolateNumber(scalePointHorizontal.range()[0],scalePointHorizontal.range()[1])(d.relativeValue) }) .attr("cy",function(d){ return d3.interpolateNumber(scalePointVertical.range()[0],scalePointVertical.range()[1])(d.relativeCost) }) .attr("r",10) .style("fill","cyan") .call(d3.drag() .on("start", benchdragstarted) .on("drag", benchdragged) .on("end", benchdragended)) } }) } function benchdragstarted(d) { //record original position. d3.select(this).each(function(d){ d.orig_x = d3.select(this).attr("cx"); d.orig_y = d3.select(this).attr("cy"); }) } function benchdragged(d) { //while dragging, scale the other charts goals based on the delta of the drag. d3.select(this).attr("cx", d.x = d3.event.x).attr("cy", d.y = d3.event.y); } function benchdragended(d) { } </script> </body>
https://d3js.org/d3.v4.min.js