Reputation: 259
I've prepared D3 Radial Chart component to show percentage value of some target. It would be great to add smoothy transition effect when start drawing foreground circle from 0 to chartPercentage (eg. 70%).
The question is - how to prepare transition / delay / duration effect with code which is attached below ?
Second idea which I also want to implement is to count value inside of the circle (radial-content) with animation from 0 to chartValue. How to prepare such solution?
Thank you !
const chartPercentage = 70;
const chartValue = 1.1242
const radius = 75;
const border = 7;
const padding = 0;
const width = 400;
const height = 400;
const twoPi = Math.PI * 2;
const boxSize = (radius + padding) * 2;
let svg;
function setArc() {
return d3.arc()
.startAngle(0)
.innerRadius(radius)
.outerRadius(radius - border)
.cornerRadius(50);
}
function draw() {
svg = d3.select(".chart").append("svg")
.attr('width', width)
.attr('height', height);
svg.append("foreignObject")
.attr("width", boxSize)
.attr("height", boxSize)
.append("xhtml:div")
.attr('class', 'radial-wrapper')
.html(`<div class="radial-content">${chartValue}</div>`);
const field = svg.append('g')
.attr('transform', 'translate(' + boxSize / 2 + ',' + boxSize / 2 + ')');
const meter = field.append('g')
.attr('class', 'progress-meter');
const background = meter.append("path")
.datum({endAngle: twoPi})
.attr('class', 'background')
.attr('fill', '#2D2E2F')
.attr('fill-opacity', 0.1)
.attr("d", setArc());
const foreground = meter.append("path")
.datum({endAngle: (chartPercentage/100) * twoPi})
.attr('class', 'foreground')
.attr('fill', 'red')
.attr('fill-opacity', 1)
.attr('d', setArc());
}
draw();
body { margin:30px;position:fixed;top:0;right:0;bottom:0;left:0; }
.radial-wrapper{ display: flex; align-items: center; justify-content: center;width: 100%; height: 100%;}
<script src="https://d3js.org/d3.v4.min.js"></script>
<div class="chart"></div>
Upvotes: 1
Views: 1212
Reputation: 8509
I rewrote your code. When you need to animate some attribute, you should use attrTween
not attr
method.
const chartPercentage = 70;
const chartValue = 1.1242
const radius = 75;
const border = 7;
const padding = 0;
const width = 400;
const height = 400;
const twoPi = Math.PI * 2;
const boxSize = (radius + padding) * 2;
let svg;
const setArc = d3.arc()
.startAngle(0)
.innerRadius(radius)
.outerRadius(radius - border)
.cornerRadius(50);
const arcParams = {};
function draw() {
svg = d3.select(".chart").append("svg")
.attr('width', width)
.attr('height', height);
svg.append("foreignObject")
.attr("width", boxSize)
.attr("height", boxSize)
.append("xhtml:div")
.attr('class', 'radial-wrapper')
.html(`<div class="radial-content"></div>`);
const field = svg.append('g')
.attr('transform', 'translate(' + boxSize / 2 + ',' + boxSize / 2 + ')');
const meter = field.append('g')
.attr('class', 'progress-meter');
const background = meter
.append("path")
.attr('class', 'background')
.attr('fill', '#2D2E2F')
.attr('fill-opacity', 0.1)
.attr("d", setArc({ endAngle: twoPi }));
const foreground = meter
.append("path")
.transition()
.ease(d3.easeBounce)
.duration(1500)
.attr('class', 'foreground')
.attr('fill', 'red')
.attr('fill-opacity', 1)
.attrTween("d", function() {
return arcTween({ endAngle: 0 }, chartPercentage/100 )
})
}
function arcTween(d, new_score) {
var new_startAngle = 0
var new_endAngle = new_startAngle + new_score * 2 * Math.PI
var interpolate_start = d3.interpolate(d.startAngle, new_startAngle)
var interpolate_end = d3.interpolate(d.endAngle, new_endAngle)
return function(t) {
d.endAngle = interpolate_end(t)
d3.select('.radial-content')
.text((d.endAngle / new_endAngle * chartValue).toFixed(4));
return setArc(d)
}
}
draw();
body {
margin: 30px;
position: fixed;
top: 0;
right: 0;
bottom: 0;
left: 0;
}
.radial-wrapper {
display: flex;
align-items: center;
justify-content: center;
width: 100%;
height: 100%;
}
<script src="https://d3js.org/d3.v4.min.js"></script>
<div class="chart"></div>
Upvotes: 1
Reputation: 32327
Make a function for attribute tween.
function arcTween(a) {
var j = {"endAngle":0};//start angle
var i = d3.interpolateObject(j, a);
return function(t) {
d3.select(".radial-content").text(d3.format(".4n")(chartValue*t));
return arc(i(t));
};
}
In the above function
d3.select(".radial-content").text(d3.format(".4n")(chartValue*t));
this will change the text(and output it in the format) in the radial content as the transition runs.
now add the tween function to the foreground path.
const foreground = meter.append("path")
.datum({
endAngle: (chartPercentage / 100) * twoPi
})
.attr('class', 'foreground')
.attr('fill', 'red')
.attr('fill-opacity', 1)
.transition().duration(750).attrTween("d", arcTween);
working code here
Upvotes: 1