How to create donut chart in d3.js? - javascript

I am trying to create responsive donut chart in d3.js.
I wrote the code for chart in d3.js, but I am not able to create according to expected chart.
Here's the actual chart, while I'm trying to achieve is this one.
My d3.js script below:
document.addEventListener('DOMContentLoaded', function(e) {
var dataset = {
oranges: [200, 200, 200, 200]
};
var width = 960,
height = 500,
radius = Math.min(width, height) / 2;
var enterClockwise = {
startAngle: 0,
endAngle: 0
};
var enterAntiClockwise = {
startAngle: Math.PI * 2,
endAngle: Math.PI * 2
};
var color = d3.scale.category20();
var pie = d3.layout.pie()
.sort(null);
var arc = d3.svg.arc()
.innerRadius(radius - 100)
.outerRadius(radius - 20);
var svg = d3.select('#Donut-chart').append('svg')
.attr('id', 'Donut-chart-render')
.attr("width", '100%')
.attr("height", '100%')
.attr('viewBox', (-width / 2) + ' ' + (-height / 2) + ' ' + width + ' ' + height)
.attr('preserveAspectRatio', 'xMinYMin')
var path = svg.selectAll("path")
.data(pie(dataset.apples))
.enter().append("path")
.attr("fill", function (d, i) { return color(i); })
.attr("d", arc(enterClockwise))
.each(function (d) {
this._current = {
data: d.data,
value: d.value,
startAngle: enterClockwise.startAngle,
endAngle: enterClockwise.endAngle
}
});
path.transition()
.duration(750)
.attrTween("d", arcTween);
d3.selectAll("input").on("change", change);
var timeout = setTimeout(function () {
d3.select("input[value=\"oranges\"]").property("checked", true).each(change);
}, 2000);
function change() {
clearTimeout(timeout);
path = path.data(pie(dataset[this.value]));
path.enter().append("path")
.attr("fill", function (d, i) {
return color(i);
})
.attr("d", arc(enterAntiClockwise))
.each(function (d) {
this._current = {
data: d.data,
value: d.value,
startAngle: enterAntiClockwise.startAngle,
endAngle: enterAntiClockwise.endAngle
};
}); // store the initial values
path.exit()
.transition()
.duration(750)
.attrTween('d', arcTweenOut)
.remove() // now remove the exiting arcs
path.transition().duration(750).attrTween("d", arcTween); // redraw the arcs
}
function arcTween(a) {
var i = d3.interpolate(this._current, a);
this._current = i(0);
return function (t) {
return arc(i(t));
};
}
function arcTweenOut(a) {
var i = d3.interpolate(this._current, { startAngle: Math.PI * 2, endAngle: Math.PI * 2, value: 0 });
this._current = i(0);
return function (t) {
return arc(i(t));
};
}
function type(d) {
d.value = +d.value;
return d;
}
});
I'm stuck with this problem, so helpful answers will be appreciated.
Thanks in advance!

To add labels to a pie chart, use arc.centroid(). You'll need an arc generator first. See this example for reference.
svg.append("g")
.attr("font-family", "sans-serif")
.attr("font-size", 12)
.attr("text-anchor", "middle")
.selectAll("text")
.data(arcs)
.join("text")
.attr("transform", d => `translate(${arc.centroid(d)})`)
.call(text => text.append("tspan")
.attr("y", "-0.4em")
.attr("font-weight", "bold")
.text(d => d.data.name))
.call(text => text.filter(d => (d.endAngle - d.startAngle) > 0.25).append("tspan")
.attr("x", 0)
.attr("y", "0.7em")
.attr("fill-opacity", 0.7)
.text(d => d.data.value.toLocaleString()));

Related

Need help importing D3 radar chart into react component

