Currently trying to animate the drawing of a PieChart in d3.js
and I'm currently having difficulty with the ArcTween function, heres my code:
<!doctype HTML>
<html>
<head>
<title>Page Title</title>
<meta charset="UTF-8">
<script type="text/javascript" src="js/d3.min.js"></script>
<link rel="stylesheet" type="text/css" href="css/style.css">
</head>
<body>
<script type="text/javascript">
//=========================================================================================================================================
// initializing variables
var data = []; // empty array to hold the objects imported from the JSON file
var oRadius = 300; //var holding value for the outer radius of the arc
var iRadius = 80; //var holding the value for the inner radius of the arc
var cRadius = 3; //var holding the value for the corner radius of the arc
var colors = d3.scale.category20b();//built in D3 function to color pieces of data
var width = 1400; //setting the width of the svg
var height = 1000; //setting the height of the svg
var dRadius = 5; //setting the radius for the dots
var sColor = "white"; // color for the stroke of the arcs
var dStrokeColor = "#666"; // stroke color for the dots
var dFillColor = "#ccc" // fill color for the dots
var fontSize = 25; // font size value for the text labels
var numFont = 15; // font size for the number labels
var linePadding = 160; // value to translate the text and number labels to the end of each polyLine
var myArcMaker= d3.svg.arc().outerRadius(oRadius).innerRadius(iRadius).cornerRadius(cRadius); //var that returns the values needed to create the arcs of the pie chart
var bigArcMaker= d3.svg.arc().outerRadius(400).innerRadius(oRadius).cornerRadius(cRadius); //var that returns the values needed to create the arcs of the big pie chart used to draw the polylines
var mySvg = d3.select('body') //select the html body in the DOM
.append('svg') // place an empty svg in the body
.attr('width', width) //give the svg this width
.attr("height", height)//give the svg this height
.append("g")//append a group to the svg
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")")// centers the pie chart in the center of the svg this will make sure everything in the group is centered
mySvg.append("g") //creates a group
.attr("class", "slices"); // give it a class of slices, used for the pie chart arcs
mySvg.append("g") // creates a group
.attr("class", "dots"); // give it a class of dots, used for the circles in the centre of the arcs
mySvg.append("g")//creates a group
.attr("class", "lines");// give it a class of lines, used for the big pie chart surrounding the main piechart
mySvg.append("g")//creates a group
.attr("class", "polyLines");//gives it a class of polylines, used for the polylines protruding it from the pie chart arcs
mySvg.append("g")//creates a group
.attr("class", "labels");//gives a class of labels, used for the text labels at the end of the polylines
mySvg.append("g")//creates a group
.attr("class", "numlabels");//gives it a class of numlabels, used for the number labels at the end of the polylines
var myPie = d3.layout.pie()
.sort(null) //removes any d3 sorting
.startAngle((Math.PI)/360) // setting the start angle for the arcs
.endAngle((2*(Math.PI))) // setting the end angle for the arcs
.padAngle(2*(2*(Math.PI))/360).value(function(d){return d.value}); //setting the values for that start angle, end angle and pad angle for the arcs and takes in the the values from the objects in the data array
//======================================================================================================================================================
d3.json("data.json", function (json) // importing the json file
{
data = json; // setting the empty data array equal to the values of the objects in the json file
visual(); // this function holds all the d3 code to create the arc
})
//======================================================================================================================================================
function visual() // this function prevents the code that creates the arc from running before the objects from the json file are added into the empty data array
{
// console.log(data); // checking to see if the objects are loaded into the data ray using the console in chrome
var slice = mySvg.select("g.slices") //selecting the class slices
.selectAll("path.slice")//selecting all paths in the slice class
.data(myPie(data))//binds the data to the DOM elements
.enter()//begin looping through the data
.append("path")//append a path
.attr("class", "slice")//give it a class of slice
.attr("d", function(d) {//runs the data in the dataset through the arcmaker function
return myArcMaker(d)
})
.attr("fill", function(d, i) {
return colors(i);
}) //using the d3 color brewer to color each arc
.each(function(d) { this._current = d; })
.attr("stroke", "white") //giving each arc a stroke of white
.style('stroke-width',2) //gives the stroke a width of 2px
.each(function(d) { this._current = d; })
.transition().duration(750).attrTween("d", arcTween); // redraw the arcs
var dots = mySvg.select("g.dots") //select the group dots
.selectAll("cirlces") //select all circles in the dots group
.data(myPie(data))//binds the data to the DOM elements
.enter()//begin the loop
.append("circle")//draw a circle
.attr("class", "g.dots")//gives it a class of dots
.attr("transform", function(d)//transform each circle to the centre of each arc using the .centroid method
{
return "translate(" + myArcMaker.centroid(d) + ")";
})
.attr("r", dRadius)//setting the radius of the circle
.attr("fill", dFillColor)//setting the fill color
.attr("stroke", sColor)//setting the stroke color
//
var lines = mySvg.select(".lines") //select the lines class *--------------------------------------*
.selectAll("path.lines")//select all paths in the lines class * This section here is to draw *
.data(myPie(data)) //binds the data to the DOM elements * the invisible piechart that *
.enter()//begin the loop * surrounds the smaller piechart this *
.append("path")//draw a path * used for the polylines later on *
.attr("class", "lines")//give it a class of lines *--------------------------------------*
.attr("d", function(d) {//set the d value to the values returned from the bigArcMaker function
return bigArcMaker(d)
}).attr("fill", "none")//give the fill a value of none
.attr("stroke", "none")//give the stroke a value of none
// var outerDots = mySvg.select("g.dots")//select the dots group
// .selectAll("cirlces")//select all circles in the dots group
// .data(myPie(data))//binds the data to the DOM elements
// .enter()
// .append("circle")//create a circle
// .attr("class", "g.dots")//give it a class of dots
// .attr("transform", function(d)//translate it to the centroid of the bigArc surrounding the main pie chart
// {
// return "translate(" + bigArcMaker.centroid(d) + ")";
// })
// .attr("r", dRadius)
// .attr("fill", dFillColor)
// .attr("stroke", sColor)
// var x1 = myArcMaker.centroid(d)[0];
// var y1 = myArcMaker.centroid(d)[1];
// var x2 = bigArcMaker.centroid(d)[0];
// var y2 = bigArcMaker.centroid(d)[1];
// var x3 = function(d){if(x2<0){return bigArcMaker.centroid(d)[0]-160}}
// var lineData = [{'x': x1},
// ]
var polyLines = mySvg.select(".polyLines")//select the class of polylines
.selectAll("polylines")//sellect all polylines in the class of polylines
.data(myPie(data))//binds the data to the DOM elements
.enter()
.append("polyline")//create a polyline
.attr("class", "polyLines")//give it a class of polylines
.attr("points", function(d) {//set the points for the polylines
var p = " "; //used because i was getting errors with my strings also makes this the conditional statement more readable
p += myArcMaker.centroid(d)[0] + ',' + myArcMaker.centroid(d)[1] + ',' + bigArcMaker.centroid(d)[0] + ',' + bigArcMaker.centroid(d)[1] + ',';
p += bigArcMaker.centroid(d)[0] < 0 ? bigArcMaker.centroid(d)[0] - linePadding : bigArcMaker.centroid(d)[0] + linePadding; //if the x value for the bigArc maker is less than zero - linePadding, if its greater, add linePading
p += ',' + bigArcMaker.centroid(d)[1];
return p;
})
.attr("fill", "none")//gives the fill value none
.attr("stroke", sColor) //gives the stroke a color of sColor
.style('stroke-width',2)//gives the stroke-width of 2 pixels
var labels = mySvg.select(".labels")//select class labels
.selectAll("text")//select all text
.data(myPie(data))//binds the data to the DOM elements
.enter()
.append("text")//create a text
.text(function(d) //setting the text value to the Fruits label in the dataset
{
return d.data.Fruits;
})
.style("fill", sColor)//sets the fill to the sColor attribute
.attr("x", function(d)//conditional statement that determines where the text will be placed, similar to the polylines
{
if(bigArcMaker.centroid(d)[0]>0)
{
return bigArcMaker.centroid(d)[0]+linePadding;
}
else
{
return bigArcMaker.centroid(d)[0]-linePadding;
}
})
.attr("y", function(d){return bigArcMaker.centroid(d)[1]})//set the why value to the y value of the centroid array
.attr("text-anchor", function(d){
if(bigArcMaker.centroid(d)>0)//conditional statement that sets the text anchor for each piece of text, similar to polylines
{
return "start"
}
else{
return "end"
}
})
.attr("font-family", "Helvetica") // sets the font family
.style("font-size", fontSize)//sets the font size
var numLabels = mySvg.select(".numlabels") //select the class numlabels
.selectAll("text")//selects all text in the class numlabels
.data(myPie(data))//binds the data to the DOM elements
.enter()
.append("text")//append a text
.text(function(d) //sets the value of the text to the value attribute in the dataset array
{
return d.data.value;
})
.style("fill", sColor)//sets the fill color
.attr("x", function(d)//conditional statement to set the x value of the text similar to the polylines and labels
{
if(bigArcMaker.centroid(d)[0]>0)
{
return bigArcMaker.centroid(d)[0]+linePadding;
}
else
{
return bigArcMaker.centroid(d)[0]-linePadding;
}
})
.attr("y", function(d){return bigArcMaker.centroid(d)[1]})//set the why value to the y value of the centroid array
.attr("text-anchor", function(d){
if(bigArcMaker.centroid(d)>0)//conditional statement that sets the text anchor for each piece of text, similar to polylines
{
return "start"
}
else{
return "end"
}
})
.attr("dy", 20)
.attr("font-family", "Helvetica") // sets the font family
.style("font-size", numFont)
function arcTween(a)
{
var i = d3.interpolate(this._current, a);
this._current = i(0);
return function(t)
{
return myArcMaker(i(t));
};
}
}
</script>
</body>
</html>
I want it to start from the start angle and draw to the end angle, ive tried using d.startAngle and d.endAngle but when I inspect it in chrome I get an uncaught reference error because d is not defined. Currently the code is compiling and I'm getting no errors when I inspect it, I'm not quite sure how this works so an explination would be great
heres my JSFiddle: http://jsfiddle.net/JamieHyland1/gyekfnr2/
You are close, your tween function is a little off. Also, you can scrap all the this._current
stuff and just make a closure in the tween function itself.
function arcTween(a) { //<-- a is the datum bound to each arc
var startAngle = a.startAngle; //<-- keep reference to start angle
var i = d3.interpolate(a.startAngle, a.endAngle); //<-- interpolate start to end
return function(t) {
return myArcMaker({ //<-- return arc at each iteration from start to interpolate end
startAngle: startAngle,
endAngle: i(t)
});
};
}
You call this as:
var slice = mySvg.select("g.slices") //selecting the class slices
.selectAll("path.slice") //selecting all paths in the slice class
.data(myPie(data)) //binds the data to the DOM elements
.enter() //begin looping through the data
.append("path") //append a path
.attr("class", "slice") //give it a class of slice
.attr("fill", function(d, i) {
return colors(i);
}) //using the d3 color brewer to color each arc
.attr("stroke", "white") //giving each arc a stroke of white
.style('stroke-width', 2) //gives the stroke a width of 2px
.transition().duration(750).attrTween("d", arcTween); // redraw the arcs
Full code:
<!DOCTYPE html> <html> <head> <title>Page Title</title> <meta charset="UTF-8" /> <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 type="text/javascript"> //========================================================================================================================================= // initializing variables var data = []; // empty array to hold the objects imported from the JSON file var oRadius = 300; //var holding value for the outer radius of the arc var iRadius = 80; //var holding the value for the inner radius of the arc var cRadius = 3; //var holding the value for the corner radius of the arc var colors = d3.scale.category20b(); //built in D3 function to color pieces of data var width = 1400; //setting the width of the svg var height = 1000; //setting the height of the svg var dRadius = 5; //setting the radius for the dots var sColor = "white"; // color for the stroke of the arcs var dStrokeColor = "#666"; // stroke color for the dots var dFillColor = "#ccc" // fill color for the dots var fontSize = 25; // font size value for the text labels var numFont = 15; // font size for the number labels var linePadding = 160; // value to translate the text and number labels to the end of each polyLine var myArcMaker = d3.svg.arc().outerRadius(oRadius).innerRadius(iRadius).cornerRadius(cRadius); //var that returns the values needed to create the arcs of the pie chart var bigArcMaker = d3.svg.arc().outerRadius(400).innerRadius(oRadius).cornerRadius(cRadius); //var that returns the values needed to create the arcs of the big pie chart used to draw the polylines var mySvg = d3.select('body') //select the html body in the DOM .append('svg') // place an empty svg in the body .attr('width', width) //give the svg this width .attr("height", height) //give the svg this height .append("g") //append a group to the svg .attr("transform", "translate(" + width / 2 + "," + height / 2 + ")") // centers the pie chart in the center of the svg this will make sure everything in the group is centered mySvg.append("g") //creates a group .attr("class", "slices"); // give it a class of slices, used for the pie chart arcs mySvg.append("g") // creates a group .attr("class", "dots"); // give it a class of dots, used for the circles in the centre of the arcs mySvg.append("g") //creates a group .attr("class", "lines"); // give it a class of lines, used for the big pie chart surrounding the main piechart mySvg.append("g") //creates a group .attr("class", "polyLines"); //gives it a class of polylines, used for the polylines protruding it from the pie chart arcs mySvg.append("g") //creates a group .attr("class", "labels"); //gives a class of labels, used for the text labels at the end of the polylines mySvg.append("g") //creates a group .attr("class", "numlabels"); //gives it a class of numlabels, used for the number labels at the end of the polylines var myPie = d3.layout.pie() .sort(null) //removes any d3 sorting .startAngle((Math.PI) / 360) // setting the start angle for the arcs .endAngle((2 * (Math.PI))) // setting the end angle for the arcs .padAngle(2 * (2 * (Math.PI)) / 360).value(function(d) { return d.value }); //setting the values for that start angle, end angle and pad angle for the arcs and takes in the the values from the objects in the data array //====================================================================================================================================================== // d3.json("data.json", function (json) // importing the json file // { data = [{ value: 10 }, { value: 20 }, { value: 30 }]; visual(); // this function holds all the d3 code to create the arc // }) //====================================================================================================================================================== function visual() // this function prevents the code that creates the arc from running before the objects from the json file are added into the empty data array { // console.log(data); // checking to see if the objects are loaded into the data ray using the console in chrome var slice = mySvg.select("g.slices") //selecting the class slices .selectAll("path.slice") //selecting all paths in the slice class .data(myPie(data)) //binds the data to the DOM elements .enter() //begin looping through the data .append("path") //append a path .attr("class", "slice") //give it a class of slice .attr("fill", function(d, i) { return colors(i); }) //using the d3 color brewer to color each arc .attr("stroke", "white") //giving each arc a stroke of white .style('stroke-width', 2) //gives the stroke a width of 2px .transition().duration(750).attrTween("d", arcTween); // redraw the arcs var dots = mySvg.select("g.dots") //select the group dots .selectAll("cirlces") //select all circles in the dots group .data(myPie(data)) //binds the data to the DOM elements .enter() //begin the loop .append("circle") //draw a circle .attr("class", "g.dots") //gives it a class of dots .attr("transform", function(d) //transform each circle to the centre of each arc using the .centroid method { return "translate(" + myArcMaker.centroid(d) + ")"; }) .attr("r", dRadius) //setting the radius of the circle .attr("fill", dFillColor) //setting the fill color .attr("stroke", sColor) //setting the stroke color // var lines = mySvg.select(".lines") //select the lines class *--------------------------------------* .selectAll("path.lines") //select all paths in the lines class * This section here is to draw * .data(myPie(data)) //binds the data to the DOM elements * the invisible piechart that * .enter() //begin the loop * surrounds the smaller piechart this * .append("path") //draw a path * used for the polylines later on * .attr("class", "lines") //give it a class of lines *--------------------------------------* .attr("d", function(d) { //set the d value to the values returned from the bigArcMaker function return bigArcMaker(d) }).attr("fill", "none") //give the fill a value of none .attr("stroke", "none") //give the stroke a value of none // var outerDots = mySvg.select("g.dots")//select the dots group // .selectAll("cirlces")//select all circles in the dots group // .data(myPie(data))//binds the data to the DOM elements // .enter() // .append("circle")//create a circle // .attr("class", "g.dots")//give it a class of dots // .attr("transform", function(d)//translate it to the centroid of the bigArc surrounding the main pie chart // { // return "translate(" + bigArcMaker.centroid(d) + ")"; // }) // .attr("r", dRadius) // .attr("fill", dFillColor) // .attr("stroke", sColor) // var x1 = myArcMaker.centroid(d)[0]; // var y1 = myArcMaker.centroid(d)[1]; // var x2 = bigArcMaker.centroid(d)[0]; // var y2 = bigArcMaker.centroid(d)[1]; // var x3 = function(d){if(x2<0){return bigArcMaker.centroid(d)[0]-160}} // var lineData = [{'x': x1}, // ] var polyLines = mySvg.select(".polyLines") //select the class of polylines .selectAll("polylines") //sellect all polylines in the class of polylines .data(myPie(data)) //binds the data to the DOM elements .enter() .append("polyline") //create a polyline .attr("class", "polyLines") //give it a class of polylines .attr("points", function(d) { //set the points for the polylines var p = " "; //used because i was getting errors with my strings also makes this the conditional statement more readable p += myArcMaker.centroid(d)[0] + ',' + myArcMaker.centroid(d)[1] + ',' + bigArcMaker.centroid(d)[0] + ',' + bigArcMaker.centroid(d)[1] + ','; p += bigArcMaker.centroid(d)[0] < 0 ? bigArcMaker.centroid(d)[0] - linePadding : bigArcMaker.centroid(d)[0] + linePadding; //if the x value for the bigArc maker is less than zero - linePadding, if its greater, add linePading p += ',' + bigArcMaker.centroid(d)[1]; return p; }) .attr("fill", "none") //gives the fill value none .attr("stroke", sColor) //gives the stroke a color of sColor .style('stroke-width', 2) //gives the stroke-width of 2 pixels var labels = mySvg.select(".labels") //select class labels .selectAll("text") //select all text .data(myPie(data)) //binds the data to the DOM elements .enter() .append("text") //create a text .text(function(d) //setting the text value to the Fruits label in the dataset { return d.data.Fruits; }) .style("fill", sColor) //sets the fill to the sColor attribute .attr("x", function(d) //conditional statement that determines where the text will be placed, similar to the polylines { if (bigArcMaker.centroid(d)[0] > 0) { return bigArcMaker.centroid(d)[0] + linePadding; } else { return bigArcMaker.centroid(d)[0] - linePadding; } }) .attr("y", function(d) { return bigArcMaker.centroid(d)[1] }) //set the why value to the y value of the centroid array .attr("text-anchor", function(d) { if (bigArcMaker.centroid(d) > 0) //conditional statement that sets the text anchor for each piece of text, similar to polylines { return "start" } else { return "end" } }) .attr("font-family", "Helvetica") // sets the font family .style("font-size", fontSize) //sets the font size var numLabels = mySvg.select(".numlabels") //select the class numlabels .selectAll("text") //selects all text in the class numlabels .data(myPie(data)) //binds the data to the DOM elements .enter() .append("text") //append a text .text(function(d) //sets the value of the text to the value attribute in the dataset array { return d.data.value; }) .style("fill", sColor) //sets the fill color .attr("x", function(d) //conditional statement to set the x value of the text similar to the polylines and labels { if (bigArcMaker.centroid(d)[0] > 0) { return bigArcMaker.centroid(d)[0] + linePadding; } else { return bigArcMaker.centroid(d)[0] - linePadding; } }) .attr("y", function(d) { return bigArcMaker.centroid(d)[1] }) //set the why value to the y value of the centroid array .attr("text-anchor", function(d) { if (bigArcMaker.centroid(d) > 0) //conditional statement that sets the text anchor for each piece of text, similar to polylines { return "start" } else { return "end" } }) .attr("dy", 20) .attr("font-family", "Helvetica") // sets the font family .style("font-size", numFont) function arcTween(a) { var startAngle = a.startAngle; var i = d3.interpolate(a.startAngle, a.endAngle); return function(t) { return myArcMaker({ startAngle: startAngle, endAngle: i(t) }); }; } } </script> </body> </html>
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.