var $this = $('.crescentchart');
var data = [{
label: 'Fudge Brownie',
value: 45,
},
{
label: 'Cherry Vanilla',
value: 60,
},
{
label: 'Pistachio',
value: 5,
},
{
label: 'Caramel',
value: 10,
}
];
var oldData = "";
var width = $this.data('width'),
height = $this.data('height'),
radius = $this.data('r'),
thickness = $this.data("thickness"),
spacing = $this.data("spacing");
var color = d3.scaleOrdinal()
.range(["#bc658d", "#82c4c3", "#f9d89c", "#f5a7a7"]);
var svg = d3.select($this[0])
.append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr('class', 'crescentchart')
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");
var segments = svg.append('g').attr('class', 'segments');
var data = setData(data, radius);
//append previous value to it.
$.each(data, function(index, value) {
if (oldData[index] != undefined) {
data[index]["previousFraction"] = oldData[index].fraction;
} else {
data[index]["previousFraction"] = 0;
}
});
var arcpaths = segments.selectAll("path")
.data(data);
arcpaths.enter().append("path")
.style("fill", function(d, i) {
return color(i);
})
.transition()
.ease(d3.easeElastic)
.duration(3000)
.attrTween("d", function(d) {
return arcTween(d, thickness, radius);
});
arcpaths.transition()
.ease(d3.easeElastic)
.style("fill", function(d, i) {
return color(i);
})
.duration(3000)
.attrTween("d", function(d) {
return arcTween(d, thickness, radius);
});
arcpaths.exit().transition()
.ease(d3.easeBounce)
.duration(3000)
.attrTween("d", function(d) {
return arcTween(d, thickness, radius);
})
.remove();
function arcTween(b, thickness, ir) {
var prev = JSON.parse(JSON.stringify(b));
prev.endAngle = b.previousEndAngle;
prev.fraction = b.previousFraction;
var i = d3.interpolate(prev, b);
const arc = getArc(thickness, ir);
return function(t) {
return arc(i(t));
};
}
function getRadiusRing(ir, i) {
return ir - (i * (thickness + spacing));
}
// The portion of the fraction that is drawn as an arc,
// instead of as a straight part
const arcPortion = 3 / 8;
function getArc(thickness, ir) {
var arc = d3.arc()
.innerRadius(function(d) {
return getRadiusRing(ir, d.index);
})
.outerRadius(function(d) {
return getRadiusRing(ir + thickness, d.index);
})
.startAngle(- arcPortion * 2 * Math.PI)
.endAngle(function(d, i) {
return -(d.fraction + arcPortion) * 2 * Math.PI;
});
// This function is only called when endAngle is greater than
// 3 * Math.PI, otherwise we simply draw an arc, because it makes
// no difference.
function jShape(d, i) {
const context = d3.path();
const innerRadius = getRadiusRing(ir, d.index);
const outerRadius = getRadiusRing(ir + thickness, d.index);
const startAngle = -arcPortion * 2 * Math.PI - Math.PI / 2;
const endAngle = 0;
// Start at the correct position on the inside
context.moveTo(
innerRadius * Math.cos(startAngle),
innerRadius * Math.sin(startAngle)
);
context.arc(0, 0, innerRadius, startAngle, endAngle, true);
// Now draw the straight part
const fullLength = innerRadius * 2 * Math.PI;
// The first 0.25 corresponds to the curved part drawn earlier.
const straightLength = (d.fraction - arcPortion) * fullLength
context.lineTo(
innerRadius * Math.cos(endAngle),
innerRadius * Math.sin(endAngle) - straightLength
);
// Move to the outside
context.lineTo(
outerRadius * Math.cos(endAngle),
innerRadius * Math.sin(endAngle) - straightLength
);
context.lineTo(
outerRadius * Math.cos(endAngle),
outerRadius * Math.sin(endAngle)
);
// And curve back
context.arc(0, 0, outerRadius, endAngle, startAngle, false);
context.closePath();
return context + "";
}
return function(d, i) {
return d.fraction <= arcPortion ? arc(d, i) : jShape(d, i);
}
}
function setData(data, r) {
var segmentValueSum = 0;
$.each(data, function(ri, va) {
segmentValueSum += va.value;
});
$.each(data, function(ri, va) {
var segmentValue = va.value;
var fraction = segmentValue / segmentValueSum;
data[ri]["index"] = ri;
data[ri]["fraction"] = fraction;
});
return data;
}
body {
background: #eeeeee;
}
.arc path {
stroke: #fff;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<script src="https://d3js.org/d3.v4.js"></script>
<h1>CrescentChart I</h1>
<div class="crescentchart" data-width="300" data-height="300" data-r="90" data-thickness="6" data-spacing="10" />