I've been messing around, trying to figure out why this outline is appearing while the donut chart draws, and then disappears once it's finished. It's a light grey outline that's that sort of appears while the interpolating/bounce affect take place Here's the code and fiddle. Thanks for any help.
var data = [
{name: "Yo", value: 5500},
{name: "Dawg", value: 3800},
{name: "Dis", value: 2500},
{name: "Pie", value: 2000},
{name: "Doe", value: 1500},
{name: "Yo", value: 5000}
];
var margin = {top: 40, right: 20, bottom: 20, left: 20};
width = 400 - margin.left - margin.right;
height = width - margin.top - margin.bottom;
var chart = d3.select("body")
.append('svg')
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + ((width/2)+margin.left) + "," + ((height/2)+margin.top) + ")");
var radius = Math.min(width, height) / 2;
var color = d3.scale.ordinal()
.range(['red', 'blue', 'yellow', 'orange', 'green', '#ffffff']);
var arc = d3.svg.arc()
.outerRadius(radius)
.innerRadius(radius - 20);
var myScale = d3.scale.linear().domain([0, 360]).range([0, 2 * Math.PI]);
var pie = d3.layout.pie()
.sort(null)
.startAngle(myScale(45))
.endAngle(myScale(405))
.value(function(d) { return d.value; });
var g = chart.selectAll(".arc")
.data(pie(data))
.enter().append("g")
.attr("class", "arc");
g.append("path").attr("fill", function(d, i) { return color(i); })
// .attr("d", arc)
.transition()
.ease("bounce")
.duration(2000)
.attrTween("d", tweenPie);
function tweenPie(b) {
var i = d3.interpolate({startAngle: myScale(45), endAngle: myScale(405)}, b);
return function(t) { return arc(i(t)); };
}
And here's the fiddle:
http://jsfiddle.net/connorsan/SdN2F/18/
in your tweenPie-function, you have to set the startAngle and the endAngle to the same value so the transition starts from one point - otherwise it draws another arc that creates the mentioned effect. try
function tweenPie(b) {
var i = d3.interpolate({startAngle: myScale(45), endAngle: myScale(45)}, b);
return function(t) { return arc(i(t)); };
fiddle: http://jsfiddle.net/SdN2F/22/
Related
The labels on my pie chart are not visible, whatever I try (resetting radius, changing text color). The pie chart itself is visible. I have looked at many examples, such as https://bl.ocks.org/santi698/f3685ca8a1a7f5be1967f39f367437c0, but to no avail. There must be something simple that I'm missing, probably in the centroid function. Any help would be appreciated! Code is as follows. It is part of a jinja template, but I guess that's not relevant. Obviously the arcs.append("text") statement is the one that has a mistake somewhere.
<svg width="960" height="500">
</svg>
<script>
var svg = d3.select("svg");
var margin = 50,
width = +svg.attr("width") - margin,
height = +svg.attr("height") - margin,
radius = height / 2;
var g = svg.append("g")
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");
data = [{fruit: "apple", amount: 3},
{fruit: "pear", amount: 2},
{fruit: "kiwi", amount: 5}];
console.log(data);
var colors = ['green', 'red', 'blue'];
var arc = d3.arc()
.outerRadius(radius - 10)
.innerRadius(0);
var labelArc = d3.arc()
.outerRadius(radius - 40)
.innerRadius(radius - 40);
var pie = d3.pie()
.value(function(d) { return d.amount; });
console.log(pie(data))
var arcs = g.selectAll("arc")
.data(pie(data))
.enter()
.append("g")
.attr("class", "arc");
arcs.append("path")
.attr("d", arc)
.style('fill', function(d, i) {
return colors[i];
});
arcs.append("text")
.attr("transform", function(d) {
return "translate(" + labelArc.centroid(d) + ")";
})
.text(function(d) {
return d.fruit;
});
</script>
D3 pie generator returns an array of objects with several created properties, among them:
data - the input datum; the corresponding element in the input data array.
Therefore, it should be:
.text(function(d) {
return d.data.fruit;
});
Here is your code with that change:
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.13.0/d3.min.js"></script>
<svg width="960" height="500">
</svg>
<script>
var svg = d3.select("svg");
var margin = 50,
width = +svg.attr("width") - margin,
height = +svg.attr("height") - margin,
radius = height / 2;
var g = svg.append("g")
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");
data = [{
fruit: "apple",
amount: 3
},
{
fruit: "pear",
amount: 2
},
{
fruit: "kiwi",
amount: 5
}
];
var colors = ['green', 'red', 'blue'];
var arc = d3.arc()
.outerRadius(radius - 10)
.innerRadius(0);
var labelArc = d3.arc()
.outerRadius(radius - 40)
.innerRadius(radius - 40);
var pie = d3.pie()
.value(function(d) {
return d.amount;
});
var arcs = g.selectAll("arc")
.data(pie(data))
.enter()
.append("g")
.attr("class", "arc");
arcs.append("path")
.attr("d", arc)
.style('fill', function(d, i) {
return colors[i];
});
arcs.append("text")
.attr("transform", function(d) {
return "translate(" + labelArc.centroid(d) + ")";
})
.text(function(d) {
return d.data.fruit;
});
</script>
I'm looking to create non-circular arcs using SVG in HTML using d3js v3. My problem is that the non-circular arcs I'm able to create are actually circular arcs transformed. As a result, the arc's stroke width isn't uniform and looks awkward. Here is a jsFiddle example:
var vis = d3.select("body").append("svg")
vis.attr("width", "100%")
.attr("height", "100%")
.append("path")
.attr("d", d3.svg.arc()
.innerRadius(500)
.outerRadius(500)
.startAngle(-90 * (Math.PI / 180))
.endAngle(90 * (Math.PI / 180)))
.attr("stroke", "black")
.attr("transform", "translate(510,170) scale(1,0.3)")
.attr("stroke-width", 14);
Here is an example of the result:
Here is what the arc should look like (drawn in Viso):
Does anyone know of a way to create SVG arc's that are non-circular (meaning the radius changes)?
You can create a simple line chart and use d3.curveBasis or any other interpolation you like. Play with the data to get the desired result.
Here's the snippet:
var margin = {
top: 30,
right: 20,
bottom: 30,
left: 50
};
var width = 550 - margin.left - margin.right;
var height = 150 - margin.top - margin.bottom;
var x = d3.scaleLinear().range([0, width]);
var y = d3.scaleLinear().range([height, 0]);
var valueline = d3.line()
.x(function(d) {
return x(d.x);
})
.y(function(d) {
return y(d.y);
}).curve(d3.curveBasis);
var svg = d3.select("body")
.append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var data = [{
x: 1,
y: "0"
}, {
x: 1.05,
y: "0.6"
}, {
x: 3,
y: "1.3"
}, {
x: 4.95,
y: "0.6"
}, {
x: 5,
y: "0"
}];
data.forEach(function(d) {
d.x = d.x
d.y = +d.y;
});
x.domain(d3.extent(data, function(d) {
return d.x;
}));
y.domain([0, d3.max(data, function(d) {
return d.y;
})]);
svg.append("path")
.attr("d", valueline(data));
path {
stroke: black;
stroke-width: 8;
fill: none;
stroke-linecap: round;
shape-rendering: geometricprecision;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.13.0/d3.min.js"></script>
I am working on a d3 applicaton - with a pie chart -- I would like to get animation onload and on a call to action. Like when the chart becomes visible during a scroll.
Where the pie segments grow around the central pivot. So tween or snap to the other segment like a relay race
http://jsfiddle.net/pg886/192/
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://d3js.org/d3.v3.min.js"></script>
<div class="piechart" data-role="piechart" data-width=400 data-height=400 data-radius=30 data-innerradius=20
data-data=x>
</div>
<style>
.piechart{
/*border: 1px solid black;*/
/*text-align: center;
font-size: 12px;*/
}
</style>
<script>
$( document ).ready(function() {
console.log("test")
var $this = $('.piechart');
var data = [{
"label": "Apples",
"value": 100
},
{
"label": "Pears",
"value": 120
},
{
"label": "Bananas",
"value": 20
}];
var w = $this.data("width");
var h = $this.data("height");
var ir = $this.data("innerradius");
var r = $this.data("radius");
function colores_google(n) {
var colores_g = ["#f7b363", "#448875", "#c12f39", "#2b2d39", "#f8dd2f"];
//var colores_g = ["#47abd5", "#005a70", "#f5a0a3", "#ff7276", "#a9a19c", "#d0743c", "#ff8c00"];
return colores_g[n % colores_g.length];
}
var radius = Math.min(w, h) / 4;
var arc = d3.svg.arc()
.outerRadius(radius - 10)
.innerRadius(0);
var labelArc = d3.svg.arc()
.outerRadius(radius - r)
.innerRadius(radius - ir);
var pie = d3.layout.pie()
.sort(null)
.value(function(d) { return d.value; });
var chart = d3.select('.piechart').append("svg")
.attr("class", "chart")
.attr("width", w)
.attr("height", h)
.attr("transform", "translate(0,0)");
var piechart = chart
.append("g")
.attr("class", "piechart")
.attr("width", (radius*2))
.attr("transform", "translate(0,"+h/4+")");
var path_group = piechart.append("g")
.attr("class", "path_group")
.attr("transform", "translate(90," + ((h / 4) - 20) + ")");
var padding = 45;
var legendPaddingTop = 30;
var legend = chart.append("g")
.attr("class", "legend")
.attr("width", w/2)
.attr("height", h)
.attr("transform", "translate(" + (w - 50) + "," + (h / 4) + ")");
var label_group = legend.append("svg:g")
.attr("class", "label_group")
.attr("transform", "translate(" + (-(w / 3) + 20) + "," + 0 + ")");
var legend_group = legend.append("svg:g")
.attr("class", "legend_group")
.attr("transform", "translate(" + (-(w / 3) - 100) + "," + 0 + ")");
var g = path_group.selectAll(".arc")
.data(pie(data))
.enter().append("g")
.attr("class", "arc");
g.append("path")
.attr("d", arc)
.style("fill", function(d, i) {
return colores_google(i);
});
var legendHeight = legendPaddingTop;
var ySpace = 18;
//draw labels
var labels = label_group.selectAll("text.labels")
.data(data);
labels.enter().append("svg:text")
.attr("class", "labels")
.attr("dy", function(d, i) {
legendHeight+=ySpace;
return (ySpace * i) + 4;
})
.attr("text-anchor", function(d) {
return "start";
})
.text(function(d) {
return d.label;
});
labels.exit().remove();
//draw labels
//draw legend
var legend = legend_group.selectAll("circle").data(data);
legend.enter().append("svg:circle")
.attr("cx", 100)
.attr("cy", function(d, i) {
return ySpace * i;
})
.attr("r", 7)
.attr("width", 18)
.attr("height", 18)
.style("fill", function(d, i) {
return colores_google(i);
});
legend.exit().remove();
//draw legend
//reset legend height
//console.log("optimum height for legend", legendHeight);
$this.find('.legend').attr("height", legendHeight);
function type(d) {
d.value = +d.value;
return d;
}
});
</script>
So you can achieve this pretty easily, and there are a couple of blocks that will help you.
Arc Tween Firstly this block gives you an example of how to tween an arc. Basically you can't get that automatically so you have to write your own attrTween function. Fortunately this is pretty simple and Mike Bostock gives a really good example in there.
Here's a code sample - but the link gives a really good verbose description of what's going on.
.attrTween("d", function(d) {
var interpolate = d3.interpolate(d.endAngle, newAngle);
return function(t) {
d.endAngle = interpolate(t);
return arc(d);
};
}
Next you want something like this Donut with transitions. This is actually the end-result for where you're trying to get to. This effect is really easy to achieve, all you need to do is set your angles correctly and the timings.
Angles: So here you want both the endAngle and the startAngle to be the same at the start (which should be the endAngle value of the previous segment or 0 for the first segment).
Timing: You want to allow 1 animation to complete before you start the next, simply by delaying them. You can see how that's done with this snippet:
.transition()
.delay(function(d,i) { return i * 500; })
.duration(500)
.attrTween(...)
const dataset = [
{ age: "<5", population: 2704659 },
{ age: "5-13", population: 4499890 },
{ age: "14-17", population: 2159981 },
{ age: "18-24", population: 3853788 },
{ age: "25-44", population: 14106543 },
{ age: "45-64", population: 8819342 },
{ age: "≥65", population: 612463 },
];
const TIME = 2000 / dataset.length;
const color = d3.scaleOrdinal(["#98abc5", "#8a89a6", "#7b6888", "#6b486b", "#a05d56", "#d0743c", "#ff8c00"]);
const pie = d3.pie()
.sort(null)
.value(function(d) { return d.population; });
const path = d3.arc()
.innerRadius(0)
.outerRadius(350);
d3.select("#container")
.selectAll(".arc")
.data(pie(dataset))
.enter()
.append("g")
.attr("class", "arc")
.append("path")
.attr("fill", function(d) { return color(d.data.age); })
.transition()
.duration(TIME)
.ease(d3.easeLinear)
.delay(function(d, i) { return i * TIME; })
.attrTween("d", function(d) {
// Note the 0.1 to prevent errors generating the path
const angleInterpolation = d3.interpolate(d.startAngle + 0.1, d.endAngle);
return function(t) {
d.endAngle = angleInterpolation(t);
return path(d);
}
});
<script src="https://d3js.org/d3.v4.min.js"></script>
<svg width="800" height="800">
<g id="container" transform="translate(400, 400)"></g>
</svg>
I know how I can use an array with d3.jsdonuts but I can't seem to understand how objects work with d3.js.
I'd like to output the following object:
Code
var width = 175,
height = 175,
radius = Math.min(width, height) / 2,
donutWidth = 23;
var color = d3.scaleOrdinal(d3.schemeCategory20);
var data = [{
user: 'Bob',
value: 100
},
{
user: 'Danny',
value: 200
}
];
function render(data) {
//data = [100,250];
var svg = d3.select("#pot").append("svg")
.attr("width", width)
.attr("height", height)
var svg_g = svg.append("g").attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");
var pie = d3.pie()
.sort(null);
var arc = d3.arc()
.innerRadius(radius - donutWidth)
.outerRadius(radius);
var path = svg_g.selectAll("path")
.data(pie(data));
var pathEnter = path.enter().append("path")
.attr("fill", function(d, i) {
return color(i);
})
.attr("d", arc)
var pathUpdate = path.attr("d", arc);
}
render(data);
JSFiddle
I'd like the values from the object to be rendered.
If you need to show the values in the donut you should add:
.value(function(d) { return d.value }) at var pie = d3.pie().
Something like this:
(function() {
var width = 175,
height = 175,
radius = Math.min(width, height) / 2,
donutWidth = 23;
var color = d3.scaleOrdinal(d3.schemeCategory20);
var data = [{
user: 'Bob',
value: 100
}, {
user: 'Danny',
value: 200
}, {
user: 'Test',
value: 50
}];
function render(data) {
var svg = d3.select("#pot").append("svg")
.attr("width", width)
.attr("height", height)
var svg_g = svg.append("g").attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");
var pie = d3.pie().value(function(d) {
return d.value
})
.sort(null);
var arc = d3.arc()
.innerRadius(radius - donutWidth)
.outerRadius(radius);
var path = svg_g.selectAll("path")
.data(pie(data));
var pathEnter = path.enter().append("path")
.attr("fill", function(d, i) {
return color(i);
})
.attr("d", arc)
var pathUpdate = path.attr("d", arc);
}
render(data);
})();
<script src="https://d3js.org/d3.v4.min.js"></script>
<div id="pot"></div>
I have been trying this for a couple of days but not able to find solution for it. I have a time series data which comes every 5 minutes and can extend for days. I am quiet new to D3 and Java Script. It took me some time to build it. Following is a link of the jsfiddle, i have been working on:
https://jsfiddle.net/adityap16/d61gtadm/2/
Code:
var data = [
{"mytime": "2015-12-01T11:10:00.000Z", "value": 64},
{"mytime": "2015-12-01T11:15:00.000Z", "value": 67},
{"mytime": "2015-12-01T11:20:00.000Z", "value": 70},
{"mytime": "2015-12-01T11:25:00.000Z", "value": 64},
{"mytime": "2015-12-01T11:30:00.000Z", "value": 72},
{"mytime": "2015-12-01T11:35:00.000Z", "value": 75},
{"mytime": "2015-12-01T11:40:00.000Z", "value": 71},
{"mytime": "2015-12-01T11:45:00.000Z", "value": 80}
];
var parseDate = d3.time.format("%Y-%m-%dT%H:%M:%S.%LZ").parse;
data.forEach(function(d) {
d.mytime = parseDate(d.mytime);
});
//var margin = { top: 30, right: 30, bottom: 40, left:50 },
var margin = { top: 30, right: 30, bottom: 40, left:50 },
height = 200,
width = 800;
var color = "green";
var xaxis_param = "mytime";
var yaxis_param = "value"
var params1 = {margin:margin,height:height,width:width, color: color, xaxis_param:xaxis_param, yaxis_param :yaxis_param};
draw_graph(data,params1);
function draw_graph(data,params){
//Get the margin
var xaxis_param = params.xaxis_param;
var yaxis_param = params.yaxis_param;
var color_code = params.color;
var margin = params.margin;
var height = params.height - margin.top - margin.bottom,
width = params.width - margin.left - margin.right;
console.log("1")
var x_extent = d3.extent(data, function(d){
return d[xaxis_param]});
console.log("2")
var y_extent = d3.extent(data, function(d){
return d[yaxis_param]});
var x_scale = d3.time.scale()
.domain(x_extent)
.range([0,width]);
console.log("3")
var y_scale = d3.scale.linear()
.domain([0,y_extent[1]])
.range([height,0]);
//Line
var lineGen = d3.svg.line()
.x(function (d) {
return x_scale(d[xaxis_param]);
})
.y(function (d) {
return y_scale(d[yaxis_param]);
});
var myChart = d3.select('body').append('svg')
.style('background', '#E7E0CB')
.attr('width', width + margin.left + margin.right)
.attr('height', height + margin.top + margin.bottom)
.append('g')
.attr('transform', 'translate('+ margin.left +', '+ margin.top +')');
myChart
.append('svg:path')
.datum(data)
.attr('class', 'line')
.attr("d",lineGen)
.attr('stroke', color_code)
.attr('stroke-width', 1)
.attr('fill', 'none');
var legend = myChart.append("g")
.attr("class", "legend")
.attr("transform", "translate(" + 5 + "," + (height - 25) + ")")
legend.append("rect")
.style("fill", color_code)
.attr("width", 20)
.attr("height", 20);
legend.append("text")
.text(yaxis_param)
.attr("x", 25)
.attr("y", 12);
var vGuideScale = d3.scale.linear()
.domain([0,y_extent[1]])
.range([height, 0])
var vAxis = d3.svg.axis()
.scale(vGuideScale)
.orient('left')
.ticks(5)
var hAxis = d3.svg.axis()
.scale(x_scale)
.orient('bottom')
.ticks(d3.time.minute, 5);
myChart.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(hAxis);
myChart.append("g")
.attr("class", "y axis")
.call(vAxis)
}
I am currently stumped with two problems.
a.)I have to place vertical markers for end of each day. How to place vertical lines to show these transition (markers) from one day to another (just a simple black vertical line would do?
b.) Can we deal with missing data suppose i don't get data for a day, can i get blanks for that period of time and straight away plot the next day data. (This one is not that important)?
Here is a working jsfiddle: https://jsfiddle.net/kmandov/d61gtadm/3/
This is how you can achieve the desired results:
Problem a.) Vertical ticks for each day:
First, create a new major axis and set the tickSize to -height, so the ticks go all the way through your chart:
var majorAxis = d3.svg.axis()
.scale(x_scale)
.orient('bottom')
.ticks(d3.time.day, 1)
.tickSize(-height);
and then create the corresponding svg element:
myChart.append("g")
.attr("class", "x axis major")
.attr("transform", "translate(0," + height + ")")
.call(majorAxis);
Then remove the label for the major ticks(you don't want them to overlap):
.axis.major text {
display: none;
}
Here is a nice example by M. Bostock himself: http://bl.ocks.org/mbostock/4349486
Problem B.) Missing Data
Set .defined() on the line:
var lineGen = d3.svg.line()
.defined(function(d) { return d[yaxis_param]; })
.x(function (d) {
return x_scale(d[xaxis_param]);
})
.y(function (d) {
return y_scale(d[yaxis_param]);
});
Here is an example on how to deal with gaps in your data: http://bl.ocks.org/mbostock/3035090