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.
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'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>
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);
}
I need help adding a second series of data to my bar chart. Currently I'm populating the bars from the glob key in my dataset. This will be the first series. The second series I would like to be is local.
How do I go about adding that?
Play with my JSFiddle here.
var w = 300;
var h = 200;
var colors = ["#377EB8", "#4DAF4A"];
var dataset = [
{"keyword": "payday loans", "glob": 1500000, "local": 673000, "cpc": "14.11"},
{"keyword": "title loans", "glob": 165000, "local": 165000, "cpc": "12.53" },
{"keyword": "personal loans", "glob": 550000, "local": 301000, "cpc": "6.14"}
];
var data = [[1500000, 165000, 550000],
[673000, 165000, 301000]];
var tdata = d3.transpose(dataset.glob, dataset.local);
var series = 2; // Global & Local
var x0Scale = d3.scale.ordinal()
.domain(d3.range(dataset.length))
.rangeRoundBands([0, w], 0.05);
var x1Scale = d3.scale.ordinal()
.domain(d3.range(dataset.length))
.rangeRoundBands([0, w], 0.05);
var yScale = d3.scale.linear()
.domain([0, d3.max(dataset, function(d) {return d.glob;})])
.range([0, h]);
var glob = function(d) {
return d.glob;
};
var cpc = function(d) {
return d.cpc;
};
var commaFormat = d3.format(',');
//SVG element
var svg = d3.select("#searchVolume")
.append("svg")
.attr("width", w)
.attr("height", h);
// Graph Bars
svg.selectAll("rect")
.data(dataset, cpc, glob)
.enter()
.append("rect")
.attr("x", function(d, i){
return x0Scale(i);
})
.attr("y", function(d) {
return h - yScale(d.glob);
})
.attr("width", x0Scale.rangeBand())
.attr("height", function(d) {
return yScale(d.glob);
})
.attr("fill", colors[1])
.on("mouseover", function(d) {
//Get this bar's x/y values, then augment for the tooltip
var xPosition = parseFloat(d3.select(this).attr("x")) + x0Scale.rangeBand() / 3;
var yPosition = parseFloat(d3.select(this).attr("y")) + yScale;
//Update Tooltip Position & value
d3.select("#tooltip")
.style("left", xPosition + "px")
.style("top", yPosition + "px")
.select("#cpcVal")
.text(d.cpc);
d3.select("#tooltip").classed("hidden", false);
})
.on("mouseout", function() {
//Remove the tooltip
d3.select("#tooltip").classed("hidden", true);
});
//Create labels
svg.selectAll("text")
.data(dataset, glob)
.enter()
.append("text")
.text(function(d) {
return commaFormat(d.glob);
})
.attr("text-anchor", "middle")
.attr("x", function(d, i) {
return x0Scale(i) + x0Scale.rangeBand() / 2;
})
.attr("y", function(d) {
return h - yScale(d.glob) + 14;
})
.attr("font-family", "sans-serif")
.attr("font-size", "11px")
.attr("fill", "white");
It's easiest to create an svg group (<g>) element for each set of data, and then add the individual parts to each group. For example, roughly:
http://jsfiddle.net/WXMwv/1/
var sets = svg.selectAll(".set")
.data(dataset)
.enter().append("g")
.attr("class","set")
.attr("transform",function(d,i){
return "translate(" + x0Scale(i) + ",0)"
})
.on("mouseover", function(d,i) {
//Create x value from data index
var xPosition = parseFloat(x0Scale(i) + x0Scale.rangeBand() / 6);
var yPosition = 0;
//Update Tooltip Position & value
d3.select("#tooltip")
.style("left", xPosition + "px")
.style("top", yPosition + "px")
.select("#cpcVal")
.text(d.cpc);
d3.select("#tooltip").classed("hidden", false);
})
.on("mouseout", function() {
//Remove the tooltip
d3.select("#tooltip").classed("hidden", true);
});
sets.append("rect")
.attr("class","glob")
.attr("width", x0Scale.rangeBand()/2)
.attr("y", function(d) {
return yScale(d.glob);
})
.attr("height", function(d){
return h - yScale(d.glob);
})
.attr("fill", colors[1])
sets.append("rect")
.attr("class","local")
.attr("width", x0Scale.rangeBand()/2)
.attr("y", function(d) {
return yScale(d.local);
})
.attr("x", x0Scale.rangeBand()/2)
.attr("height", function(d){
return h - yScale(d.local);
})
.attr("fill", colors[0])
The text elements are left as an exercise for the reader :)
I'm generating voronoi paths based on some points in forced layout. I'd like to randomly assign these paths 1 of 10 classes and then wrap some of these classes with a clipPath that I can then apply to another element.
Is it possible to wrap svg tags around elements using d3 as opposed to appending?
Or is it even possible to use multiple paths generated by d3 in a clipPath?
Thank you for your help,
w = $(window).width();
h = $(window).height();
function ranNum(){
return Math.floor((Math.random()*10)+1);
}
$('#grid').css('height', h);
var vertices = d3.range(50).map(function(d) { return {x: d.x, y: d.y}; });
//console.log(vertices);
links = [];
voronoiVertices = [];
var force = d3.layout.force()
.nodes(vertices)
.size([w, h])
.linkDistance(60)
.charge(-900)
.on("tick", tick)
.start();
var svg = d3.select("body").append("svg:svg")
.attr("width", w)
.attr("height", h);
//path gradient
var defs = svg.append('defs')
var radialGradient = defs.append('radialGradient')
.attr('id', 'pathGrad')
.attr('cx', '50%')
.attr('cy', '50%')
.attr('r', '50%')
.attr('fx', '50%')
.attr('fy', '50%');
var stop1 = radialGradient.append('stop')
.attr('offset', '.2')
.attr('stop-color', '#a8a8a8');
var stop2 = radialGradient.append('stop')
.attr('offset', '1')
.attr('stop-color', '#0000000');
//path dropShadow
var filterShadow = defs.append('filter')
.attr('id', 'pathShadow')
.attr('height', '130%');
var gCir = svg.append('g')
.attr("class", "gCircle");
var gPath = svg.append('g')
.attr("class", "gPath");
var circle = svg.selectAll("circle");
var path = gPath.selectAll("path")
.data(d3.geom.voronoi(vertices))
.enter().append("path")
.attr("fill", "url(#pathGrad)");
//wraps path with random class after generation
$('path').each(function(){$(this).attr('class', 'path-' + Math.floor((Math.random()*10)+1))});
var clip = defs.append("svg:clipPath")
.attr("id", "clip")
.append("svg:rect")
.attr("id", "clip-rect")
.attr("x", "0")
.attr("y", "0")
.attr("width", '900px')
.attr("height", '900px');
var gClip = svg.append("svg:g")
.attr('clip-path', 'url(#clip)');
/*
var clip = gClip.append("svg:image")
.attr("class", "circle")
.attr("xlink:href", "clip.jpg")
.attr("x", "0px")
.attr("y", "0px")
.attr("width", w)
.attr("height", h);
*/
var selectPath = d3.selectAll('.path-10');
console.log(selectPath);
function tick() {
voronoiVertices = vertices.map(function(t){return [t.x, t.y]})
path = path.data(d3.geom.voronoi(voronoiVertices))
path.enter().append("path")
.attr("d", function(t) { return "M" + t.join("L") + "Z"; });
path.attr("fill", "url(#pathGrad)")
.attr("d", function(t) { return "M" + t.join("L") + "Z"; });
circle = circle.data(vertices)
circle.enter().append("circle")
.call(force.drag)
.attr("r", 0)
.attr('class', function(d) { return d.index; })
.attr("cx", function(d) { return d.x; })
.attr("cy", function(d) { return d.y; })
.transition().duration(5000).attr("r", 5);
circle.attr("cx", function(d) { return d.x; })
.attr("cy", function(d) { return d.y; });
}
});
There is a strange mix of jQuery and D3 in this. Try to do the things in D3 when you work with it. For example I'd rather do this:
.attr("class", function(d){return 'path-'+Math.floor((Math.random()*10)+1))});
than this:
$('path').each(function(){$(this).attr('class', 'path-' + Math.floor((Math.random()*10)+1))});
d3 has an exotic but smart way of doing things, it's better to learn this update pattern well before doing something serious.
And here is the working code:
w = 1200;
h = 500;
function ranNum(){
return Math.floor((Math.random()*10)+1);
}
var vertices = d3.range(50).map(function(d) { return {x: d.x, y: d.y}; });
//console.log(vertices);
links = [];
voronoiVertices = [];
var force = d3.layout.force()
.nodes(vertices)
.size([w, h])
.linkDistance(60)
.charge(-900)
.on("tick", tick)
.start();
var svg = d3.select("body").append("svg")
.attr("width", w)
.attr("height", h);
//path gradient
var defs = svg.append('defs')
var radialGradient = defs.append('radialGradient')
.attr('id', 'pathGrad')
.attr('cx', '50%')
.attr('cy', '50%')
.attr('r', '50%')
.attr('fx', '50%')
.attr('fy', '50%');
var stop1 = radialGradient.append('stop')
.attr('offset', '.2')
.attr('stop-color', '#a8a8a8');
var stop2 = radialGradient.append('stop')
.attr('offset', '1')
.attr('stop-color', '#0000000');
//path dropShadow
var filterShadow = defs.append('filter')
.attr('id', 'pathShadow')
.attr('height', '130%');
var gCir = svg.append('g')
.attr("class", "gCircle");
var gPath = svg.append('g')
.attr("class", "gPath");
var circle = svg.selectAll("circle");
var path = gPath.selectAll("path")
.data(d3.geom.voronoi(vertices))
.enter().append("path")
.attr("fill", "url(#pathGrad)");
//wraps path with random class after generation
//$('path').each(function(){$(this).attr('class', 'path-' + Math.floor((Math.random()*10)+1))});
var clip = defs.append("svg:clipPath")
.attr("id", "clip")
.append("svg:rect")
.attr("id", "clip-rect")
.attr("x", "0")
.attr("y", "0")
.attr("width", '900px')
.attr("height", '900px');
var gClip = svg.append("svg:g")
.attr('clip-path', 'url(#clip)');
function tick() {
voronoiVertices = vertices.map(function(t){return [t.x, t.y]})
path = path.data(d3.geom.voronoi(voronoiVertices))
path.enter().append("path")
.attr("d", function(t) { return "M" + t.join("L") + "Z"; });
path.attr("fill", "url(#pathGrad)")
.attr("d", function(t) { return "M" + t.join("L") + "Z"; });
circle = circle.data(vertices)
circle.enter().append("circle")
.call(force.drag)
.attr("r", 0)
.attr('class', function(d) { return d.index; })
.attr("cx", function(d) { return d.x; })
.attr("cy", function(d) { return d.y; })
.transition().duration(5000).attr("r", 5);
circle.attr("cx", function(d) { return d.x; })
.attr("cy", function(d) { return d.y; });
}
Good luck!