I'm trying to draw pie chart. I've found the working example which was available in d3.v3. I've updated the d3 version to d3.v4 since then, chart is not working.
Tried to figure it out, but couldn't get the solution. Can any please suggest here what's wrong with this code
<!DOCTYPE html>
<html>
<head>
<title>Pie</title>
<script type="text/javascript" src="https://d3js.org/d3.v4.min.js"></script>
</head>
<body>
</body>
</html>
<script type="text/javascript">
var dataset = {
apples: [53245, 28479, 19697, 24037, 40245, 34234],
oranges: [53245, 28479, 19697, 24037, 40245, 34234],
lemons: [53245, 28479, 19697, 24037, 40245, 34234],
pears: [53245, 28479, 19697, 24037, 40245, 34234],
banana: [53245, 28479, 19697, 24037, 40245, 34234],
pineapples: [53245, 28479, 19697, 24037, 40245, 34234],
};
var width = 460,
height = 300,
cwidth = 25;
// d3.v3
/*
var color = d3.scale.category20();
var pie = d3.layout.pie().sort(null);
var arc = d3.svg.arc();
*/
// d3.v4
var color = d3.scaleOrdinal(d3.schemeCategory10);
var pie = d3.pie().sort(null);
var arc = d3.arc();
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");
var gs = svg.selectAll("g").data(d3.values(dataset)).enter().append("g");
var path = gs.selectAll("path")
.data(function(d) { return pie(d); })
.enter().append("path")
.attr("fill", function(d, i) { return color(i); })
.attr("d", function(d, i, j) { return arc.innerRadius(10+cwidth*j).outerRadius(cwidth*(j+1))(d); });
</script>
You use third argument (j) here:
function(d, i, j) { return arc.innerRadius(10 + cwidth * j) ....
In d3v3 this argument is the index of the parent dataset, but in d3v4 it is the set (array) of dom-nodes. So you should store parent index somewhere. Rewrite your path variable this way:
var path = gs.selectAll("path")
.data(function(d, i) {
return pie(d).map(function(item) {
return { data: item, parentIndex: i }; // save parent dataset index in parenIndex property
});
})
.enter().append("path")
.attr("fill", function(d, i) { return color(i); })
.attr("d", function(d, i) {
return arc
.innerRadius(10+cwidth*d.parentIndex)
.outerRadius(cwidth*(d.parentIndex+1))(d.data);
});
Check working example:
var dataset = {
apples: [53245, 28479, 19697, 24037, 40245, 34234],
oranges: [53245, 28479, 19697, 24037, 40245, 34234],
lemons: [53245, 28479, 19697, 24037, 40245, 34234],
pears: [53245, 28479, 19697, 24037, 40245, 34234],
banana: [53245, 28479, 19697, 24037, 40245, 34234],
pineapples: [53245, 28479, 19697, 24037, 40245, 34234],
};
var width = 460,
height = 300,
cwidth = 25;
// d3.v3
/*
var color = d3.scale.category20();
var pie = d3.layout.pie().sort(null);
var arc = d3.svg.arc();
*/
// d3.v4
var color = d3.scaleOrdinal(d3.schemeCategory10);
var pie = d3.pie().sort(null);
var arc = d3.arc();
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");
var gs = svg.selectAll("g").data(d3.values(dataset)).enter().append("g");
var path = gs.selectAll("path")
.data(function(d, i) {
return pie(d).map(function(item) {
return { data: item, parentIndex: i };
});
})
.enter().append("path")
.attr("fill", function(d, i) { return color(i); })
.attr("d", function(d, i) {
return arc
.innerRadius(10+cwidth*d.parentIndex)
.outerRadius(cwidth*(d.parentIndex+1))(d.data);
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.12.0/d3.min.js"></script>
Related
I am learning this v3 example code and it works fine, but after modify it for v6, I got it running but the second time move the same circle coordinate will not match the mouse position any more!
console.clear()
console.log("======")
//connect_arrow()
drag_move()
function drag_move() {
var w = 600,
h = 400,
r = 25;
var data = [{x: 50, y: 50}, {x: 150, y: 150} ];
var drag = d3.drag();
//drag.subject(Object)
drag.on('start', dragstart)
drag.on('end', dragend)
drag.on('drag', dragmove)
var svg = d3.select('body')
.append('svg')
.attr('width', w)
.attr('height', h)
.style('border','2px solid');
d3.select("body")
.append("div")
.attr("id", "circle_id")
.text("Circle ID: ");
d3.select("body")
.append("div")
.attr("id", "x_var")
.text("X: ");
d3.select("body")
.append("div")
.attr("id", "y_var")
.text("Y: ");
d3.select("#circle_id")
.append("span")
.attr("id", "circle_id_text");
d3.select("#x_var")
.append("span")
.attr("id", "x_coord");
d3.select("#y_var")
.append("span")
.attr("id", "y_coord");
svg.selectAll('circle')
.data(data)
.enter()
.append('circle')
.attr('r', r)
.attr('cx', (d)=> d.x)
.attr('cy', (d)=> d.y)
.attr('id', (d,i)=> ("circle_" + i))
.call(drag);
// Functions
var circle;
var circleID;
function dragstart(d){
circle = d3.select(this);
if (circle.attr('id') === 'circle_0'){
circle.style('fill','red');
} else {
circle.style('fill', 'blue');
}
}
function dragend(d){
d3.select('#circle_id_text')
.text(null);
d3.select('#x_coord')
.text(null);
d3.select('#y_coord')
.text(null);
d3.select(this)
.style('fill','black');
}
function dragmove(d,event){
circle = d3.select(this);
circleID = circle.attr('id');
d.x = Math.max(r, Math.min(w-r, event.x))
d.y = Math.max(r, Math.min(h-r, event.y))
d3.select('#circle_id_text').text(circleID);
d3.select('#x_coord').text(d.x);
d3.select('#y_coord').text(d.y);
circle.attr('cx',d.x).attr('cy',d.y)
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/6.7.0/d3.min.js"></script>
dragmove parameter should be (event,d), not (d,event)!
console.clear()
console.log("======")
//connect_arrow()
drag_move()
function drag_move() {
var w = 600,
h = 400,
r = 25;
var data = [{x: 50, y: 50}, {x: 150, y: 150} ];
var drag = d3.drag();
//drag.subject(Object)
drag.on('start', dragstart)
drag.on('end', dragend)
drag.on('drag', dragmove)
var svg = d3.select('body')
.append('svg')
.attr('width', w)
.attr('height', h)
.style('border','2px solid');
d3.select("body")
.append("div")
.attr("id", "circle_id")
.text("Circle ID: ");
d3.select("body")
.append("div")
.attr("id", "x_var")
.text("X: ");
d3.select("body")
.append("div")
.attr("id", "y_var")
.text("Y: ");
d3.select("#circle_id")
.append("span")
.attr("id", "circle_id_text");
d3.select("#x_var")
.append("span")
.attr("id", "x_coord");
d3.select("#y_var")
.append("span")
.attr("id", "y_coord");
svg.selectAll('circle')
.data(data)
.enter()
.append('circle')
.attr('r', r)
.attr('cx', (d)=> d.x)
.attr('cy', (d)=> d.y)
.attr('id', (d,i)=> ("circle_" + i))
.call(drag);
// Functions
var circle;
var circleID;
function dragstart(d){
circle = d3.select(this);
if (circle.attr('id') === 'circle_0'){
circle.style('fill','red');
} else {
circle.style('fill', 'blue');
}
}
function dragend(d){
d3.select('#circle_id_text')
.text(null);
d3.select('#x_coord')
.text(null);
d3.select('#y_coord')
.text(null);
d3.select(this)
.style('fill','black');
}
function dragmove(event,d){
circle = d3.select(this);
circleID = circle.attr('id');
d.x = Math.max(r, Math.min(w-r, event.x))
d.y = Math.max(r, Math.min(h-r, event.y))
d3.select('#circle_id_text').text(circleID);
d3.select('#x_coord').text(d.x);
d3.select('#y_coord').text(d.y);
circle.attr('cx',d.x).attr('cy',d.y)
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/6.7.0/d3.min.js"></script>
I am attempting to animate 3 circles along 3 paths, these paths are randomly generated.Only two paths are generating and only one circle is animating along its allocated path. I have tried pairing the dataset and circle, this has no effect.
One of the paths seems to be blending two dataset's to generate a monster path. How do i stop this? How do i get each circle to its allocated path?
There is probably a more elegant way to do this.
var w = 2000, h = 2000;
var dataset1 = [] ;
for (var i = 0; i < 5; i++) {
var x = Math.floor((Math.random()*900)+1);
var y = Math.floor((Math.random()*900)+1);
dataset1.push({"x":x, "y":y});
};
var dataset2 = [] ;
for (var i = 0; i < 4; i++) {
var x = Math.floor((Math.random()*700)+1);
var y = Math.floor((Math.random()*600)+1);
dataset2.push({"x":x, "y":y});
};
var dataset3 = [] ;
for (var i = 0; i < 3; i++) {
var x = Math.floor((Math.random()*800)+1);
var y = Math.floor((Math.random()*400)+1);
dataset2.push({"x":x, "y":y});
};
var lineFunction = d3.svg.line()
.x(function(d) { return d.x; })
.y(function(d) { return d.y; })
.interpolate ("cardinal-closed")
.tension(0)
;
var svg = d3.select("body").append("svg")
.attr("width", w)
.attr("height", h)
;
var path1 = svg.append("path")
.datum( dataset1 )
.attr("d", lineFunction)
.attr("stroke", "black")
.attr("stroke-width", 3)
.attr("fill", "none")
;
var circle1 = svg.append("circle")
.attr("r", 130)
.attr("transform", "translate(" + [0] + ")")
;
var path2 = svg.append("path")
.datum( dataset2 )
.attr("d", lineFunction)
.attr("stroke", "black")
.attr("stroke-width", 3)
.attr("fill", "none")
;
var circle2 = svg.append("circle")
.attr("r", 30)
.attr("transform", "translate(" + [0] + ")")
;
var path3 = svg.append("path")
.datum( dataset2 )
.attr("d", lineFunction)
.attr("stroke", "black")
.attr("stroke-width", 3)
.attr("fill", "none")
;
var circle3 = svg.append("circle")
.attr("r", 10)
.attr("transform", "translate(" + [0] + ")")
;
transition();
function transition() {
circle1.transition()
.duration(10000)
.attrTween("transform", translateAlong(path1.node()))
.each("end", transition);
}
transition();
function transition() {
circle2.transition()
.duration(10000)
.attrTween("transform", translateAlong(path2.node()))
}
transition();
function transition() {
circle3.transition()
.duration(10000)
.attrTween("transform", translateAlong(path3.node()))
}
function translateAlong(path) {
var l = path.getTotalLength();
return function(d, i, a) {
return function(t) {
var p = path.getPointAtLength(t * l);
return "translate(" + p.x + "," + p.y + ")";
};
};
}
function translateAlong(path) {
var l = path.getTotalLength();
return function(d, i, a) {
return function(t) {
var p = path.getPointAtLength(t * l);
return "translate(" + p.x + "," + p.y + ")";
};
};
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<!DOCTYPE html>
<meta charset="utf-8">
<body>
<style>
path {
fill: none;
stroke: red;
stroke-width: 3px;
}
circle {
fill: red;
stroke: #fff;
stroke-width: 3px;
opacity: 0.7;
}
</style>
<script src="https://d3js.org/d3.v3.min.js"></script>
You had 3 problems with your code:
You were pushing dataset3 values into dataset2 array.
You were using dataset2 array for painting path3 element.
This is the most important problem: you had 3 functions with the same name. The last one simply overwrites the other ones. They should have different names. Alternatively, for DRY, write a general function and pass both the circle and the path as arguments.
Here is your code with those changes.
var w = 2000,
h = 2000;
var dataset1 = [];
for (var i = 0; i < 5; i++) {
var x = Math.floor((Math.random() * 900) + 1);
var y = Math.floor((Math.random() * 900) + 1);
dataset1.push({
"x": x,
"y": y
});
};
var dataset2 = [];
for (var i = 0; i < 4; i++) {
var x = Math.floor((Math.random() * 700) + 1);
var y = Math.floor((Math.random() * 600) + 1);
dataset2.push({
"x": x,
"y": y
});
};
var dataset3 = [];
for (var i = 0; i < 3; i++) {
var x = Math.floor((Math.random() * 800) + 1);
var y = Math.floor((Math.random() * 400) + 1);
dataset3.push({
"x": x,
"y": y
});
};
var lineFunction = d3.svg.line()
.x(function(d) {
return d.x;
})
.y(function(d) {
return d.y;
})
.interpolate("cardinal-closed")
.tension(0);
var svg = d3.select("body").append("svg")
.attr("width", w)
.attr("height", h);
var path1 = svg.append("path")
.datum(dataset1)
.attr("d", lineFunction)
.attr("stroke", "black")
.attr("stroke-width", 3)
.attr("fill", "none");
var circle1 = svg.append("circle")
.attr("r", 130)
.attr("transform", "translate(" + [0] + ")");
var path2 = svg.append("path")
.datum(dataset2)
.attr("d", lineFunction)
.attr("stroke", "black")
.attr("stroke-width", 3)
.attr("fill", "none");
var circle2 = svg.append("circle")
.attr("r", 30)
.attr("transform", "translate(" + [0] + ")");
var path3 = svg.append("path")
.datum(dataset3)
.attr("d", lineFunction)
.attr("stroke", "black")
.attr("stroke-width", 3)
.attr("fill", "none");
var circle3 = svg.append("circle")
.attr("r", 10)
.attr("fill", "blue")
.attr("transform", "translate(" + [0] + ")");
transition();
function transition() {
circle1.transition()
.duration(10000)
.attrTween("transform", translateAlong(path1.node()))
.each("end", transition);
}
transition2();
function transition2() {
circle2.transition()
.duration(10000)
.attrTween("transform", translateAlong(path2.node()))
.each("end", transition2);
}
transition3();
function transition3() {
circle3.transition()
.duration(10000)
.attrTween("transform", translateAlong(path3.node()))
.each("end", transition3);
}
function translateAlong(path) {
var l = path.getTotalLength();
return function(d, i, a) {
return function(t) {
var p = path.getPointAtLength(t * l);
return "translate(" + p.x + "," + p.y + ")";
};
};
}
function translateAlong(path) {
var l = path.getTotalLength();
return function(d, i, a) {
return function(t) {
var p = path.getPointAtLength(t * l);
return "translate(" + p.x + "," + p.y + ")";
};
};
}
path {
fill: none;
stroke: red;
stroke-width: 3px;
}
circle {
fill: red;
stroke: #fff;
stroke-width: 3px;
opacity: 0.7;
}
<script src="https://d3js.org/d3.v3.min.js"></script>
First and foremost, you are likely having a problem with the code because you are redefining the same function. You have 3 different definitions for the transition() function (one for each of the circles). Once that code is fixed, then we can move on to the next problem.
I am trying to update d3.js based graph with some random data after each 3 seconds, but it only renders the data first time. Here is the code
function drawGraph(data) {
var areaWidth = 200;
var areaHeight = 200;
var gdpData = data;
var leftMargin = 10;
var rightMargin = 10;
var maxGDP = Math.max.apply(Math, gdpData);
var myScale = d3.scaleLinear()
.domain([0, maxGDP])
.range([0, areaWidth - leftMargin - rightMargin]);
var spacing = areaHeight/(gdpData.length+3);
var fda = d3.select('#fun-drawing-area');
var recs = fda.selectAll('rect')
.data(gdpData);
recs.exit().remove();
recs.enter()
.append('rect')
.attr('y', function(d, i) {
return spacing * (i+1);
})
.attr('x', '10')
.attr('width', function(d, i) {
return myScale(d);
})
.attr('height', '20')
.style({
fill: 'steelblue',
stroke: 'black',
'stroke-width': 1
});
}
setInterval(function() {
var arr = [];
for (var i=0, t=5; i<t; i++) {
arr.push(Math.round(Math.random() * t))
}
console.log(arr);
drawGraph(arr);
}, 3000);
here is the plunkr https://plnkr.co/edit/OfFABbrZ6Teet6atLQD0?p=preview
In the new D3 4.x, you have to merge the selections:
recs.enter()
.append('rect')
.merge(recs)
.attr('y', function(d, i) {
return spacing * (i+1);
})
.attr('x', '10')
.attr('width', function(d, i) {
return myScale(d);
})
.attr('height', '20')
.style({
fill: 'steelblue',
stroke: 'black',
'stroke-width': 1
});
Here's your plunker: https://plnkr.co/edit/kaFCLcUKNqSiJkpOCHvS?p=preview
PS: I changed setTimeout to setInterval.
I have a requirement from my client to create multiple arcs. i find a solution for that. But still i don't know how to add the animation to the pies.
any one help me?
here is the code :
var dataset = {
apples: [53245, 28479, 19697, 24037, 40245],
oranges: [53245, 28479, 19697, 24037, 40245],
lemons: [53245, 28479, 19697, 24037, 40245],
pears: [53245, 28479, 19697, 24037, 40245],
pineapples: [53245, 28479, 19697, 24037, 40245],
};
var width = 460,
height = 300,
cwidth = 25;
var color = d3.scale.category20();
var pie = d3.layout.pie()
.sort(null);
var arc = d3.svg.arc();
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");
var gs = svg.selectAll("g").data(d3.values(dataset)).enter().append("g");
var path = gs.selectAll("path")
.data(function(d) { return pie(d); })
.enter().append("path")
.attr("fill", function(d, i) { return color(i); })
.attr("d", function(d, i, j) { return arc.innerRadius(10+cwidth*j).outerRadius(cwidth*(j+1))(d); });
Live Demo
But how do add the animations to each of the pies?
You are overwriting the arc innerRadius and outerRadius every time you do this:
.attr("d", function(d, i, j) { return arc.innerRadius(10+cwidth*j).outerRadius(cwidth*(j+1))(d); })
So, when you call tweenPie at the end, it's using whatever final state your arc is in. You can see this more clearly by adding a delay before the transition
.transition()
.delay(1000) // add this
.duration(3000)
.attrTween('d', tweenPie);
So, either define a different arc for each row of values or reset the arc inner/outer radius in your tweenPie call to match whichever row of data you're drawing.
I have three donut charts
var data = [
{
institution: [53245, 28479, 19697, 24037, 40245]
},
{
institution: [45, 9, 127, 37, 11]
},
{
institution: [3245, 88479, 45697, 1037, 77245]
}
]
var width = 100,
height = 100,
radius = Math.min(width, height) / 2;
var color = d3.scale.category20();
for (index = 0; index < data.length; ++index) {
var pie = d3.layout.pie()
.sort(null);
var arc = d3.svg.arc()
.innerRadius(radius - 30)
.outerRadius(radius - 5);
var svg = d3.select("body").append("svg:svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");
var path = svg.selectAll("path")
.data(pie(data[index].institution))
.enter().append("path")
.attr("fill", function(d, i) { return color(i); })
.attr("d", arc);
}
For each segment in each chart I need a short line and a label with the value, for example here
Is there any simple solution for this?
Here my solution:
var data = [
{
institution: [53245, 28479, 19697, 24037, 40245]
},
{
institution: [45, 9, 127, 37, 11]
},
{
institution: [3245, 88479, 45697, 1037, 77245]
}
];
var width = 200;
var height = 200;
var radius = Math.min(width, height) / 4;
var r = radius;
var textOffset=5;
var colours = ['#ffc400','#e53517','#7ab51d','#009fda','#c2c2c2'];
for (index = 0; index < data.length; ++index) {
var pie = d3.layout.pie().sort(null);
var arc = d3.svg.arc().innerRadius(radius - 30).outerRadius(radius - 5);
var svg = d3.select("body").append("svg:svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");
var piedata=pie(data[index].institution);
var ticks = svg.selectAll("line").data(piedata).enter().append("line");
ticks.attr("x1", 0)
.attr("x2", 0)
.attr("y1", -radius+4)
.attr("y2", -radius-2)
.attr("stroke", "gray")
.attr("transform", function(d) {
return "rotate(" + (d.startAngle+d.endAngle)/2 * (180/Math.PI) + ")";
});
var labels = svg.selectAll("text").data(piedata).enter().append("text");
labels.attr("class", "value")
.attr("transform", function(d) {
var dist=radius+15;
var winkel=(d.startAngle+d.endAngle)/2;
var x=dist*Math.sin(winkel);
var y=-dist*Math.cos(winkel);
return "translate(" + x + "," + y + ")";
})
.attr("dy", "0.35em")
.attr("text-anchor", "middle")
.text(function(d){
return d.value;
});
var path = svg.selectAll("path")
.data(piedata)
.enter().append("path")
.attr("fill", function(d, i) { return colours[i]; })
.attr("d", arc);
}