<div id="activity"></div>
#activity, #activity svg {
margin-left: auto;
margin-right: auto;
}
#activity {
width: 450px;
margin: 5rem auto 0 auto;
}
body {
background-color: black;
}
/* Data */
const data = [
{
id: 'moving',
goal: 350,
unit: 'kcal',
values: [0,0,0,0,0,0,0,0,5,2,2,1,5,2,47,19,53,10,2],
},
{
id: 'exercising',
goal: 40,
unit: 'min',
values: [0,0,0,0,0,0,0,0,2,0,2,0,0,1,1,0,31,3,0],
},
{
id: 'standing',
goal: 12,
unit: 'h',
values: [0,0,0,0,0,0,0,0,1,1,1,0,1,1,1,0,1,1,1],
},
];
/* Define color, labels, icons. */
const defs = {
moving: {
label: 'Moving',
icon: '↦',
color: 'hotpink'
},
exercising: {
label: 'Exercising',
icon: '↠',
color: 'limegreen'
},
standing: {
label: 'Standing',
icon: '↟',
color: 'turquoise'
}
};
/* Chart configuration. */
const width = 450;
const height = 600;
const margin = 40;
const circleStroke = 50;
const circleSpace = 2;
const fullCircle = Math.PI * 2;
/* Generate statistics. */
const activity = data.map((row) => {
return {
...row,
total: d3.sum(row.values),
perc: parseInt(d3.sum(row.values) / row.goal * 100) / 100,
}
});
/* Generate the description. */
const generateDescription = () => {
return activity.map((row) => {
const percentage = row.perc >= 1 ? 'Complete' : `${row.perc * 100}%`;
return `${defs[row.id].label}: ${percentage}.`;
}).join(' ');
}
/* Draw a ring with a given radius and angle. */
const drawRing = (parent, arc, color, opacity) => {
return parent.append('path')
.attr('d', d3.arc()
.innerRadius(arc.inner)
.outerRadius(arc.outer)
.startAngle(arc.start)
.endAngle(arc.end)
)
.attr('fill', color)
.attr('opacity', opacity);
}
/* Create the chart. */
const chart = d3.select('#activity').append('svg')
.attr('width', width)
.attr('height', height);
/* Set the radius. */
const radius = (width - margin) / 2;
/* Create a group for all 3 rings. */
const rings = chart.append('g')
.attr('transform', `translate(${width / 2}, ${radius})`);
/* Draw 3 rings */
activity.forEach((stat, index) => {
/* Set the properties for the arc */
const arc = {
inner: radius - circleStroke * (index + 1) - circleSpace * index,
outer: radius - circleStroke * index - circleSpace * index,
start: 0,
end: fullCircle
};
/* Add a group per ring */
const group = rings.append('g')
.attr('role', 'img') // SR support
.attr('aria-labelledby', `activityTitle-${stat.id}`); // SR support
/* Add title. */
group.append('title')
.text(`${defs[stat.id].label}: ${stat.perc * 100}%.`)
.attr('id', `activityTitle-${stat.id}`);
/* Draw the dark band */
drawRing(
group,
{...arc, start: fullCircle * stat.perc },
defs[stat.id].color,
0.25
);
/* Draw the active band */
drawRing(
group,
{...arc, end: fullCircle * stat.perc},
defs[stat.id].color,
1
);
/* Add the labels. */
group.append('text')
.text(defs[stat.id].icon)
.attr('fill', '#000')
.attr('transform', `translate(${circleSpace}, -${(arc.outer + arc.inner) / 2 - circleSpace * (index + 2)})`)
.attr('font-size', '1.5rem');
/* Add legend. */
const legend = chart.append('text')
.attr('text-anchor', 'middle')
.attr('transform', `translate(${width / 2}, ${radius * 2 + 20 * (index + 2)})`)
.attr('fill', defs[stat.id].color);
legend.append('tspan')
.text(`${defs[stat.id].icon} `)
.attr('aria-hidden', 'true');
legend.append('tspan')
.text(`${defs[stat.id].label}: ${stat.total}/${stat.goal}${stat.unit} (${stat.perc * 100}%)`);
});
This Pen doesn't use any external CSS resources.
This Pen doesn't use any external JavaScript resources.