currently I am trying to design dynamic pie chart whereby the pie chart's slices will change according to the random generated data. Below is the code.
var dataset = [
{ name: 'Smooth', percent: 40.00, class: 'custom-normal' },
{ name: 'Moderate', percent: 10.00, class: 'custom-warning' },
{ name: 'Heavy', percent: 50.00, class: 'custom-danger' }
];
var width = 960,
height = 500,
radius = Math.min(width, height) / 2; //Math.min return the smallest value between width and height (for optimization purposes)
var colorValues = d3.scaleOrdinal().domain(["Smooth", "Moderate", "Heavy"]).range(["#605A4C", "#ff9900", "#ff1a1a"]);
var percent = "percent"; //predefine the legend of dataset (the string index)
var category = "class";
var name = "name";
var pie = d3.pie()
.value(function(d) { return d[percent]; })
.sort(null)
.padAngle(.02); //the gap
var arc = d3.arc()
.innerRadius(radius - 100) //optimization
.outerRadius(radius - 20); //optimization
var svg = d3.select("#chart")
.append("svg")
.attrs({
width: width,
height: height,
class: "shadow"
}).append("g")
.attrs({
transform: 'translate(' + width / 2 + ',' + height / 2 + ')'
});
svg.append('g')
.attrs({
class: 'slices'
});
var path = svg.select('.slices')
.selectAll('path')
.data(pie(dataset))
.enter().append('path')
.attrs({
d: arc
}).each(function(d, i) {
this._current = d;
console.log(this._current);
console.log('okay!');
}).attrs({
class: function(d, i){
return d[category];
},
fill: function(d, i) {
console.log("this is color value" + colorValues());
return colorValues(d[i]);
}
}); //initial details (this._current)
var randomGenerator = setInterval(function() {
var data = dataset.map(function(d, i) {
for (var key in d) {
if (d[key] === "Smooth") {
//console.log("smooth");
dataset[0].percent = Math.floor(Math.random() * 100);
//console.log(dataset[0].percent);
} else if (d[key] === "Moderate") {
dataset[1].percent = Math.floor(Math.random() * 100);
//console.log(dataset[1].percent);
//console.log("moderate");
} else if (d[key] === "Heavy") {
dataset[2].percent = Math.floor(Math.random() * 100);
//console.log(dataset[2].percent);
//console.log("heavy");
}
}
});
}, 3000);
var timer = setInterval(function() {
pie.value(function(d) {
return d[percent];
}); // change the value function
path = path.data(pie(dataset)); // compute the new angles
path.transition().duration(750).attrTween("d", arcTween); // redraw the arcs
}, 3000);
// Store the displayed angles in _current.
// Then, interpolate from _current to the new angles.
// During the transition, _current is updated in-place by d3.interpolate.
function arcTween(a) {
var i = d3.interpolate(this._current, a);
console.log(this._current);
this._current = i(0);
return function(t) {
return arc(i(t));
};
}
body {
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
margin: auto;
position: relative;
width: 960px;
}
<meta charset="utf-8">
<div id="mydiv" class="widget">
<div id="chart" class="Chart chart-container"></div>
</div>
<script src="https://d3js.org/d3.v4.js"></script>
<script src="https://d3js.org/d3-selection-multi.v0.4.min.js"></script>
Notice that the color of each slices is not scaled according to what I have defined earlier. i.e. :var colorValues = d3.scaleOrdinal().domain(["Smooth", "Moderate", "Heavy"]).range(["#605A4C", "#ff9900", "#ff1a1a"]);
. I believe it should be the selection problem, however I found no clue on that. And yet, the class as well, it only get the first row of data in var dataset
and apply to all.
If I understand correctly,
By pushing your data through the pie function, you are altering your data. While your initial structure is:
{ name: 'Smooth', percent: 40.00, class: 'custom-normal' }
After running this data through the pie function, the datum bound to each arc has the following structure:
{
"data": {
"name": "Heavy",
"percent": 48,
"class": "custom-danger"
},
"index": 2,
"value": 50,
"startAngle": 3.1515926535897933,
"endAngle": 6.283185307179586,
"padAngle": 0.02
}
In your code you are coloring the wedges based on this:
fill: function(d, i) {
console.log("this is color value" + colorValues());
return colorValues(d[i]);
}
The d
in function(d) {}
refers to the datum, so it is just a single item in the input data (the datum bound to a particular arc), which is an object - the use of d[number] suggests that you anticipate an array. In any event, each time you run this function you will get undefined
.
Instead access the property of the datum you want: name
(I assume) and use:
fill: function(d, i) {
console.log("this is color value" + colorValues());
return colorValues(d.data.name);
}
var dataset = [
{ name: 'Smooth', percent: 40.00, class: 'custom-normal' },
{ name: 'Moderate', percent: 10.00, class: 'custom-warning' },
{ name: 'Heavy', percent: 50.00, class: 'custom-danger' }
];
var width = 960,
height = 500,
radius = Math.min(width, height) / 2; //Math.min return the smallest value between width and height (for optimization purposes)
var colorValues = d3.scaleOrdinal().domain(["Smooth", "Moderate", "Heavy"]).range(["#605A4C", "#ff9900", "#ff1a1a"]);
var percent = "percent"; //predefine the legend of dataset (the string index)
var category = "class";
var name = "name";
var pie = d3.pie()
.value(function(d) { return d[percent]; })
.sort(null)
.padAngle(.02); //the gap
var arc = d3.arc()
.innerRadius(radius - 100) //optimization
.outerRadius(radius - 20); //optimization
var svg = d3.select("#chart")
.append("svg")
.attrs({
width: width,
height: height,
class: "shadow"
}).append("g")
.attrs({
transform: 'translate(' + width / 2 + ',' + height / 2 + ')'
});
svg.append('g')
.attrs({
class: 'slices'
});
var path = svg.select('.slices')
.selectAll('path')
.data(pie(dataset))
.enter().append('path')
.attrs({
d: arc
}).each(function(d, i) {
this._current = d;
console.log(this._current);
console.log('okay!');
}).attrs({
class: function(d, i){
return d.data.class;
},
fill: function(d, i) {
console.log("this is color value" + colorValues());
return colorValues(d.data.name);
}
}); //initial details (this._current)
var randomGenerator = setInterval(function() {
var data = dataset.map(function(d, i) {
for (var key in d) {
if (d[key] === "Smooth") {
//console.log("smooth");
dataset[0].percent = Math.floor(Math.random() * 100);
//console.log(dataset[0].percent);
} else if (d[key] === "Moderate") {
dataset[1].percent = Math.floor(Math.random() * 100);
//console.log(dataset[1].percent);
//console.log("moderate");
} else if (d[key] === "Heavy") {
dataset[2].percent = Math.floor(Math.random() * 100);
//console.log(dataset[2].percent);
//console.log("heavy");
}
}
});
}, 3000);
var timer = setInterval(function() {
pie.value(function(d) {
return d[percent];
}); // change the value function
path = path.data(pie(dataset)); // compute the new angles
path.transition().duration(750).attrTween("d", arcTween); // redraw the arcs
}, 3000);
// Store the displayed angles in _current.
// Then, interpolate from _current to the new angles.
// During the transition, _current is updated in-place by d3.interpolate.
function arcTween(a) {
var i = d3.interpolate(this._current, a);
console.log(this._current);
this._current = i(0);
return function(t) {
return arc(i(t));
};
}
body {
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
margin: auto;
position: relative;
width: 960px;
}
<meta charset="utf-8">
<div id="mydiv" class="widget">
<div id="chart" class="Chart chart-container"></div>
</div>
<script src="https://d3js.org/d3.v4.js"></script>
<script src="https://d3js.org/d3-selection-multi.v0.4.min.js"></script>