I have a radar chart working in a regular js file, and a react app component that can render and return a simple bar chart. I'm trying to get the much more complex radar chart to render and return in react's component. I'm currently getting an error: ./src/App.js Attempted import error: 'scale' is not exported from 'd3' (imported as 'd3'). Any other feedback/suggestions in general would be appreciated. I'm new to D3. Thanks so much in advance.
import * as d3 from "d3";
class App extends React.Component {
constructor(props) {
super(props);
this.state = {};
}
/////////////////////////////////////////////////////////
/////////////// The Radar Chart Function ////////////////
/////////////// Written by Nadieh Bremer ////////////////
////////////////// VisualCinnamon.com ///////////////////
/////////// Inspired by the code of alangrafu ///////////
/////////////////////////////////////////////////////////
/* Radar chart design created by Nadieh Bremer - VisualCinnamon.com */
componentDidMount() {
//////////////////////// Set-Up //////////////////////////////
var margin = { top: 100, right: 100, bottom: 100, left: 100 },
width = Math.min(700, window.innerWidth - 10) - margin.left - margin.right,
height = Math.min(
width,
window.innerHeight - margin.top - margin.bottom - 20
);
////////////////////////// Data //////////////////////////////
var data = [
[
//Tsircon
{ axis: "BALLISTIC", value: 65 },
{ axis: "Glide", value: 39 },
{ axis: "20G TURN", value: 43 },
{ axis: "FISH HOOK DIVE", value: 50 },
{ axis: "PHUGOID MOTION", value: 60 },
{ axis: "ENERGY SCRUB", value: 52 },
],
// [
// //Kinzhal
// { axis: "BALLISTIC", value: 0.27 },
// { axis: "Glide", value: 0.16 },
// { axis: "20G TURN", value: 0.35 },
// { axis: "FISH HOOK DIVE", value: 0.13 },
// { axis: "PHUGOID MOTION", value: 0.2 },
// { axis: "ENERGY SCRUB", value: 0.13 },
// ],
// [
// //Example Missile Overlay
// { axis: "BALLISTIC", value: 0.26 },
// { axis: "Glide", value: 0.1 },
// { axis: "20G TURN", value: 0.3 },
// { axis: "FISH HOOK DIVE", value: 0.14 },
// { axis: "PHUGOID MOTION", value: 0.22 },
// { axis: "ENERGY SCRUB", value: 0.04 },
// ],
];
//////////////////// Draw the Chart //////////////////////////
var color = d3.scale.ordinal().range(["#739CC4", "#CC333F", "#00A0B0"]);
var radarChartOptions = {
w: width,
h: height,
margin: margin,
maxValue: 0.5,
levels: 5,
roundStrokes: false,
color: color,
};
//Call function to draw the Radar chart
RadarChart(".radarChart", data, radarChartOptions);
function RadarChart(id, data, options) {
var cfg = {
w: 600, //Width of the circle
h: 600, //Height of the circle
margin: { top: 20, right: 20, bottom: 20, left: 20 }, //The margins of the SVG
levels: 3, //How many levels or inner circles should there be drawn
maxValue: 0, //What is the value that the biggest circle will represent
labelFactor: 1.25, //How much farther than the radius of the outer circle should the labels be placed
wrapWidth: 60, //The number of pixels after which a label needs to be given a new line
opacityArea: 0.35, //The opacity of the area of the blob
dotRadius: 4, //The size of the colored circles of each blog
opacityCircles: 0.1, //The opacity of the circles of each blob
strokeWidth: 2, //The width of the stroke around each blob
roundStrokes: false, //If true the area and stroke will follow a round path (cardinal-closed)
color: d3.scale.category10(), //Color function
};
//Put all of the options into a variable called cfg
if ("undefined" !== typeof options) {
for (var i in options) {
if ("undefined" !== typeof options[i]) {
cfg[i] = options[i];
}
} //for i
} //if
//If the supplied maxValue is smaller than the actual one, replace by the max in the data
var maxValue = Math.max(
cfg.maxValue,
d3.max(data, function (i) {
return d3.max(
i.map(function (o) {
return o.value;
})
);
})
);
var allAxis = data[0].map(function (i, j) {
return i.axis;
}), //Names of each axis
total = allAxis.length, //The number of different axes
radius = Math.min(cfg.w / 2, cfg.h / 2), //Radius of the outermost circle
Format = d3.format("%"), //Percentage formatting
angleSlice = (Math.PI * 2) / total; //The width in radians of each "slice"
//Scale for the radius
var rScale = d3.scale.linear().range([0, radius]).domain([0, maxValue]);
//////////// Create the container SVG and g /////////////
//Remove whatever chart with the same id/class was present before
d3.select(id).select("svg").remove();
//Initiate the radar chart SVG
var svg = d3
.select(id)
.append("svg")
.attr("width", cfg.w + cfg.margin.left + cfg.margin.right)
.attr("height", cfg.h + cfg.margin.top + cfg.margin.bottom)
.attr("class", "radar" + id);
//Append a g element
var g = svg
.append("g")
.attr(
"transform",
"translate(" +
(cfg.w / 2 + cfg.margin.left) +
"," +
(cfg.h / 2 + cfg.margin.top) +
")"
);
////////// Glow filter ///////////
//Filter for the outside glow
var filter = g.append("defs").append("filter").attr("id", "glow"),
feGaussianBlur = filter
.append("feGaussianBlur")
.attr("stdDeviation", "2.5")
.attr("result", "coloredBlur"),
feMerge = filter.append("feMerge"),
feMergeNode_1 = feMerge.append("feMergeNode").attr("in", "coloredBlur"),
feMergeNode_2 = feMerge.append("feMergeNode").attr("in", "SourceGraphic");
/////////////// Draw the Circular grid //////////////////
//Wrapper for the grid & axes
var axisGrid = g.append("g").attr("class", "axisWrapper");
//Draw the background circles
axisGrid
.selectAll(".levels")
.data(d3.range(1, cfg.levels + 1).reverse())
.enter()
.append("circle")
.attr("class", "gridCircle")
.attr("r", function (d, i) {
return (radius / cfg.levels) * d;
})
.style("fill", "#CDCDCD")
.style("stroke", "#CDCDCD")
.style("fill-opacity", cfg.opacityCircles)
.style("filter", "url(#glow)");
//Text indicating at what % each level is
axisGrid
.selectAll(".axisLabel")
.data(d3.range(1, cfg.levels + 1).reverse())
.enter()
.append("text")
.attr("class", "axisLabel")
.attr("x", 4)
.attr("y", function (d) {
return (-d * radius) / cfg.levels;
})
.attr("dy", "0.4em")
.style("font-size", "10px")
.attr("fill", "#737373")
.text(function (d, i) {
return Format((maxValue * d) / cfg.levels);
});
//////////////////// Draw the axes //////////////////////
//Create the straight lines radiating outward from the center
var axis = axisGrid
.selectAll(".axis")
.data(allAxis)
.enter()
.append("g")
.attr("class", "axis");
//Append the lines
axis
.append("line")
.attr("x1", 0)
.attr("y1", 0)
.attr("x2", function (d, i) {
return rScale(maxValue * 1.1) * Math.cos(angleSlice * i - Math.PI / 2);
})
.attr("y2", function (d, i) {
return rScale(maxValue * 1.1) * Math.sin(angleSlice * i - Math.PI / 2);
})
.attr("class", "line")
.style("stroke", "white")
.style("stroke-width", "2px");
//Append the labels at each axis
axis
.append("text")
.attr("class", "legend")
.style("font-size", "11px")
.attr("text-anchor", "middle")
.attr("dy", "0.35em")
.attr("x", function (d, i) {
return (
rScale(maxValue * cfg.labelFactor) *
Math.cos(angleSlice * i - Math.PI / 2)
);
})
.attr("y", function (d, i) {
return (
rScale(maxValue * cfg.labelFactor) *
Math.sin(angleSlice * i - Math.PI / 2)
);
})
.text(function (d) {
return d;
})
.call(wrap, cfg.wrapWidth);
svg.append("text")
.attr("x", (width / 4.8))
.attr("y", 75 - (margin.top / 2))
.attr("text-anchor", "middle")
.style("font-size", "24px")
.style("font-weight", "bold")
.style("fill", "white")
.text("MANEUVERS");
svg.append("text")
.attr("x", (width / 4.2))
.attr("y", 95 - (margin.top / 2))
.attr("text-anchor", "middle")
.style("font-size", "16px")
.style("fill", "white")
.text("initiate: % ground track");
svg.append("text")
.attr("x", (width / 3.6))
.attr("y", 115 - (margin.top / 2))
.attr("text-anchor", "middle")
.style("font-size", "16px")
.style("fill", "#8CD9FF")
.text("% variability");
svg.append("text")
.attr("x", (width / 8))
.attr("y", 135 - (margin.top / 2))
.attr("text-anchor", "middle")
.style("font-size", "16px")
.style("fill", "white")
.text("sample:");
svg.append("text")
.attr("x", (width / 3.4))
.attr("y", 135 - (margin.top / 2))
.attr("text-anchor", "middle")
.style("font-size", "16px")
.style("fill", "#76ADDB")
.text("% trajectories");
///////////// Draw the radar chart blobs ////////////////
//The radial line function
var radarLine = d3.svg.line
.radial()
.interpolate("linear-closed")
.radius(function (d) {
return rScale(d.value);
})
.angle(function (d, i) {
return i * angleSlice;
});
if (cfg.roundStrokes) {
radarLine.interpolate("cardinal-closed");
}
//Create a wrapper for the blobs
var blobWrapper = g
.selectAll(".radarWrapper")
.data(data)
.enter()
.append("g")
.attr("class", "radarWrapper");
//Append the backgrounds
blobWrapper
.append("path")
.attr("class", "radarArea")
.attr("d", function (d, i) {
return radarLine(d);
})
.style("fill", function (d, i) {
return cfg.color(i);
})
.style("fill-opacity", cfg.opacityArea)
.on("mouseover", function (d, i) {
//Dim all blobs
d3.selectAll(".radarArea")
.transition()
.duration(200)
.style("fill-opacity", 0.1);
//Bring back the hovered over blob
d3.select(this).transition().duration(200).style("fill-opacity", 0.7);
})
.on("mouseout", function () {
//Bring back all blobs
d3.selectAll(".radarArea")
.transition()
.duration(200)
.style("fill-opacity", cfg.opacityArea);
});
//Create the outlines
blobWrapper
.append("path")
.attr("class", "radarStroke")
.attr("d", function (d, i) {
return radarLine(d);
})
.style("stroke-width", cfg.strokeWidth + "px")
.style("stroke", function (d, i) {
return cfg.color(i);
})
.style("fill", "none")
.style("filter", "url(#glow)");
//Append the circles
blobWrapper
.selectAll(".radarCircle")
.data(function (d, i) {
return d;
})
.enter()
.append("circle")
.attr("class", "radarCircle")
.attr("r", cfg.dotRadius)
.attr("cx", function (d, i) {
return rScale(d.value) * Math.cos(angleSlice * i - Math.PI / 2);
})
.attr("cy", function (d, i) {
return rScale(d.value) * Math.sin(angleSlice * i - Math.PI / 2);
})
.style("fill", function (d, i, j) {
return cfg.color(j);
})
.style("fill-opacity", 0.8);
//////// Append invisible circles for tooltip ///////////
//Wrapper for the invisible circles on top
var blobCircleWrapper = g
.selectAll(".radarCircleWrapper")
.data(data)
.enter()
.append("g")
.attr("class", "radarCircleWrapper");
//Append a set of invisible circles on top for the mouseover pop-up
blobCircleWrapper
.selectAll(".radarInvisibleCircle")
.data(function (d, i) {
return d;
})
.enter()
.append("circle")
.attr("class", "radarInvisibleCircle")
.attr("r", cfg.dotRadius * 1.5)
.attr("cx", function (d, i) {
return rScale(d.value) * Math.cos(angleSlice * i - Math.PI / 2);
})
.attr("cy", function (d, i) {
return rScale(d.value) * Math.sin(angleSlice * i - Math.PI / 2);
})
.style("fill", "none")
.style("pointer-events", "all")
.on("mouseover", function (d, i) {
newX = parseFloat(d3.select(this).attr("cx")) - 10;
newY = parseFloat(d3.select(this).attr("cy")) - 10;
tooltip
.attr("x", newX)
.attr("y", newY)
.text(Format(d.value))
.transition()
.duration(200)
.style("opacity", 1);
})
.on("mouseout", function () {
tooltip.transition().duration(200).style("opacity", 0);
});
//Set up the small tooltip for when you hover over a circle
var tooltip = g.append("text").attr("class", "tooltip").style("opacity", 0);
/////////////////// Helper Function /////////////////////
//Taken from http://bl.ocks.org/mbostock/7555321
//Wraps SVG text
function wrap(text, width) {
text.each(function () {
var text = d3.select(this),
words = text.text().split(/\s+/).reverse(),
word,
line = [],
lineNumber = 0,
lineHeight = 1.4, // ems
y = text.attr("y"),
x = text.attr("x"),
dy = parseFloat(text.attr("dy")),
tspan = text
.text(null)
.append("tspan")
.attr("x", x)
.attr("y", y)
.attr("dy", dy + "em");
while ((word = words.pop())) {
line.push(word);
tspan.text(line.join(" "));
if (tspan.node().getComputedTextLength() > width) {
line.pop();
tspan.text(line.join(" "));
line = [word];
tspan = text
.append("tspan")
.attr("x", x)
.attr("y", y)
.attr("dy", ++lineNumber * lineHeight + dy + "em")
.text(word);
}
}
});
} //wrap
} //RadarChart
} //componentDidMount
render() {
const styles = {
container: {
display: "grid",
justifyItems: "center"
}
};
return (
// <div ref="chart" style={styles.container}>
<div className=".radarChart" style={styles.container}>
<h1 style={{ textAlign: "center" }}>Hi, I'm the radar chart</h1>
</div>
);
}
}
export default App;
Edit: update: so I found some answers elsewhere, thank you #Michael Rovinsky for pointing me in the right direction. Some of this code was pre D3 version 4.
d3.scale.linear() vs d3.scaleLinear()
What is the d3.js v4.0 equivalent for d3.scale.category10()?
Current error: I am now getting
src\App.js
Line 406:8: 'newX' is not defined no-undef
Line 407:8: 'newY' is not defined no-undef
Line 410:21: 'newX' is not defined no-undef
Line 411:21: 'newY' is not defined no-undef
Which was fixed by defining them with "let" so the new code is
let newX = parseFloat(d3.select(this).attr("cx")) - 10; let newY = parseFloat(d3.select(this).attr("cy")) - 10;
Finally I was getting an error TypeError: Cannot read property 'radial' of undefined
which was solved by updating var radarLine = d3.svg.line.radial().interpolate("linear-closed").radius(...
to radarLine = d3.lineRadial().radius(...
The graph rendered once I fixed the typo className=".radarChart" to className="radarChart"

Label names are not displayed from input data in pie transition chart of d3.js

I am using d3.js to draw a pie transition chart. But when labels are placed in the data array as show below:
data = [{"label":"sector1", "value":25}, {"label":"sector2", "value":45}]
The pie chart won't be displayed. Instead "NaN" will be printed.
The complete code is pasted below:
var w = 400,
h = 400,
r = Math.min(w, h) / 2,
data = [{"label":"sector1", "value":25}, {"label":"sector2", "value":45}], // Data with label-value pairs
color = d3.scale.category20(),
arc = d3.svg.arc().outerRadius(r),
donut = d3.layout.pie();
var vis = d3.select("body").append("svg") // Place the chart in 'pie-chart-div'
.data([data])
.attr("width", w)
.attr("height", h);
var arcs = vis.selectAll("g.arc")
.data(donut)
.enter().append("g")
.attr("class", "arc")
.attr("transform", "translate(" + r + "," + r + ")");
var paths = arcs.append("path")
.attr("fill", function(d, i) { return color(i); });
var labels = arcs.append("text")
.attr("transform", function(d) { d.innerRadius = 120; return "translate(" + arc.centroid(d) + ")"; })
.attr("dy", ".35em")
.text(function(d) { return d.value; });
paths.transition()
.ease("bounce")
.duration(2000)
.attrTween("d", tweenPie);
paths.transition()
.ease("elastic")
.delay(function(d, i) { return 2000 + i * 50; })
.duration(750)
.attrTween("d", tweenDonut);
function tweenPie(b) {
b.innerRadius = 0;
var i = d3.interpolate({startAngle: 0, endAngle: 0}, b);
return function(t) {
return arc(i(t));
};
}
function tweenDonut(b) {
b.innerRadius = r * .6;
var i = d3.interpolate({innerRadius: 0}, b);
return function(t) {
return arc(i(t));
};
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
How to display the label names along with the values in the chart ?
You need to call donut with your data inside like that :
data2 = data.map(function(d) { return d.value}) // [25, 45]
...
.data(donut(data2))
and then call the label ;
.text(function(d, i) { return data[i].label; });
See http://jsfiddle.net/980f0cdj/1/

How to display labels in pie transition chart using d3.js?

I am using pie transition chart of d3.js to visualize the potential customers.
The graph is drawn, but the labels are not displayed on the graph.
Here is the code:
var w = 400,
h = 400,
r = Math.min(w, h) / 2,
data = [25,45], // Data values
color = d3.scale.category20(),
arc = d3.svg.arc().outerRadius(r),
donut = d3.layout.pie();
var vis = d3.select("#pie-chart-div").append("svg") // Place the chart in 'pie-chart-div'
.data([data])
.attr("width", w)
.attr("height", h);
var arcs = vis.selectAll("g.arc")
.data(donut)
.enter().append("g")
.attr("class", "arc")
.attr("transform", "translate(" + r + "," + r + ")");
var paths = arcs.append("path")
.attr("fill", function(d, i) { return color(i); });
paths.transition()
.ease("bounce")
.duration(2000)
.attrTween("d", tweenPie);
paths.transition()
.ease("elastic")
.delay(function(d, i) { return 2000 + i * 50; })
.duration(750)
.attrTween("d", tweenDonut);
function tweenPie(b) {
b.innerRadius = 0;
var i = d3.interpolate({startAngle: 0, endAngle: 0}, b);
return function(t) {
return arc(i(t));
};
}
function tweenDonut(b) {
b.innerRadius = r * .6;
var i = d3.interpolate({innerRadius: 0}, b);
return function(t) {
return arc(i(t));
};
}
How to display labels in the pie transition chart ?
To display the text in the "exact location" (which I supposed is in the middle of the arc), you can use the centroid() function:
var labels = arcs.append("text")
.attr("transform", function(d) {
d.innerRadius = 120;
return "translate(" + arc.centroid(d) + ")";
})
.attr("dy", ".35em")
.text(function(d) { return d.value; });
Check this demo:
var w = 400,
h = 400,
r = Math.min(w, h) / 2,
data = [25,45], // Data values
color = d3.scale.category20(),
arc = d3.svg.arc().outerRadius(r),
donut = d3.layout.pie();
var vis = d3.select("body").append("svg") // Place the chart in 'pie-chart-div'
.data([data])
.attr("width", w)
.attr("height", h);
var arcs = vis.selectAll("g.arc")
.data(donut)
.enter().append("g")
.attr("class", "arc")
.attr("transform", "translate(" + r + "," + r + ")");
var paths = arcs.append("path")
.attr("fill", function(d, i) { return color(i); });
var labels = arcs.append("text")
.attr("transform", function(d) { d.innerRadius = 120; return "translate(" + arc.centroid(d) + ")"; })
.attr("dy", ".35em")
.text(function(d) { return d.value; });
paths.transition()
.ease("bounce")
.duration(2000)
.attrTween("d", tweenPie);
paths.transition()
.ease("elastic")
.delay(function(d, i) { return 2000 + i * 50; })
.duration(750)
.attrTween("d", tweenDonut);
function tweenPie(b) {
b.innerRadius = 0;
var i = d3.interpolate({startAngle: 0, endAngle: 0}, b);
return function(t) {
return arc(i(t));
};
}
function tweenDonut(b) {
b.innerRadius = r * .6;
var i = d3.interpolate({innerRadius: 0}, b);
return function(t) {
return arc(i(t));
};
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>

D3 Donut Chart Enter with Transition

I have been working from this D3 block http://bl.ocks.org/dbuezas/9306799.
I have the code pretty much the way I want it. Except the chart only animates when part of the data set is shared. If the the dataset contains no labels in common, the graph is simply swapped out. How do I make it do a growing arc transition from the beginning so the user always knows when new data is presented? I am taking this snippet out of an angular directive so it might not stand quite on its own, fyi.
var padding = 0;
var height = 350;
var width = 450;
var svg = d3.select("body")
.append("svg")
.attr('viewBox','0 0 '+ width +' '+ height)
.attr('preserveAspectRatio','xMinYMin')
.append("g")
svg.append("g")
.attr("class", "slices");
svg.append("g")
.attr("class", "labels");
svg.append("g")
.attr("class", "lines");
var radius = Math.min(width, height) / 2;
var pie = d3.layout.pie()
.sort(null)
.value(function(d) {
return d.value;
});
var arc = d3.svg.arc()
.outerRadius(radius * 0.8)
.innerRadius(radius * 0.6);
var outerArc = d3.svg.arc()
.innerRadius(radius * 0.9)
.outerRadius(radius * 0.9);
svg.attr("transform", "translate(" + width / 2 + "," + element.parent().height() / 2 + ")");
var key = function(d){ return d.data.label; };
var color = d3.scale.category20b();
var dataset = [
{
label: "SUPPLY",
percentage: "22",
value: 10621
},
{
label: "FROZEN",
percentage: "22",
value: 17621
}
];
render(dataset);
function render(data) {
/* ------- PIE SLICES -------*/
var slice = svg.select(".slices").selectAll("path.slice")
.data(pie(data), key);
slice.enter()
.insert("path")
.style("fill", function(d) { return color(d.data.label); })
.attr("class", "slice");
slice
.transition().duration(1000)
.attrTween("d", function(d) {
this._current = this._current || d;
var interpolate = d3.interpolate(this._current, d);
this._current = interpolate(0);
return function(t) {
return arc(interpolate(t));
};
})
slice.exit()
.remove();
/* ------- TOOLTIP -----------*/
var tooltip = d3.select(element.parent()[0])
.append('div')
.attr('class', 'donut-tip bodhi-tooltip');
tooltip.append('div')
.attr('class', 'bodhi-tooltip-text');
slice.on('mouseover', function(d) {
tooltip.select('.bodhi-tooltip-text').html(d.data.label + '</br>' + d.data[scope.tooltipKeyOne] + "%" + '</br>' + "$" + (d.data.total / 100).toFixed(2));
tooltip.style('display', 'table');
});
slice.on('mouseout', function(d) {
tooltip.style('display', 'none');
});
/* ------- TEXT LABELS -------*/
var text = svg.select(".labels").selectAll("text")
.data(pie(data), key);
text.enter()
.append("text")
.attr("dy", ".35em")
.text(function(d) {
return d.data.label;
});
function midAngle(d){
return d.startAngle + (d.endAngle - d.startAngle)/2;
}
text.transition().duration(1000)
.attrTween("transform", function(d) {
this._current = this._current || d;
var interpolate = d3.interpolate(this._current, d);
this._current = interpolate(0);
return function(t) {
var d2 = interpolate(t);
var pos = outerArc.centroid(d2);
return "translate("+ pos +")";
};
})
.styleTween("text-anchor", function(d){
this._current = this._current || d;
var interpolate = d3.interpolate(this._current, d);
this._current = interpolate(0);
return function(t) {
var d2 = interpolate(t);
return midAngle(d2) < Math.PI ? "start":"end";
};
});
text.exit()
.remove();
};
The heart of the problem is there's no starting point for entering slices to tween from. That said, I'd really simplify the tween code a bit:
slice.enter()
.insert("path")
.style("fill", function(d) {
return color(d.data.label);
})
.attr("class", "slice")
.each(function(d){
this._current = { //<-- give the entering slices a starting point
startAngle: d.startAngle, //<-- have them "grow" from nothing
endAngle: d.startAngle
};
});
slice
.transition().duration(1000)
.attrTween("d", function(d) {
var endAt = { //<-- have the arc end where it's supposed to
startAngle: d.startAngle,
endAngle: d.endAngle
};
var interpolate = d3.interpolate(this._current, endAt);
this._current = endAt; //<-- store this for next cycle
return function(t) {
return arc(interpolate(t));
};
})
Full working code:
<!DOCTYPE html>
<html>
<head>
<script data-require="d3#3.5.3" data-semver="3.5.3" src="//cdnjs.cloudflare.com/ajax/libs/d3/3.5.3/d3.js"></script>
</head>
<body>
<script>
var padding = 0;
var height = 350;
var width = 450;
var svg = d3.select("body")
.append("svg")
.attr('viewBox', '0 0 ' + width + ' ' + height)
.attr('preserveAspectRatio', 'xMinYMin')
.append("g")
svg.append("g")
.attr("class", "slices");
svg.append("g")
.attr("class", "labels");
svg.append("g")
.attr("class", "lines");
var radius = Math.min(width, height) / 2;
var pie = d3.layout.pie()
.sort(null)
.value(function(d) {
return d.value;
});
var arc = d3.svg.arc()
.outerRadius(radius * 0.8)
.innerRadius(radius * 0.6);
var outerArc = d3.svg.arc()
.innerRadius(radius * 0.9)
.outerRadius(radius * 0.9);
svg.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");
var key = function(d) {
return d.data.label;
};
var color = d3.scale.category20b();
update();
setInterval(update, 2000);
function update() {
var someData = [];
for (var i = 0; i < Math.random() * 10; i++){
someData.push({
label: "Slice " + i,
percentage: "22",
value: Math.random()
});
}
render(someData);
}
function render(data) {
/* ------- PIE SLICES -------*/
var slice = svg.select(".slices").selectAll("path.slice")
.data(pie(data), key);
slice.enter()
.insert("path")
.style("fill", function(d) {
return color(d.data.label);
})
.attr("class", "slice")
.each(function(d){
this._current = {
startAngle: d.startAngle,
endAngle: d.startAngle
};
});
slice
.transition().duration(1000)
.attrTween("d", function(d) {
var endAt = {
startAngle: d.startAngle,
endAngle: d.endAngle
};
var interpolate = d3.interpolate(this._current, endAt);
this._current = endAt;
return function(t) {
return arc(interpolate(t));
};
})
slice.exit()
.remove();
/* ------- TEXT LABELS -------*/
var text = svg.select(".labels").selectAll("text")
.data(pie(data), key);
text.enter()
.append("text")
.attr("dy", ".35em")
.text(function(d) {
return d.data.label;
});
function midAngle(d) {
return d.startAngle + (d.endAngle - d.startAngle) / 2;
}
text.transition().duration(1000)
.attrTween("transform", function(d) {
this._current = this._current || d;
var interpolate = d3.interpolate(this._current, d);
this._current = interpolate(0);
return function(t) {
var d2 = interpolate(t);
var pos = outerArc.centroid(d2);
return "translate(" + pos + ")";
};
})
.styleTween("text-anchor", function(d) {
this._current = this._current || d;
var interpolate = d3.interpolate(this._current, d);
this._current = interpolate(0);
return function(t) {
var d2 = interpolate(t);
return midAngle(d2) < Math.PI ? "start" : "end";
};
});
text.exit()
.remove();
};
</script>
</body>
</html>

Piece out effect for Multilayer pie Chart using d3.js

I have a multilayer pie chart with different inner radius as shown below:
The code for the above pie chart one can find here:
var width = 960,
height = 500,
radius = Math.min(width, height) / 2;
var color = d3.scale.ordinal()
.range(["cyan", "green", "blue", "brown", "violet", "orange", "purple"]);
var arcMajor = d3.svg.arc()
.outerRadius(function (d) {
return radius - 20;
})
.innerRadius(0);
//this for making the minor arc
var arcMinor = d3.svg.arc()
.outerRadius(function (d) {
// scale for calculating the radius range([20, radius - 40])
var s = scale((d.data.major - d.data.minor));
if (s > radius - 20) {
return radius - 20;
}
return scale(d.data.major - d.data.minor);
})
.innerRadius(0);
var labelr = 260;
var pie = d3.layout.pie()
.sort(null)
.value(function (d) {
return d.major;
});
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");
data = [{
major: 500,
minor: 250,
grp: 1
}, {
major: 100,
minor: 80,
grp: 2
}, {
major: 100,
minor: 50,
grp: 3
}, {
major: 100,
minor: 60,
grp: 4
}, {
major: 100,
minor: 10,
grp: 5
}];
var scale = d3.scale.linear()
.range([d3.min(data, function (d) {
return d.minor;
}), radius - 100 - d3.max(data, function (d) {
return d.minor / d.major;
})])
//setting the scale domain
.domain([d3.min(data, function (d) {
return d.minor;
}), d3.max(data, function (d) {
return d.minor;
})]);
var g = svg.selectAll(".arc")
.data(pie(data))
.enter().append("g")
.attr("class", "arc");
g.append("svg:text")
.attr("transform", function (d) {
var c = arcMajor.centroid(d),
x = c[0],
y = c[1],
// pythagorean theorem for hypotenuse
h = Math.sqrt(x * x + y * y);
return "translate(" + (x / h * labelr) + ',' +
(y / h * labelr) + ")";
})
.attr("dy", ".35em")
.attr("text-anchor", function (d) {
// are we past the center?
return (d.endAngle + d.startAngle) / 2 > Math.PI ?
"end" : "start";
})
.text(function (d, i) { return d.value.toFixed(2); });
//this makes the major arc
g.append("path")
.attr("d", function (d) {
return arcMajor(d);
})
.style("fill", function (d) {
return d3.rgb(color(d.data.grp));
});
//this makes the minor arcs
g.append("path")
.attr("d", function (d) {
return arcMinor(d);
})
.style("fill", function (d) {
return d3.rgb(color(d.data.grp)).darker(2);//for making the inner path darker
});
http://jsfiddle.net/6e8aue0h/10/
I want to add piece out feature to this pie. Like this:
I tried using d3-pie plugin but it didnt work.
When you hover over particular section it should get pie out as shown in below figure.
https://github.com/dansdom/plugins-d3-pie
How can I implement this in this particular situation?
Thanking you very much.
here I added piece out effect for major pie in similar fashion you are able to add for inner pie. I have added arc over variable
var arcOver = d3.svg.arc()
.outerRadius(radius + 9);
and mouseenter and mouseout function for major arc.
.on("mouseenter", function(d) {
d3.select(this)
.attr("stroke","white")
.transition()
.duration(1000)
.attr("d", arcOver)
.attr("stroke-width",6);
})
.on("mouseleave", function(d) {
d3.select(this).transition()
.attr("d", arcMajor)
.attr("stroke","none");
})
here is the sample.
var width = 960,
height = 500,
radius = Math.min(width, height) / 2;
var color = d3.scale.ordinal()
.range(["cyan", "green", "blue", "brown", "violet", "orange", "purple"]);
var arcMajor = d3.svg.arc()
.outerRadius(function (d) {
return radius - 20;
})
.innerRadius(0);
var arcOver = d3.svg.arc()
.outerRadius(radius + 9);
//this for making the minor arc
var arcMinor = d3.svg.arc()
.outerRadius(function (d) {
// scale for calculating the radius range([20, radius - 40])
var s = scale((d.data.major - d.data.minor));
if (s > radius - 20) {
return radius - 20;
}
return scale(d.data.major - d.data.minor);
})
.innerRadius(0);
var arcOverMin = d3.svg.arc()
.outerRadius(radius - 90 );
// Define the div for the tooltip
var div = d3.select("body").append("div")
.attr("class", "tooltip")
.style("opacity", 0);
var labelr = 260;
var pie = d3.layout.pie()
.sort(null)
.value(function (d) {
return d.major;
});
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");
data = [{
major: 500,
minor: 250,
grp: 1
}, {
major: 100,
minor: 80,
grp: 2
}, {
major: 100,
minor: 50,
grp: 3
}, {
major: 100,
minor: 60,
grp: 4
}, {
major: 100,
minor: 10,
grp: 5
}];
var scale = d3.scale.linear()
.range([d3.min(data, function (d) {
return d.minor;
}), radius - 100 - d3.max(data, function (d) {
return d.minor / d.major;
})])
//setting the scale domain
.domain([d3.min(data, function (d) {
return d.minor;
}), d3.max(data, function (d) {
return d.minor;
})]);
var g = svg.selectAll(".arc")
.data(pie(data))
.enter().append("g")
.attr("class", "arc");
g.append("svg:text")
.attr("transform", function (d) {
var c = arcMajor.centroid(d),
x = c[0],
y = c[1],
// pythagorean theorem for hypotenuse
h = Math.sqrt(x * x + y * y);
return "translate(" + (x / h * labelr) + ',' +
(y / h * labelr) + ")";
})
.attr("dy", ".35em")
.attr("text-anchor", function (d) {
// are we past the center?
return (d.endAngle + d.startAngle) / 2 > Math.PI ?
"end" : "start";
})
.text(function (d, i) { return d.value.toFixed(2); });
//this makes the major arc
g.append("path")
.attr("d", function (d) {
return arcMajor(d);
})
.on("mouseenter", function(d) {
d3.select(this)
.attr("stroke","white")
.transition()
.duration(1000)
.attr("d", arcOver)
.attr("stroke-width",6);
div.transition()
.duration(200)
.style("opacity", .9);
div.html(
'<a href= "http://facebook.com">' + // The first <a> tag
d.data.major +
"</a>"
+ "<br/>" + d.data.minor)
.style("left", (d3.event.pageX) + "px")
.style("top", (d3.event.pageY - 28) + "px");
})
.on("mouseleave", function(d) {
d3.select(this).transition()
.attr("d", arcMajor)
.attr("stroke","none");
})
.style("fill", function (d) {
return d3.rgb(color(d.data.grp));
});
//this makes the minor arcs
g.append("path")
.attr("d", function (d) {
return arcMinor(d);
})
.on("mouseenter", function(d) {
d3.select(this)
.attr("stroke","white")
.transition()
.duration(1000)
.attr("d", arcOverMin)
.attr("stroke-width",6);
})
.on("mouseleave", function(d) {
d3.select(this).transition()
.attr("d", arcMinor)
.attr("stroke","none");
})
.style("fill", function (d) {
return d3.rgb(color(d.data.grp)).darker(2);//for making the inner path darker
});
.arc text {
font: 10px sans-serif;
text-anchor: middle;
}
.arc path {
stroke: #fff;
}
div.tooltip {
position: absolute;
text-align: center;
width: 60px;
height: 28px;
padding: 2px;
font: 12px sans-serif;
background: lightsteelblue;
border: 0px;
border-radius: 8px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
You will just need to increase the outerRadius of arcs for implementing this effect.
Hope this helps.
var arcMajorOver = d3.svg.arc()
.outerRadius(function(d) {
return radius - 10;
});
var arcMinorOver = d3.svg.arc()
.outerRadius(function(d) {
var s = scale((d.data.major - d.data.minor));
if (s > radius - 20) {
return radius - 20;
}
return scale(d.data.major - d.data.minor) + 10;
});
//this makes the major arc
g.append("path")
.attr("d", function(d) {
return arcMajor(d);
})
.style("fill", function(d) {
return d3.rgb(color(d.data.grp));
}).on("mouseenter", function(d) {
d3.select(this)
.attr("stroke", "white")
.transition()
.duration(1000)
.attr("d", arcMajorOver)
.style("stroke-width",6);
})
.on("mouseleave", function(d) {
d3.select(this).transition()
.attr("d", arcMajor)
.style("stroke-width",0);
});;;
//this makes the minor arcs
g.append("path")
.attr("d", function(d) {
return arcMinor(d);
})
.style("fill", function(d) {
return d3.rgb(color(d.data.grp)).darker(2); //for making the inner path darker
}).on("mouseenter", function(d) {
d3.select(this)
.attr("stroke", "white")
.transition()
.duration(1000)
.attr("d", arcMinorOver)
.style("stroke-width",6);
})
.on("mouseleave", function(d) {
d3.select(this).transition()
.attr("d", arcMinor)
.style("stroke-width",0);
});
var width = 960,
height = 500,
radius = Math.min(width, height) / 2;
var color = d3.scale.ordinal()
.range(["cyan", "green", "blue", "brown", "violet", "orange", "purple"]);
var arcMajor = d3.svg.arc()
.outerRadius(function(d) {
return radius - 20;
})
.innerRadius(0);
var arcMajorOver = d3.svg.arc()
.outerRadius(function(d) {
return radius - 10;
});
//this for making the minor arc
var arcMinor = d3.svg.arc()
.outerRadius(function(d) {
// scale for calculating the radius range([20, radius - 40])
var s = scale((d.data.major - d.data.minor));
if (s > radius - 20) {
return radius - 20;
}
return scale(d.data.major - d.data.minor);
})
.innerRadius(0);
var arcMinorOver = d3.svg.arc()
.outerRadius(function(d) {
var s = scale((d.data.major - d.data.minor));
if (s > radius - 20) {
return radius - 20;
}
return scale(d.data.major - d.data.minor) + 10;
});
var labelr = 260;
var pie = d3.layout.pie()
.sort(null)
.value(function(d) {
return d.major;
});
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");
data = [{
major: 500,
minor: 250,
grp: 1
}, {
major: 100,
minor: 80,
grp: 2
}, {
major: 100,
minor: 50,
grp: 3
}, {
major: 100,
minor: 60,
grp: 4
}, {
major: 100,
minor: 10,
grp: 5
}];
var scale = d3.scale.linear()
.range([d3.min(data, function(d) {
return d.minor;
}), radius - 100 - d3.max(data, function(d) {
return d.minor / d.major;
})])
//setting the scale domain
.domain([d3.min(data, function(d) {
return d.minor;
}), d3.max(data, function(d) {
return d.minor;
})]);
var g = svg.selectAll(".arc")
.data(pie(data))
.enter().append("g")
.attr("class", "arc");
g.append("svg:text")
.attr("transform", function(d) {
var c = arcMajor.centroid(d),
x = c[0],
y = c[1],
// pythagorean theorem for hypotenuse
h = Math.sqrt(x * x + y * y);
return "translate(" + (x / h * labelr) + ',' +
(y / h * labelr) + ")";
})
.attr("dy", ".35em")
.attr("text-anchor", function(d) {
// are we past the center?
return (d.endAngle + d.startAngle) / 2 > Math.PI ?
"end" : "start";
})
.text(function(d, i) {
return d.value.toFixed(2);
});
//this makes the major arc
g.append("path")
.attr("d", function(d) {
return arcMajor(d);
})
.style("fill", function(d) {
return d3.rgb(color(d.data.grp));
}).on("mouseenter", function(d) {
d3.select(this)
.transition()
.duration(1000)
.attr("d", arcMajorOver)
.style("stroke-width",6);
})
.on("mouseleave", function(d) {
d3.select(this).transition()
.attr("d", arcMajor)
.style("stroke-width", 0);
});;;
//this makes the minor arcs
g.append("path")
.attr("d", function(d) {
return arcMinor(d);
})
.style("fill", function(d) {
return d3.rgb(color(d.data.grp)).darker(2); //for making the inner path darker
}).on("mouseenter", function(d) {
d3.select(this)
.transition()
.duration(1000)
.attr("d", arcMinorOver)
.style("stroke-width",6);
})
.on("mouseleave", function(d) {
d3.select(this).transition()
.attr("d", arcMinor)
.style("stroke-width", 0);
});
.arc text {
font: 10px sans-serif;
text-anchor: middle;
}
.arc path {
stroke: #fff;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>

Categories

Resources