Published
Edited
May 8, 2020
Importers
1 star
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
function makeChart({
data,
format,
color,
colTitle,
title,
type_ = "Display",
colorScale,
pointer,
xBuffer = 175 / 954
}) {
const barHeight = (35 / 954) * width;

const titleSize = pxToVw(16.5);

const header = type_ === "Display" ? "Zone" : "Team";
const dataWithZone = [{ name: header, value: 0 }].concat(data);

const height =
Math.ceil((data.length + 0.1) * barHeight) + margin.top + margin.bottom;

const x = d3
.scaleLinear()
.domain(d3.extent(dataWithZone, d => d.value))
.rangeRound([margin.left + xBuffer * width, width - margin.right]);

const svg = d3.create("svg").attr("viewBox", [0, 0, width, height]);

function fillFunction(d) {
if (colorScale) return colorScale(d.value);
if (type_ === "Display") return color;
else return d.name.includes("Bucks") ? "darkgreen" : "#94ae83";
}

const y = d3
.scaleBand()
.domain(d3.range(dataWithZone.length))
.range([margin.top, height - margin.bottom])
.paddingInner(.4);

const yAxis = g =>
g
.attr("transform", `translate(${margin.left},0)`)
.call(
d3
.axisLeft(y)
.tickSize(0)
.tickFormat(i => dataWithZone[i].name)
)
.selectAll("text")
.attr("dy", "0.785em")
.attr("text-anchor", "start");

const rect = svg
.append("g")
.selectAll("rect")
.data(dataWithZone)
.join("rect")
.attr("fill", d => fillFunction(d))
.attr("x", d => x(Math.min(d.value, 0)))
.attr("y", (_, i) => y(i) + (y.step() - y.bandwidth()) / 2)
.attr("width", d => Math.abs(x(d.value) - x(0)))
.attr("height", y.bandwidth())
.attr("id", d => d.name)
.attr("opacity", ".8")
.attr("style", `cursor: ${pointer}`);

svg
.append("g")
.attr("font-family", "sans-serif")
.selectAll("text")
.data(dataWithZone)
.join("text")
.attr("text-anchor", d => (d.value < 0 ? "end" : "start"))
.attr("x", (d, i) => {
if (i === 0) return x(0) + 1;
return x(d.value) + Math.sign(d.value - 0) * 4;
})
.attr("y", (d, i) => y(i) + y.step() / 2)
.attr("dy", "0.35em")
.attr("font-size", (d, i) => (i === 0 ? titleSize : pxToVw(14)))
.attr("font-weight", (d, i) => {
if (i === 0) return "bold";
})
.text((d, i) => {
if (i > 0) return format(d.value);
else return colTitle;
});

svg
.append("g")
.selectAll("line")
.data(dataWithZone)
.join("line")
.attr("x1", x.range()[0] - xBuffer * width)
.attr("x2", x.range()[1])
.attr("y1", (d, i) => y(i) + y.step())
.attr("y2", (d, i) => y(i) + y.step())
.attr("style", "stroke: lightgrey; stroke-width: 1.5px; opacity: .25");

const zeroValue = d3.line()([
[x(0), y(0) + y.step()],
[x(0), y(dataWithZone.length - 1) + y.step()]
]);
svg
.append("g")
.append("path")
.attr("d", zeroValue)
.attr("style", "stroke: lightgrey; stroke-width: 2.5px");

svg
.append("g")
.call(yAxis)
.call(g => g.select(".domain").remove())
.selectAll("text")
.attr("font-size", (d, i) => (i === 0 ? titleSize : pxToVw(15)))
.attr("font-weight", (d, i) => {
if (i === 0) return "bold";
});

const chart = svg.node();

let titleDiv = `<div/>`;
if (title)
titleDiv = `<div style="text-align: center; font-family: sans-serif; font-size: ${pxToVw(
16.5
)}; font-weight: 700" >${title}</div>`;

const container = html`
${titleDiv}
${chart}
<figcaption style="margin-top: 5px">Data from <a href="http://www.stats.nba.com">stats.nba.com</href></figcaption>
</div>`;
container.value = null;

rect
.on('mouseover', function() {
d3.select(this).attr('opacity', '1');
container.value = d3.select(this).attr("id");
container.dispatchEvent(new CustomEvent("input"));
})
.on('mouseout', function(d, i) {
d3.select(this).attr('opacity', '.8');
container.value = null;
container.dispatchEvent(new CustomEvent("input"));
});

return container;
}
Insert cell
Insert cell
makeChart({
data: [
{ name: "League Average", value: .323 },
{ name: "Milwaukee Bucks", value: .260 }
],
format: d3.format(".1%"),
title: "Percentage of Total Shots Allowed (Restricted Area)",
type_: "Compare",
xBuffer: 120 / width
})
Insert cell
Insert cell
threeOnly
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more