I'm kind of new to D3JS and I've made a 2 arc Donut (my data has only two values). I'm trying to figure out how to animate them so that the largest path get "self drawn" first and then the smallest one gets self drawn as well.
I know this can be achieved setting the start angle to 0 and then use the D3js attrTween() and play with the desired final angle. but since I can not declare the final angle by myself and it depends on the data, and due to my very little knowledge about djse I haven´t been able to achieve it.
This is my JS code:
var width = 100,
height = 100,
radius = (Math.min(width, height) / 2);
function drawDonut(dataa, divchart) {
var sym = "%"
var color = ["#00338D", "#BC204B"];
var pie = d3.pie()
.value(function(d) {
return d
})(dataa);
var arc = d3.arc()
.outerRadius(radius - 10)
.innerRadius(radius - (radius / 1.9));
var labelArc = d3.arc()
.outerRadius(radius - 31)
.innerRadius(radius - 31);
var svg = d3.select(divchart)
.append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + 50 + "," + 50 + ")");
var g = svg.selectAll("arc")
.data(pie)
.enter().append("g")
.attr("class", "arc");
g.append("path")
.attr("d", arc)
.data(color)
.style("fill", function(d) {
return d
});
g.append("g")
.attr("transform", "translate(-17,-17) scale(0.7)")
.html(myGroup);
}
Using the data from your previous post and attrTween as a reference, here's a snippet with the large-path-first animation applied to
var myGroup = '<g><path class="st0" d="M15.6,10.9c1.3,0,2.4-1.2,2.4-2.7c0-1.5-1.1-2.7-2.4-2.7c-1.3,0-2.4,1.2-2.4,2.7C13.2,9.7,14.2,10.9,15.6,10.9L15.6,10.9z"/><path class="st0" d="M18.6,11.6h-1.2l-1.8,5.5l-1.8-5.5h-1.2c-1.3,0-2.4,1.2-2.4,2.7v13h2.4l1.2,16.4h3.6l1.2-16.4H21v-13C21,12.8,19.9,11.6,18.6,11.6L18.6,11.6z"/><path class="st0" d="M31.9,10.9c1.3,0,2.4-1.2,2.4-2.7c0-1.5-1.1-2.7-2.4-2.7c-1.3,0-2.4,1.2-2.4,2.7C29.5,9.7,30.6,10.9,31.9,10.9L31.9,10.9z"/><path class="st0" d="M39.8,25.2l-3.6-11.6c0,0-0.6-2-2.4-2h-3.6c-1.8,0-2.4,2-2.4,2l-3.6,11.6l1.2,0.7l4.2-9.5l-3.6,14.3h3.6l1.2,13h2.4l1.2-13H38l-3.6-14.3l4.2,9.5L39.8,25.2L39.8,25.2z"/></g>';
var data1 = [4, 96];
var data2 = [1, 99];
var data3 = [16, 84];
var data4 = [12, 88];
var data5 = [29, 71];
var data6 = [15, 85];
var data7 = [12, 88];
var data8 = [10, 90];
var width = 100,
height = 100,
radius = (Math.min(width, height) / 2);
function drawDonut(data, divchart) {
var sym = "%"
var color = ["#BC204B","#00338D"];
// sort data
data = data.sort(function (a, b) { return b-a; });
var pie = d3.pie()
.value(function(d) {
return d
})(data);
var arc = d3.arc()
.outerRadius(radius - 10)
.innerRadius(radius - (radius / 1.9));
var labelArc = d3.arc()
.outerRadius(radius - 31)
.innerRadius(radius - 31);
var svg = d3.select(divchart)
.append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + 50 + "," + 50 + ")");
var g = svg.selectAll("arc")
.data(pie)
.enter().append("g")
.attr("class", "arc");
g.append("path")
.style("fill", function(d, i) {
return color[i];
})
.transition().delay(function (d, i) { return i*800}).duration(800)
.attrTween("d", arcTween)
function arcTween(d) {
var i = d3.interpolate(d.startAngle, d.endAngle);
return function (t) {
d.endAngle = i(t);
return arc(d);
}
}
g.append("g")
.attr("transform", "translate(-17,-17) scale(0.7)")
.html(myGroup);
}
drawDonut(data1, "#pie1")
drawDonut(data2, "#pie2")
drawDonut(data3, "#pie3")
drawDonut(data4, "#pie4")
drawDonut(data5, "#pie5")
drawDonut(data6, "#pie6")
drawDonut(data7, "#pie7")
drawDonut(data8, "#pie8")
div {
display: inline;
}
<script src="https://d3js.org/d3.v5.min.js"></script>
<div id="pie1"></div>
<div id="pie2"></div>
<div id="pie3"></div>
<div id="pie4"></div>
<div id="pie5"></div>
<div id="pie6"></div>
<div id="pie7"></div>
<div id="pie1"></div>
<div id="pie8"></div>
Relevant changes:
AttrTween function
function arcTween(d) {
var i = d3.interpolate(d.startAngle, d.endAngle);
return function (t) {
d.endAngle = i(t);
return arc(d);
}
}
Use the above for the paths along with a delay in the transition which btw is used to animate the paths in sequence.
g.append("path")
.style("fill", function(d, i) {
return color[i];
})
.transition().delay(function (d, i) { return i*800}).duration(800)
.attrTween("d", arcTween);
Sorted data in a descending order to draw the largest arc first.
// sort data
data = data.sort(function (a, b) { return b-a; });
Based on the answer by Shashank I like to propose a modification
do not sort the data because the order looks important (d[0] == man, d[1] == woman) and the arcs are colored in order
// sort data
//data = data.sort(function (a, b) { return b-a; });
the pie generator should also not sort the data based on value
var pie = d3.pie()
.sortValues(null) // both null means NO sort
.value(function(d) {
return d
})(data);
in Shashanks answer both parts are animated with easeCubic and each part (small or large) takes 800ms. To get a constant angular speed animation we must set the delay and duration based on the startAngle and the endAngle of the part. And the transition must be set to easeLinear. For the demo I have set the complete animation to 5000ms.
var msecPerRad = 5000 / (2*Math.PI);
g.append("path")
.style("fill", function(d, i) { return color[i]; })
.transition()
.ease(d3.easeLinear)
.delay(function (d) { return d.startAngle * msecPerRad;})
.duration(function (d) { return (d.endAngle - d.startAngle) * msecPerRad;})
.attrTween("d", arcTween);
Example with Linear easing
var myGroup = '<g><path class="st0" d="M15.6,10.9c1.3,0,2.4-1.2,2.4-2.7c0-1.5-1.1-2.7-2.4-2.7c-1.3,0-2.4,1.2-2.4,2.7C13.2,9.7,14.2,10.9,15.6,10.9L15.6,10.9z"/><path class="st0" d="M18.6,11.6h-1.2l-1.8,5.5l-1.8-5.5h-1.2c-1.3,0-2.4,1.2-2.4,2.7v13h2.4l1.2,16.4h3.6l1.2-16.4H21v-13C21,12.8,19.9,11.6,18.6,11.6L18.6,11.6z"/><path class="st0" d="M31.9,10.9c1.3,0,2.4-1.2,2.4-2.7c0-1.5-1.1-2.7-2.4-2.7c-1.3,0-2.4,1.2-2.4,2.7C29.5,9.7,30.6,10.9,31.9,10.9L31.9,10.9z"/><path class="st0" d="M39.8,25.2l-3.6-11.6c0,0-0.6-2-2.4-2h-3.6c-1.8,0-2.4,2-2.4,2l-3.6,11.6l1.2,0.7l4.2-9.5l-3.6,14.3h3.6l1.2,13h2.4l1.2-13H38l-3.6-14.3l4.2,9.5L39.8,25.2L39.8,25.2z"/></g>';
var data1 = [50, 50];
var data2 = [70, 30];
var data3 = [16, 84];
var data4 = [12, 88];
var data5 = [29, 71];
var data6 = [15, 85];
var data7 = [12, 88];
var data8 = [10, 90];
var width = 100,
height = 100,
radius = (Math.min(width, height) / 2);
function drawDonut(data, divchart) {
var sym = "%"
var color = ["#BC204B","#00338D"];
// sort data
//data = data.sort(function (a, b) { return b-a; });
var pie = d3.pie()
.sortValues(null) // both null means NO sort
.value(function(d) {
return d
})(data);
var arc = d3.arc()
.outerRadius(radius - 10)
.innerRadius(radius - (radius / 1.9));
var labelArc = d3.arc()
.outerRadius(radius - 31)
.innerRadius(radius - 31);
var svg = d3.select(divchart)
.append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + 50 + "," + 50 + ")");
var g = svg.selectAll("arc")
.data(pie)
.enter().append("g")
.attr("class", "arc");
var msecPerRad = 5000 / (2*Math.PI);
g.append("path")
.style("fill", function(d, i) { return color[i]; })
.transition()
.ease(d3.easeLinear)
.delay(function (d) { return d.startAngle * msecPerRad;})
.duration(function (d) { return (d.endAngle - d.startAngle) * msecPerRad;})
.attrTween("d", arcTween);
function arcTween(d) {
var i = d3.interpolate(d.startAngle, d.endAngle);
return function (t) {
d.endAngle = i(t);
return arc(d);
}
}
g.append("g")
.attr("transform", "translate(-17,-17) scale(0.7)")
.html(myGroup);
}
drawDonut(data1, "#pie1")
drawDonut(data2, "#pie2")
drawDonut(data3, "#pie3")
drawDonut(data4, "#pie4")
drawDonut(data5, "#pie5")
drawDonut(data6, "#pie6")
drawDonut(data7, "#pie7")
drawDonut(data8, "#pie8")
div {
display: inline;
}
<script src="https://d3js.org/d3.v5.min.js"></script>
<div id="pie1"></div>
<div id="pie2"></div>
<div id="pie3"></div>
<div id="pie4"></div>
<div id="pie5"></div>
<div id="pie6"></div>
<div id="pie7"></div>
<div id="pie1"></div>
<div id="pie8"></div>
If you want the complete angle animation performed with an easeCubic it takes little more math. The main reason is that both arc segments are independent trasitions. This should work for more then 2 arc segments.
It only works for monotone increasing easing functions, not for things like bounce, elastic and back(in/out).
function easeInverse(ease) {
return function(e) {
var min = 0, max = 1;
while (max - min > 1e-3) {
var mid = (max + min) * 0.5;
emid = ease(mid);
if (emid > e) { max = mid; }
else { min = mid; }
}
return max;
}
}
var inverseCubic = easeInverse(d3.easeCubic);
var oneOver2Pi = 1.0 / (2*Math.PI);
var total_msec = 5000;
g.append("path")
.style("fill", function(d, i) { return color[i]; })
.transition()
.ease(d3.easeLinear)
.delay(function (d) { return total_msec * inverseCubic(d.startAngle * oneOver2Pi);})
.duration(function (d) { return total_msec * (inverseCubic(d.endAngle * oneOver2Pi) - inverseCubic(d.startAngle * oneOver2Pi));})
.attrTween("d", arcTween);
function arcTween(d) {
var i = d3.interpolate(inverseCubic(d.startAngle * oneOver2Pi), inverseCubic(d.endAngle * oneOver2Pi));
return function (t) {
d.endAngle = 2*Math.PI*d3.easeCubic(i(t));
return arc(d);
}
}
Example with Cubic easing for the full circle angle.
var myGroup = '<g><path class="st0" d="M15.6,10.9c1.3,0,2.4-1.2,2.4-2.7c0-1.5-1.1-2.7-2.4-2.7c-1.3,0-2.4,1.2-2.4,2.7C13.2,9.7,14.2,10.9,15.6,10.9L15.6,10.9z"/><path class="st0" d="M18.6,11.6h-1.2l-1.8,5.5l-1.8-5.5h-1.2c-1.3,0-2.4,1.2-2.4,2.7v13h2.4l1.2,16.4h3.6l1.2-16.4H21v-13C21,12.8,19.9,11.6,18.6,11.6L18.6,11.6z"/><path class="st0" d="M31.9,10.9c1.3,0,2.4-1.2,2.4-2.7c0-1.5-1.1-2.7-2.4-2.7c-1.3,0-2.4,1.2-2.4,2.7C29.5,9.7,30.6,10.9,31.9,10.9L31.9,10.9z"/><path class="st0" d="M39.8,25.2l-3.6-11.6c0,0-0.6-2-2.4-2h-3.6c-1.8,0-2.4,2-2.4,2l-3.6,11.6l1.2,0.7l4.2-9.5l-3.6,14.3h3.6l1.2,13h2.4l1.2-13H38l-3.6-14.3l4.2,9.5L39.8,25.2L39.8,25.2z"/></g>';
var data1 = [50, 50];
var data2 = [70, 30];
var data3 = [16, 84];
var data4 = [12, 88];
var data5 = [29, 71];
var data6 = [15, 85];
var data7 = [12, 88];
var data8 = [10, 90];
var width = 100,
height = 100,
radius = (Math.min(width, height) / 2);
function drawDonut(data, divchart) {
var sym = "%"
var color = ["#BC204B","#00338D"];
// sort data
//data = data.sort(function (a, b) { return b-a; });
var pie = d3.pie()
.sortValues(null) // both null means NO sort
.value(function(d) {
return d
})(data);
var arc = d3.arc()
.outerRadius(radius - 10)
.innerRadius(radius - (radius / 1.9));
var labelArc = d3.arc()
.outerRadius(radius - 31)
.innerRadius(radius - 31);
var svg = d3.select(divchart)
.append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + 50 + "," + 50 + ")");
var g = svg.selectAll("arc")
.data(pie)
.enter().append("g")
.attr("class", "arc");
function easeInverse(ease) {
return function(e) {
var min = 0, max = 1;
while (max - min > 1e-3) {
var mid = (max + min) * 0.5;
emid = ease(mid);
if (emid > e) { max = mid; }
else { min = mid; }
}
return max;
}
}
var inverseCubic = easeInverse(d3.easeCubic);
var oneOver2Pi = 1.0 / (2*Math.PI);
var total_msec = 5000;
g.append("path")
.style("fill", function(d, i) { return color[i]; })
.transition()
.ease(d3.easeLinear)
.delay(function (d) { return total_msec * inverseCubic(d.startAngle * oneOver2Pi);})
.duration(function (d) { return total_msec * (inverseCubic(d.endAngle * oneOver2Pi) - inverseCubic(d.startAngle * oneOver2Pi));})
.attrTween("d", arcTween);
function arcTween(d) {
var i = d3.interpolate(inverseCubic(d.startAngle * oneOver2Pi), inverseCubic(d.endAngle * oneOver2Pi));
return function (t) {
d.endAngle = 2*Math.PI*d3.easeCubic(i(t));
return arc(d);
}
}
g.append("g")
.attr("transform", "translate(-17,-17) scale(0.7)")
.html(myGroup);
}
drawDonut(data1, "#pie1")
drawDonut(data2, "#pie2")
drawDonut(data3, "#pie3")
drawDonut(data4, "#pie4")
drawDonut(data5, "#pie5")
drawDonut(data6, "#pie6")
drawDonut(data7, "#pie7")
drawDonut(data8, "#pie8")
div {
display: inline;
}
<script src="https://d3js.org/d3.v5.min.js"></script>
<div id="pie1"></div>
<div id="pie2"></div>
<div id="pie3"></div>
<div id="pie4"></div>
<div id="pie5"></div>
<div id="pie6"></div>
<div id="pie7"></div>
<div id="pie1"></div>
<div id="pie8"></div>
Related
I'm trying to create a donut chart in d3js where each arc has a circle at its end.
Circle's edge must fit on arc's one.
I tried both by appending a circle and a circle wrapped in marker but with no succes.
Trying to append a marker seems to be the closest solution to the desired one but I can't help the marker oveflowing the arc edges.
Code:
var data = [
{
name: "punti",
count: 3,
color: "#fff000"
},
{
name: "max",
count: 7,
color: "#f8b70a"
}
];
var totalCount = data.reduce((acc, el) => el.count + acc, 0);
var image_width = 32;
var image_height = 32;
var width = 540,
height = 540,
radius = 200,
outerRadius = radius - 10,
innerRadius = 100;
var cornerRadius = innerRadius;
var markerRadius = (outerRadius - innerRadius) / 2;
var arc = d3
.arc()
.outerRadius(outerRadius)
.innerRadius(innerRadius)
.cornerRadius(cornerRadius);
var pie = d3
.pie()
.sort(null)
.value(function(d) {
return d.count;
});
var svg = d3
.select("body")
.append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");
var pieData = pie(data);
var g = svg
.selectAll(".arc")
.data(pieData)
.enter()
.append("g");
var path = g
.append("path")
.attr("d", arc)
.style("fill", function(d, i) {
return d.data.color;
});
var marker = svg
.append("defs")
.append("marker")
.attr("id", "endmarker")
.attr("overflow", "visible")
.append("circle")
.attr("cy", 0)
.attr("cx", 0)
.attr("r", markerRadius)
.attr("fill", "red");
g.attr("marker-end", "url(#endmarker)");
g
.append("circle")
.attr("cx", function(d) {
let path = d3.select(this.parentNode);
var x = arc.centroid(d)[0];
return x;
})
.attr("cy", function(d) {
var y = arc.centroid(d)[1];
console.log(d3.select(this).attr("cx"));
return y;
})
.attr("fill", d => d.data.color)
.attr("stroke", "black")
.attr("r", (outerRadius - innerRadius) / 2);
codepen here
Thanks to anyone who will help!
Assuming that you want your output like:
I found some code from Mike Bostock's Block here which shows how to add circles to rounded Arc Corners.
I adapted the following code for you which performs quite a bit of complex mathematics.
var cornerRadius = (outerRadius - innerRadius)/2;
svg.append("g")
.style("stroke", "#555")
.style("fill", "none")
.attr("class", "corner")
.selectAll("circle")
.data(d3.merge(pieData.map(function(d) {
return [
{angle: d.startAngle + d.padAngle / 2, radius: outerRadius - cornerRadius, start: +1},
{angle: d.endAngle - d.padAngle / 2, radius: outerRadius - cornerRadius, start: -1},
];
})))
.enter().append("circle")
.attr("cx", function(d) { return d.start * cornerRadius * Math.cos(d.angle) + Math.sqrt(d.radius * d.radius - cornerRadius * cornerRadius) * Math.sin(d.angle); })
.attr("cy", function(d) { return d.start * cornerRadius * Math.sin(d.angle) - Math.sqrt(d.radius * d.radius - cornerRadius * cornerRadius) * Math.cos(d.angle); })
.attr("r", cornerRadius);
Full snippet showing the output:
<div id="chart"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.12.0/d3.min.js"></script>
<script>
var data = [
{
name: "punti",
count: 3,
color: "#fff000"
},
{
name: "max",
count: 7,
color: "#f8b70a"
},
];
var totalCount = data.reduce((acc, el) => el.count + acc, 0);
var image_width = 32;
var image_height = 32;
var width = 540,
height = 540,
radius = 200,
outerRadius = radius - 10,
innerRadius = 100;
var cornerRadius = innerRadius;
var markerRadius = (outerRadius - innerRadius) / 2;
var arc = d3
.arc()
.outerRadius(outerRadius)
.innerRadius(innerRadius)
.cornerRadius(cornerRadius);
var pie = d3
.pie()
.sort(null)
.value(function(d) {
return d.count;
});
var svg = d3
.select("body")
.append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");
var pieData = pie(data);
var g = svg
.selectAll(".arc")
.data(pieData)
.enter()
.append("g");
var path = g
.append("path")
.attr("d", arc)
.style("fill", function(d, i) {
return d.data.color;
});
var cornerRadius = (outerRadius - innerRadius)/2;
svg.append("g")
.style("stroke", "#555")
.style("fill", "none")
.attr("class", "corner")
.selectAll("circle")
.data(d3.merge(pieData.map(function(d) {
return [
{angle: d.startAngle + d.padAngle / 2, radius: outerRadius - cornerRadius, start: +1},
{angle: d.endAngle - d.padAngle / 2, radius: outerRadius - cornerRadius, start: -1},
];
})))
.enter().append("circle")
.attr("cx", function(d) { return d.start * cornerRadius * Math.cos(d.angle) + Math.sqrt(d.radius * d.radius - cornerRadius * cornerRadius) * Math.sin(d.angle); })
.attr("cy", function(d) { return d.start * cornerRadius * Math.sin(d.angle) - Math.sqrt(d.radius * d.radius - cornerRadius * cornerRadius) * Math.cos(d.angle); })
.attr("r", cornerRadius);
</script>
I have created a donut chart and I am trying to make it responsive. I want the height to have a maximum value. If the window size changes, the donut changes attributes (width and height). Now, I want to set a maximum height value and off course I need the ratio to remain the same. Note that I want the donut chart to be horizontally aligned (same margin from the right and left of the screen).
I have tried changing the values in the width by using the window.innerWidth and innerHeight with no success. I have also tried adding a max attribute for the height in the D3 part of my code.
var dataset = {
numbers: [3200, 5400, 8600]
};
var width = 500,
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 color = d3.scale.ordinal().range([d3.rgb("#c7003b"), d3.rgb('#000'), d3.rgb('#ccc'),d3.rgb('transparent')])
var pie = d3.layout.pie()
.sort(null);
var arc = d3.svg.arc()
.innerRadius(radius - 80)
.outerRadius(radius - 40);
var arcThin = d3.svg.arc()
.innerRadius(radius - 65)
.outerRadius(radius - 55);
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').append("g").attr("class", "parent");
var angleData = pie(dataset.numbers);
angleData[1].startAngle = 0;
angleData[1].endAngle = -angleData[1].endAngle + angleData[0].endAngle;
angleData[2].startAngle = angleData[0].endAngle;
angleData[2].endAngle = (2*Math.PI) + angleData[1].endAngle;
var path = svg.selectAll("path")
.data(angleData)
.enter().append("path")
.attr("fill", function (d, i) { return color(i); })
.attr("d", function(d){
return 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);
function createChart() {
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
};
});
}
function arcTween(a, j) {
var i = d3.interpolate(this._current, a);
this._current = i(0);
return function (t) {
return (j === (dataset.numbers.length - 1)) ? arcThin(i(t)) : arc(i(t));
};
}
function type(d) {
d.value = +d.value;
return d;
}
#import url(https://fonts.googleapis.com/css?family=Karla);body{font-family:Karla,sans-serif;margin:auto;position:relative}.text{text-anchor:middle;color:#000;font-size:1.7em;font-weight:700;text-transform:uppercase}#legend{align-items:center;border-radius:5px;display:flex;height:0%;justify-content:space-around;width:95%;font-size:25px}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.0.0/d3.min.js"></script>
<div id="Donut-chart"></div>
Remove width, height and preserveAspectRatio attributes from SVG element and set max-height on it via CSS. Like so:
var dataset = {
numbers: [3200, 5400, 8600]
};
var width = 500,
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 color = d3.scale.ordinal().range([d3.rgb("#c7003b"), d3.rgb('#000'), d3.rgb('#ccc'),d3.rgb('transparent')])
var pie = d3.layout.pie()
.sort(null);
var arc = d3.svg.arc()
.innerRadius(radius - 80)
.outerRadius(radius - 40);
var arcThin = d3.svg.arc()
.innerRadius(radius - 65)
.outerRadius(radius - 55);
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')
.append("g").attr("class", "parent");
var angleData = pie(dataset.numbers);
angleData[1].startAngle = 0;
angleData[1].endAngle = -angleData[1].endAngle + angleData[0].endAngle;
angleData[2].startAngle = angleData[0].endAngle;
angleData[2].endAngle = (2*Math.PI) + angleData[1].endAngle;
var path = svg.selectAll("path")
.data(angleData)
.enter().append("path")
.attr("fill", function (d, i) { return color(i); })
.attr("d", function(d){
return 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);
function createChart() {
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
};
});
}
function arcTween(a, j) {
var i = d3.interpolate(this._current, a);
this._current = i(0);
return function (t) {
return (j === (dataset.numbers.length - 1)) ? arcThin(i(t)) : arc(i(t));
};
}
function type(d) {
d.value = +d.value;
return d;
}
#Donut-chart svg {
max-height: 300px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.0.0/d3.min.js"></script>
<div id="Donut-chart"></div>
I am trying to animate the start angle of the arc using D3.js
Any help or link for reference would do.
I have tried the below:
http://jsfiddle.net/87e3d4tj/
d3.select('#my-path').datum({
startAngle: endAngle,
endAngle: ( 90 * (Math.PI/180) )
})
.transition()
.duration(duration)
.attrTween('d', d => {
var interpolate = d3.interpolate(d.startAngle, d.endAngle);
return function(t) {
d.endAngle = endAngle;
d.startAngle = interpolate(t);
return arc(d);
};
});
i have tried the below hope you are looking for the same.
https://jsfiddle.net/debasish007/eu7xo4mL/
var width = 400,
height = 400,
τ = (Math.PI/180);
var arc = d3.arc()
.innerRadius(130)
.outerRadius(150);
var svg = d3.select("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")")
var foreground = svg.append("path")
.datum({
endAngle: 90 * τ,
startAngle: -90 * τ
})
.style("fill", "blue")
.attr("d", arc);
setTimeout(function () {
foreground.transition()
.duration(750)
.call(arcTween, -30 * τ, 90 * τ);
}, 1500);
function arcTween(transition, newStartAngle, newFinishAngle) {
transition.attrTween("d", function (d) {
var interpolateStart = d3.interpolate(d.startAngle, newStartAngle);
return function (t) {
d.endAngle = newFinishAngle;
d.startAngle = interpolateStart(t);
return arc(d);
};
});
}
My customized d3 radial chart is not rendering arc for least value.
Fiddle
var width = 360,
height = 300,
barHeight = height / 2 - 40;
var formatNumber = d3.format("s");
var color = d3.scale.ordinal()
.range(["#F15D5D","#FAD64B"]);
var svg = d3.select('#chart').append("svg")
.attr("width", width)
.attr("height", height)
.attr('class','radial')
.append("g")
.attr("transform", "translate(" + width/2 + "," + height/2 + ")");
var data = [{
"name": "ABC",
"value":4
},
{
"name": "XYZ",
"value":5
},{
"name": "DEF",
"value":2
},
{
"name": "GHI",
"value":3
},{
"name": "JKL",
"value":1
}];
data.sort(function(a,b) { return b.value - a.value; });
var extent = d3.extent(data, function(d) { return d.value; });
var barScale = d3.scale.linear()
.domain(extent)
.range([0, barHeight]);
var keys = data.map(function(d,i) { return d.name; });
var numBars = keys.length;
// X scale
var x = d3.scale.linear()
.domain(extent)
.range([0, -barHeight]);
// X axis
var xAxis = d3.svg.axis()
.scale(x).orient("left")
.ticks(3)
.tickFormat(formatNumber);
// Inner circles
var circles = svg.selectAll("circle")
.data(x.ticks(5))
.enter().append("circle")
.attr("r", function(d) {return barScale(d);})
.style("fill", "none")
//.style("stroke", "black")
//.style("stroke-dasharray", "2,2")
.style("stroke-width",".5px");
// Create arcs
var arc = d3.svg.arc()
.startAngle(function(d,i) {
var a = (i * 2 * Math.PI) / numBars;
var b = ((i + 1) * 2 * Math.PI) / numBars;
var d = (b-a) / 4;
var x = a+d;
var y = b-d;
return x;//(i * 2 * Math.PI) / numBars;
})
.endAngle(function(d,i) {
var a = (i * 2 * Math.PI) / numBars;
var b = ((i + 1) * 2 * Math.PI) / numBars;
var d = (b-a) / 4;
var x = a+d;
var y = b-d;
return y;//((i + 1) * 2 * Math.PI) / numBars;
})
.innerRadius(0);
// Render colored arcs
var segments = svg.selectAll("path")
.data(data)
.enter().append("path")
.each(function(d) { d.outerRadius = 0; })
.style("fill", function (d) { return color(d.name); })
.attr("d", arc);
segments.transition().ease("elastic").duration(1000).delay(function(d,i) {return (25-i)*50;})
.attrTween("d", function(d,index) {
var i = d3.interpolate(d.outerRadius, barScale(+d.value));
return function(t) { d.outerRadius = i(t); return arc(d,index); };
});
// Outer circle
svg.append("circle")
.attr("r", barHeight)
.classed("outer", true)
.style("fill", "none")
//.style("stroke", "black")
.style("stroke-width",".5px");
// Apply x axis
svg.append("g")
.attr("class", "x axis")
.call(xAxis);
// Labels
var labelRadius = barHeight * 1.025;
var labels = svg.append("g")
.classed("labels", true);
labels.append("def")
.append("path")
.attr("id", "label-path")
.attr("d", "m0 " + -labelRadius + " a" + labelRadius + " " + labelRadius + " 0 1,1 -0.01 0");
labels.selectAll("text")
.data(keys)
.enter().append("text")
.style("text-anchor", "middle")
.style("font-weight","bold")
.style("fill", function(d, i) {return "#555";})
.append("textPath")
.attr("xlink:href", "#label-path")
.attr("startOffset", function(d, i) {return i * 100 / numBars + 50 / numBars + '%';})
.text(function(d) {return d.toUpperCase(); });
Here the item JKL with value 1 is not rendered and the outerRadius is calculated as 0.
The problem lies here:
var extent = d3.extent(data, function(d) { return d.value; });
This line sets the domain of barScale and x. However, if you do this using d3.extent, you're actually saying that the domain goes from the minimum value to the maximum value...
[1, 5];
... meaning that the value 1 will be mapped to the minimum value of the range.
Instead of that, set the minimum value in the domain to zero:
var extent = [0, d3.max(data, d=>d.value)];
Which will have this result:
[0, 5];
Here is your updated fiddle: https://jsfiddle.net/a9w7c2dw/
I am quitte a newbie to D3.js and for a project i need to make a donutchart with data that changes every 2 seconds. But every time I try to change the data i get an error:
Uncaught TypeError: path.data is not a function
I used this code as an example for the change function
Code:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>D3 donuttest</title>
<script src="//d3js.org/d3.v3.min.jss"></script>
<style>
body {
font: 10px sans-serif;
}
</style>
</head>
<body>
</body>
<div id="chartwrapper">
</div>
</body>
<script>
var wrapper="#chartwrapper";
var dummydata=70;
var data=[dummydata,100-dummydata];
var width = 460,
height = 300,
radius = Math.min(width, height) / 2;
var color = d3.scale.category20();
var pie = d3.layout.pie()
.sort(null);
var arc = d3.svg.arc()
.innerRadius(radius - 100)
.outerRadius(radius - 50);
var svg = d3.select(wrapper).append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");
var path = svg.selectAll("path")
.data(pie(data))
.enter().append("path")
.attr("fill", function(d, i) { return color(i); })
.attr("d", arc)
.transition()
.ease("bounce")
.duration(1000)
.attrTween("d", initTween);
var label=svg.append("g")
.attr("class", "display")
.attr("transform", "translate(-30,10)");
var labeltext=label.append("text")
.attr("font-size","2rem")
.text(dummydata + "%");
function change(data){
labeltext.text(data[0] + "%");
path.data(pie(data));
path.transition().duration(750).attrTween("d",arcTween);
};
//animation tween for when graph is drawn
function initTween(b) {
b.innerRadius = 0;
var i = d3.interpolate({startAngle: 0, endAngle: 0}, b);
return function(t) { return arc(i(t)); };
}
//animation for when data is changed
function arcTween(a) {
var i = d3.interpolate(this._current, a);
this._current = i(0);
return function(t) {
return arc(i(t));
};
};
//simulating data changes
setInterval(function() {
dummydata = parseInt(Math.random() * 100);
data=[dummydata,100-dummydata];
console.log(data)
change(data);
},2000);
</script>
Here's a slight modification of your code that might help you. I used this answer as a guide.
The modifications include:
only store the result of append() in the path variable;
when first drawing the chart, store the initial angles in _current instead of using the initTween function.
var wrapper = "#chartwrapper";
var dummydata = 70;
var data = [dummydata, 100 - dummydata];
var width = 460,
height = 300,
radius = Math.min(width, height) / 2;
var color = d3.scale.category20();
var arc = d3.svg.arc()
.outerRadius(radius - 100)
.innerRadius(radius - 50);
var pie = d3.layout.pie()
.sort(null);
var svg = d3.select(wrapper).append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");
var path = svg.selectAll("path")
.data(pie(data))
.enter()
.append("path");
path.attr("fill", function(d, i) {
return color(i);
})
.attr("d", arc)
.transition()
.ease("bounce")
.duration(1000)
.each(function(d) {
this._current = d;
}); // store the initial angles
var label = svg.append("g")
.attr("class", "display")
.attr("transform", "translate(-30,10)");
var labeltext = label.append("text")
.attr("font-size", "2rem")
.text(dummydata + "%");
function change(data) {
labeltext.text(data[0] + "%");
path.data(pie(data));
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));
};
}
setInterval(function() {
dummydata = parseInt(Math.random() * 100);
data = [dummydata, 100 - dummydata];
console.log(data)
change(data);
}, 2000);
body {
font: 10px sans-serif;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<div id="chartwrapper"></div>