问题
I would like to do a donut-chart in d3.js. Basically the idea would be to write 3 inputs and it should be displayed this way:
Unfortunatelly I cannot make it, I am not sure about how to implement the variables fuel, fuel 2 and fuel 3 in the javascript code, I am not sure wether it has to be indicated in the html code or in the javascript code. Thanks in advance.
<meta charset="utf-8">
<!-- Load d3.js -->
<script src="https://d3js.org/d3.v4.js"></script>
<div>
<input type="number" id="fuel" style="text-align:center">
</div>
<div>
<input type="number" id="fuel2" style="text-align:center">
</div>
<div>
<input type="number" id="fuel3" style="text-align:center">
</div>
<!-- Create a div where the graph will take place -->
<div id="my_dataviz"></div>
var fuel = document.getElementById('fuel');
var fuel2 = document.getElementById('fuel2');
var fuel3 = document.getElementById('fuel3');
// set the dimensions and margins of the graph
var width = 450
height = 450
margin = 40
// The radius of the pieplot is half the width or half the height (smallest one). I subtract a bit of margin.
var radius = Math.min(width, height) / 2 - margin
// append the svg object to the div called 'my_dataviz'
var svg = d3.select("#my_dataviz")
.append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");
// Create dummy data
var data = {fuel,fuel2,fuel3}
// set the color scale
var color = d3.scaleOrdinal()
.domain(data)
.range(["#98abc5", "#8a89a6", "#7b6888"])
// Compute the position of each group on the pie:
var pie = d3.pie()
.value(function(d) {return d.value; })
var data_ready = pie(d3.entries(data))
// Build the pie chart: Basically, each part of the pie is a path that we build using the arc function.
svg
.selectAll('whatever')
.data(data_ready)
.enter()
.append('path')
.attr('d', d3.arc()
.innerRadius(100) // This is the size of the donut hole
.outerRadius(radius)
)
.attr('fill', function(d){ return(color(d.data.key)) })
.attr("stroke", "black")
.style("stroke-width", "2px")
.style("opacity", 0.7)
``
回答1:
This answer uses d3v6
Approach 1
You could use a data array to represent both your input boxes and your slices. This can have advantages, especially if loading the chart from a data source. Try something like:
// Date representing your slices:
var data = [
{name:"A",value:1},
{name:"B",value:1},
{name:"B",value:1}
];
// Create the fields based on the data:
d3.select("#my_inputs")
.selectAll(null)
.data(data)
.enter()
.append("input")
.attr("value",function(d) { return d.value; })
.on("keyup", function(_,d) {
d.value = d3.select(this).node().value;
update();
})
Here we create an data array to represent our input fields and the slices. We bind the data to the input fields and update the bound data every time the field value changes.
Now we can update the chart each time a value changes, and initially, with a function like:
function update() {
var pie = d3.pie()
.value(function(d) {return d.value; })(data);
svg
.selectAll('path')
.data(pie)
.join('path')
.attr(.....
}
I've made a few changes below as we need to be mindful that if the number of slices is constant, the enter selection will be empty after drawing the slices the first time. So I use selection.join()
though selection.merge()
with separate treatment of update and enter is also fine.
Here's those two together:
var width = 450
height = 450
margin = 40
var radius = Math.min(width, height) / 2 - margin
// Date representing your slices:
var data = [
{name:"A",value:1},
{name:"B",value:1},
{name:"C",value:1}
];
// Create the fields based on the data:
d3.select("#my_inputs")
.selectAll(null)
.data(data)
.enter()
.append("input")
.attr("value",function(d) { return d.value; })
.on("keyup", function(_,d) {
d.value = d3.select(this).node().value;
update();
})
// Create an SVG
var svg = d3.select("#my_dataviz")
.append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");
// set the color scale
var color = d3.scaleOrdinal()
.domain(data.map(function(d) { return d.name; }))
.range(["#98abc5", "#8a89a6", "#7b6888"])
update();
function update() {
// Compute the position of each group on the pie:
var pie = d3.pie()
.value(function(d) {return d.value; })(data);
// Build the pie chart: Basically, each part of the pie is a path that we build using the arc function.
svg
.selectAll('path')
.data(pie)
.join('path')
.attr('d', d3.arc()
.innerRadius(100)
.outerRadius(radius)
)
.attr("stroke", "black")
.style("stroke-width", "2px")
.style("fill", function(d) { return color(d.data.name);})
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/6.1.0/d3.min.js"></script>
<div id="my_inputs"></div>
<div id="my_dataviz"></div>
Going further, with some tweaking, creating a user interface where the user can dynamically add/remove fields/slices from the pie chart is not difficult as the slices and fields are both represented in the data array.
Approach 2
Alternatively, if you want to have the input fields hard coded for whatever reason, you could select them all at once and make a data array out of them:
var data = d3.selectAll("input").nodes();
You can then use this like any other data array with D3. This is not a selection, .nodes() returns an array of elements. You need to use .nodes as you need an array to use the enter/update cycle correctly and D3 selections are not arrays.
We add the event listener like so:
d3.selectAll("input")
.on("keyup", function() {
update();
})
This is a bit more in line with what you had originally:
var width = 450
height = 450
margin = 40
var radius = Math.min(width, height) / 2 - margin
// Add an event listener
d3.selectAll("input")
.on("keyup", function() {
update();
})
// Create an SVG
var svg = d3.select("#my_dataviz")
.append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");
// set the color scale
var color =["#98abc5", "#8a89a6", "#7b6888"];
update();
function update() {
var data = d3.selectAll("input").nodes();
var pie = d3.pie()
.value(function(d) {return d.value; })(data);
svg
.selectAll('path')
.data(pie)
.join('path')
.attr('d', d3.arc()
.innerRadius(100)
.outerRadius(radius)
)
.attr('fill', function(d,i){ return color[i] })
.attr("stroke", "black")
.style("stroke-width", "2px")
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/6.1.0/d3.min.js"></script>
<div id="my_inputs">
<input value="1"></input>
<input value="1"></input>
<input value="1"></input>
</div>
<div id="my_dataviz"></div>
来源:https://stackoverflow.com/questions/64614134/how-to-implement-the-html-variables-for-a-graphic-from-d3-js