zeus.ugent.be/content/assets/scripts/datavis/punchcard.js
2018-05-16 22:42:07 +02:00

120 lines
3.2 KiB
JavaScript

(function () {
function punchCard() {
var margin = { top: 20, right: 20, bottom: 30, left: 150 };
var data;
var updateData;
const tooltip = d3.select('body').append('div')
.classed('tooltip', true)
.attr('id', 'pCardTooltip')
.style("opacity", 0);
;
function chart(svg) {
let width = svg.attr('width');
let height = svg.attr('height');
width -= margin.left + margin.right;
height -= margin.top + margin.bottom;
const g = svg.append('g')
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
const gAxis = g.append('g')
.classed('axis', true);
updateData = function () {
const maxValue = _(data).values().map(e => _(e).values().value()).map(e => _(e).max()).max();
const x = d3.scaleLinear()
.domain([0, 23])
.range([0, width])
;
const y = d3.scaleLinear()
.domain([0, d3.keys(data).length])
.range([0, height])
;
const r = d3.scaleSqrt()
.domain([1, maxValue])
.range([3, 11])
;
let rows = g.selectAll('g.row').data(d3.entries(data), d => d.key);
let erows = rows.enter().append('g')
.classed('row', true)
.attr('opacity', 1)
erows
.append('text')
.attr('x', -10)
.attr('y', 3)
.attr('text-anchor', 'end')
.text(d => d.key);
rows.exit().remove();
rows = erows.merge(rows);
rows.transition().duration(25).attr('transform', (d, i) => `translate(0, ${y(i)})`);
const circles = rows.selectAll('circle.punch').data(d => d3.entries(d.value), d => d.key);
circles.enter().append('circle')
.classed('punch', true)
.attr('cx', d => x(+d.key))
.attr('fill', 'orange')
.attr('r', 0)
.on("mouseover", function (d) {
tooltip.transition()
.duration(200)
.style("opacity", .9);
tooltip.html(d.value);
// We calculate the bounding rects after setting the html
let rect = d3.select(this).node().getBoundingClientRect();
let t_rect = tooltip.node().getBoundingClientRect();
tooltip
.style("left", (rect.left + rect.width/2 - t_rect.width/2) + "px")
.style("top", (rect.top - t_rect.height - 5) + "px");
})
.on("mouseout", _ => {
tooltip.transition()
.duration(500)
.style("opacity", 0);
})
.merge(circles)
.transition()
.duration(25)
.attr('r', d => r(d.value))
;
circles.exit().transition()
.attr('r', 0)
.remove();
rows.selectAll('circle.punch').attr('r', d => r(d.value));
const axis = d3.axisBottom(x).ticks(24);
gAxis
.attr('transform', `translate(0, ${y.range()[1]})`)
.call(axis);
}
updateData();
}
chart.data = function (value) {
if (!arguments.length) return data;
data = value;
if (typeof updateData === 'function') updateData();
return chart;
}
return chart;
}
window['punchCard'] = punchCard;
})();