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);
};
});
}
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>
If I simply add a % to the value it won't work. How can I add % to all the values?
var chartData = {
"barCircleMobile":[
{"index":0.3, "value":100, "fill":"#37b54e", "label":"WebMd Health"},
{"index":0.4, "value":50, "fill":"#69dad9", "label":"Livestrong.com"},
{"index":0.5, "value":31.25, "fill":"#00a8dd", "label":"Everyday
Health"},
{"index":0.6, "value":18.75, "fill":"#157996", "label":"About.com"},
{"index":0.7, "value":6.5, "fill":"#0068ff", "label":"Drugs.com"},
],
"barCircleWeb":[
{"index":0.3, "value":100, "fill":"#20d974", "label":"WebMD Health"},
{"index":0.4, "value":40, "fill":"#3bc1c4", "label":"Everyday Health"},
{"index":0.5, "value":30, "fill":"#23889d", "label":"Livestrong.com"},
{"index":0.6, "value":20, "fill":"#0071a3", "label":"About.com Health
Section"},
{"index":0.7, "value":10, "fill":"#0042a3", "label":"Healthline"},
]
};
function drawBarCircleChart(data,target,values,labels){
var w = 362,
h = 362,
size = data[0].value * 1,
radius = 200,
sectorWidth = .1,
radScale = 25,
sectorScale = 1.75,
target = d3.select(target),
valueText = d3.select(values),
labelText = d3.select(labels);
var arc = d3.svg.arc()
.innerRadius(function(d,i){return (d.index/sectorScale) * radius +
radScale; })
.outerRadius(function(d,i){return ((d.index/sectorScale) +
(sectorWidth/sectorScale)) * radius + radScale; })
.startAngle(Math.PI)
.endAngle(function(d) { return Math.PI + (d.value / size) * 2 * Math.PI;
});
var path = target.selectAll("path")
.data(data);
//TODO: seperate color and index from data object, make it a pain to update
object order
path.enter().append("svg:path")
.attr("fill",function(d,i){return d.fill})
.transition()
.ease("elastic")
.duration(1000)
.delay(function(d,i){return i*100})
.attrTween("d", arcTween);
valueText.selectAll("tspan").data(data).enter()
.append("tspan")
.attr({
x:50,
y:function(d,i){return i*14},
"text-anchor":"end"
})
.text(function(d,i){return data[i].value});
labelText.selectAll("tspan").data(data).enter()
.append("tspan")
.attr({
x:0,
y:function(d,i){return i*14}
})
.text(function(d,i){return data[i].label});
function arcTween(b) {
var i = d3.interpolate({value: 0}, b);
return function(t) {
return arc(i(t));
};
}
}
// Animation Queue
setTimeout(function(){drawBarCircleChart(chartData.barCircleWeb,"#circleBar-
web-chart","#circleBar-web-values","#circleBar-web-labels")},500);
setTimeout(function()
{drawBarCircleChart(chartData.barCircleMobile,"#circleBar-mobile-
chart","#circleBar-mobile-values","#circleBar-mobile-labels")},800);
d3.select("#circleBar-web-icon")
.transition()
.delay(500)
.duration(500)
.attr("opacity","1");
d3.select("#circleBar-web-text")
.transition()
.delay(750)
.duration(500)
.attr("opacity","1");
d3.select("#circleBar-web-clipLabels")
.transition()
.delay(600)
.duration(1250)
.attr("height","150");
d3.select("#circleBar-mobile-icon")
.transition()
.delay(800)
.duration(500)
.attr("opacity","1");
d3.select("#circleBar-mobile-text")
.transition()
.delay(1050)
.duration(500)
.attr("opacity","1");
d3.select("#circleBar-mobile-clipLabels")
.transition()
.delay(900)
.duration(1250)
.attr("height","150");
You can add '%' in your text return,
valueText.selectAll("tspan").data(data).enter()
.append("tspan")
.attr({
x:50,
y:function(d,i){return i*14},
"text-anchor":"end"
})
.text(function(d,i){return data[i].value +'%' });
I hope it helps you
As the title says. How can I reverse the animation path in a pie (D3.js). As default, the pie renders clockwise with animation. How do I reverse it?
See picture example.
JS here:
var pie = d3.layout.pie()
.sort(null)
.startAngle(1 * Math.PI)
.endAngle(3 * Math.PI)
.value(function (d) { return d.percentage; });
g.append("path")
.attr("d", arc)
.style("fill", function (d) { return d.data.color; })
.attr({
"fill": function (d) {
return d.data.color;
}
})
.transition()
.duration(1000)
.attrTween("d", function (d) {
var i = d3.interpolate(d.startAngle, d.endAngle);
return function (t) {
d.endAngle = i(t);
return arc(d);
}
});
Just swap endAngle with startAngle:
.transition()
.duration(1000)
.attrTween("d", function (d) {
var i = d3.interpolate(d.endAngle, d.startAngle);
return function (t) {
d.startAngle = i(t);
return arc(d);
}
});
Check the snippet:
var dataset = {
apples: [5345, 2879, 1997, 2437, 4045],
};
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("body").append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");
var path = svg.selectAll("path")
.data(pie(dataset.apples))
.enter().append("path")
.attr("fill", function(d, i) { return color(i); })
.attr("d", arc)
.transition()
.duration(1000)
.attrTween("d", function (d) {
var i = d3.interpolate(d.endAngle, d.startAngle);
return function (t) {
d.startAngle = i(t);
return arc(d);
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
I have a static 3 level Sunburst diagram -
http://colinwhite.net/Sunburst/
My data is being nested with this function
http://colinwhite.net/Sunburst/js/treeRemapper.js
My approach is based on this example -
http://bl.ocks.org/mbostock/4348373
For some reason my zoom and tweening is not working -
var width = 960, height = 700,
radius = Math.min(width, height) / 2,
color = d3.scale.category20c();
var x = d3.scale.linear().range([0, 2 * Math.PI]),
y = d3.scale.sqrt().range([0, radius]);
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + width / 2 + "," + height * .52 + ")");
var partition = d3.layout.partition()
.size([2 * Math.PI, radius * radius])
.value(function(d) { return 1; });
var arc = d3.svg.arc()
.startAngle(function(d) { return d.x; })
.endAngle(function(d) { return d.x + d.dx; })
.innerRadius(function(d) { return Math.sqrt(d.y); })
.outerRadius(function(d) { return Math.sqrt(d.y + d.dy); });
d3.json("data/getJson.php", function(error, data) {
var treeData = genJSON(data, ['SourceID', 'ProviderID']);
var path = svg.selectAll("path")
.data(partition.nodes(treeData))
.enter().append("path")
.attr("d", arc)
.style("fill-rule", "evenodd")
.style("fill", function(d) { return color((d.children ? d : d.parent).name); })
.on("click", click);
function click(d) {
path.transition()
.duration(750)
.attrTween("d", arcTween(d));
}
});
function arcTween(d) {
var xd = d3.interpolate(x.domain(), [d.x, d.x + d.dx]),
yd = d3.interpolate(y.domain(), [d.y, 1]),
yr = d3.interpolate(y.range(), [d.y ? 20 : 0, radius]);
return function(d, i) {
return i
? function(t) { return arc(d); }
: function(t) { x.domain(xd(t)); y.domain(yd(t)).range(yr(t)); return arc(d); };
};
}
Can anyone tell me what I'm doing wrong?
The example you are quoting is based on the "rescaling"/"readjusting" x and y domain/ranges. In your example, you have defined an arc that does not depend on the x and y scales, thus if you change these, there's no effect.
So what can you do?
First you need to take out the size of the partition (as you will handle that with scales):
var partition = d3.layout.partition()
//.size([2 * Math.PI, radius * radius])
.value(function(d) {
return 1;
});
(remove it, not comment it :) )
Next, you have to use and arc that does depend on the scales, e.g. like in the example:
var arc = d3.svg.arc()
.startAngle(function(d) { return Math.max(0, Math.min(2 * Math.PI, x(d.x))); })
.endAngle(function(d) { return Math.max(0, Math.min(2 * Math.PI, x(d.x + d.dx))); })
.innerRadius(function(d) { return Math.max(0, y(d.y)); })
.outerRadius(function(d) { return Math.max(0, y(d.y + d.dy)); });
As you can see, the max and min are kind of squeezing the "hidden" segments...
Cross fingers, run it
(I tried it with flare.json, it did work here)
Note
If you plan to also add reweighting when zoomed in, have a look at this nice example: http://bl.ocks.org/kerryrodden/477c1bfb081b783f80adof