简体   繁体   中英

Responsive D3 Donut Chart

I am using a donut chart in order to present data from a static JSON query. I now want to make it responsive. I have made other D3 graphs responsive but it is different for this one.

The idea was first to calculate the distance between two corners of the screen (hypotenuse) in order to recalculate the radius of my shape. That could be useful for later. Snippet for this calculation is shown here:

 console.log('width') console.log(window.innerWidth); console.log('height') console.log(window.innerHeight); console.log('final'); console.log(Math.sqrt(Math.pow(window.innerWidth,2) + Math.pow(window.innerHeight,2))); $("#one").html(Math.sqrt(Math.pow(window.innerWidth,2) + Math.pow(window.innerHeight,2))); 
 <script src="https://code.jquery.com/jquery-1.9.1.js"></script> <div id="one"></div> 

My main snippet is here and it is responsive but the box with the values (tooltip) is shown underneath the donut.

 var margin = {top: 20, right: 20, bottom: 50, left: 100}, width = parseInt(d3.select("#chart").style("width")) - margin.left - margin.right, height = parseInt(d3.select("#chart").style("width")) - margin.top - margin.bottom, r = 180, inner = 180/2, color= d3.scale.ordinal() .range(["#124", "#214183", "#3061c2", "#4876d1", "#87a5e1", "#c5d4f1"]); data = [{"label":"ONE", "value":194}, {"label":"TWO", "value":567}, {"label":"THREE", "value":1314}, {"label":"FOUR", "value":793}, {"label":"FIVE", "value":1929}, {"label":"SIX", "value":1383}]; var total = d3.sum(data, function(d) { return d3.sum(d3.values(d)); }); var vis = d3.select('#chart').append("svg:svg").data([data]) .attr("width", '100%') .attr("height", '100%') .attr('viewBox',(-width / 2 ) + ' ' + (-height/2) + ' '+width +' '+height) .attr('preserveAspectRatio','xMinYMin') var textTop = vis.append("text") .attr("dy", ".35em") .style("text-anchor", "middle") .attr("class", "textTop") .text( "TOTAL" ) .attr("y", -10), textBottom = vis.append("text") .attr("dy", ".35em") .style("text-anchor", "middle") .attr("class", "textBottom") .text(total.toFixed(2) + "m") .attr("y", 10); var arc = d3.svg.arc() .innerRadius(inner) .outerRadius(r); var arcOver = d3.svg.arc() .innerRadius(inner + 5) .outerRadius(r + 5); var pie = d3.layout.pie() .value(function(d) { return d.value; }); var arcs = vis.selectAll("g.slice") .data(pie) .enter() .append("svg:g") .attr("class", "slice") .on("mouseover", function(d) { d3.select(this).select("path").transition() .duration(200) .attr("d", arcOver) textTop.text(d3.select(this).datum().data.label) .attr("y", -10); textBottom.text(d3.select(this).datum().data.value.toFixed(2)) .attr("y", 10); }) .on("mouseout", function(d) { d3.select(this).select("path").transition() .duration(100) .attr("d", arc); textTop.text( "TOTAL" ) .attr("y", -10); textBottom.text(total.toFixed(2) + "m"); }); arcs.append("svg:path") .attr("fill", function(d, i) { return color(i); } ) .attr("d", arc); var legend = d3.select("#chart").append("svg") .attr("class", "legend") .attr("width", r) .attr("height", r * 2) .selectAll("g") .data(data) .enter().append("g") .attr("transform", function(d, i) { return "translate(0," + i * 20 + ")"; }); legend.append("rect") .attr("width", 18) .attr("height", 18) .style("fill", function(d, i) { return color(i); }); legend.append("text") .attr("x", 24) .attr("y", 9) .attr("dy", ".35em") .text(function(d) { return d.label; }); 
 *{ font-family: 'Roboto', sans-serif; text-transform:capitalize; margin: 0 auto; text-align:left; } body { font-family: "Roboto"!important; width: 100%; height: 400px; position: relative; } .slice path { stroke: #fff; stroke-width: 1px; } .textTop { font-size: 12pt; fill: #bbb; } .textBottom { fill: #444; font-weight: bold; font-size: 18pt; } .top { border: 1px solid #bbb; color: #777; padding: 5px; text-decoration: none; } .top:hover { border: 1px solid #555; color: #333; } 
 <script src="https://d3js.org/d3.v3.min.js"></script> <div id="chart"></div> <svg id="chart2"></svg> 

How can I include the tooltip (legend - not sure about the exact terminology) on the right side of the donut?

You can include the legend directly into you main svg by the following :

var svg = d3.select('#chart').append("svg:svg");
var vis = svg.data([data])
(...)

Rather than :

var vis = d3.select('#chart').append("svg:svg").data([data])
(...)

Then, you can add your legend to that svg, replacing :

var legend = d3.select("#chart").append("svg")

With :

var legend = svg.append("svg")

Finally, you'll have to deal with legend placement. Here is a working JSFiddle : https://jsfiddle.net/anh9Lr1e/4/ and a snippet in case the jsfiddle url expires:

 var margin = {top: 20, right: 20, bottom: 50, left: 100}, width = parseInt(d3.select("#chart").style("width")) - margin.left - margin.right, height = parseInt(d3.select("#chart").style("width")) - margin.top - margin.bottom, r = 180, inner = 180/2, color= d3.scale.ordinal() .range(["#124", "#214183", "#3061c2", "#4876d1", "#87a5e1", "#c5d4f1"]); data = [{"label":"ONE", "value":194}, {"label":"TWO", "value":567}, {"label":"THREE", "value":1314}, {"label":"FOUR", "value":793}, {"label":"FIVE", "value":1929}, {"label":"SIX", "value":1383}]; var total = d3.sum(data, function(d) { return d3.sum(d3.values(d)); }); var svg = d3.select('#chart').append("svg:svg") .attr("width", '100%') .attr("height", '100%') .attr('preserveAspectRatio','xMinYMin'); var vis = svg.data([data]) .attr("width", '100%') .attr("height", '100%') .attr('viewBox',(-width / 2 ) + ' ' + (-height/2) + ' '+width +' '+height) .attr('preserveAspectRatio','xMinYMin') var textTop = vis.append("text") .attr("dy", ".35em") .style("text-anchor", "middle") .attr("class", "textTop") .text( "TOTAL" ) .attr("y", -10), textBottom = vis.append("text") .attr("dy", ".35em") .style("text-anchor", "middle") .attr("class", "textBottom") .text(total.toFixed(2) + "m") .attr("y", 10); var arc = d3.svg.arc() .innerRadius(inner) .outerRadius(r); var arcOver = d3.svg.arc() .innerRadius(inner + 5) .outerRadius(r + 5); var pie = d3.layout.pie() .value(function(d) { return d.value; }); var arcs = vis.selectAll("g.slice") .data(pie) .enter() .append("svg:g") .attr("class", "slice") .on("mouseover", function(d) { d3.select(this).select("path").transition() .duration(200) .attr("d", arcOver) textTop.text(d3.select(this).datum().data.label) .attr("y", -10); textBottom.text(d3.select(this).datum().data.value.toFixed(2)) .attr("y", 10); }) .on("mouseout", function(d) { d3.select(this).select("path").transition() .duration(100) .attr("d", arc); textTop.text( "TOTAL" ) .attr("y", -10); textBottom.text(total.toFixed(2) + "m"); }); arcs.append("svg:path") .attr("fill", function(d, i) { return color(i); } ) .attr("d", arc); var legend = svg.append("svg") .attr("class", "legend") //.attr("width", r) //.attr("height", r * 2) .selectAll("g") .data(data) .enter().append("g") .attr("transform", function(d, i) { return "translate(" + (r + 20) + "," + i * 20 + ")"; }); legend.append("rect") .attr("width", 18) .attr("height", 18) .style("fill", function(d, i) { return color(i); }); legend.append("text") .attr("x", 24) .attr("y", 9) .attr("dy", ".35em") .text(function(d) { return d.label; }); 
 *{ font-family: 'Roboto', sans-serif; text-transform:capitalize; margin: 0 auto; text-align:left; } body { font-family: "Roboto"!important; width: 100%; height: 400px; position: relative; } .slice path { stroke: #fff; stroke-width: 1px; } .textTop { font-size: 12pt; fill: #bbb; } .textBottom { fill: #444; font-weight: bold; font-size: 18pt; } .top { border: 1px solid #bbb; color: #777; padding: 5px; text-decoration: none; } .top:hover { border: 1px solid #555; color: #333; } 
 <script src="https://d3js.org/d3.v3.min.js"></script> <div id="chart"></div> 

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM