chart = () => {
const wrapper = d3.create("div")
.style("font-family", franklinLight)
.style("pointer-events", "none");
wrapper.append("style").html(css);
const chart = wrapper.selectAll(".chart")
.data(data)
.join("div")
.attr("class", "div")
.style("display", "inline-block")
.style("margin-left", (d, i) => width <= breakpoint ? 0 : i == 0 ? 0 : "24px")
.style("margin-right", (d, i) => width <= breakpoint ? 0 : i === 0 ? "24px" : 0)
.style("margin-bottom", (d, i) => width <= breakpoint && i === 0 ? "24px" : 0)
.style("width", `${basewidth}px)`);
chart.append("div")
.style("margin-bottom", "16px")
.text(d => d.type === "leaf" ? "Leaf appearance" : "Temperature, January to May")
const svg = chart.append("svg")
.attr("width", chartwidth + margin.left + margin.right)
.attr("height", chartheight + margin.top + margin.bottom)
.style("overflow", "visible");
const g = svg.append("g")
.attr("transform", `translate(${[margin.left, margin.top]})`);
g.append("rect")
.attr("x", -r)
.attr("width", chartwidth + (r * 2))
.attr("height", chartheight)
.style("pointer-events", "all")
.style("fill", "white")
.on("mousemove", (ev) => {
const year = Math.round(x.invert(ev.offsetX - margin.left));
tipLabel.style("display", d => d.year === year ? "block" : "none");
})
.on("mouseout", () => {
tipLabel.style("display", (d, i, e) => i === e.length - 1 ? "block" : "none");
});
g.append("g").call(xAxis);
g.append("g")
.each((d, i, e) => {
d3.select(e[i]).call(g => d.yAxisGenerator(g, "line"));
});
g.selectAll("circle")
.data(d => d.data)
.join("circle")
.attr("fill", d => d.type === "leaf" ? colorScale(d.anomaly) : "#fff")
.attr("stroke", d => d.type === "leaf" ? d3.color(colorScale(d.anomaly)).darker() : "#767676")
.attr("cx", d => x(d.year))
.attr("cy", d => data.find(d0 => d0.type === d.type).y(d.anomaly))
.attr("r", r);
g.append("g")
.each((d, i, e) => {
d3.select(e[i]).call(g => d.yAxisGenerator(g, "text"));
});
g.append("path")
.attr("fill", "none")
.attr("stroke", "black")
.attr("stroke-width", 2)
.attr("d", d => d.line(d.loess));
// Labels
const tipLabel = g.selectAll(".tip-label")
.data(d => d.data)
.join("g")
.attr("class", "tip-label")
.attr("transform", d => `translate(${[ x(d.year), data.find(d0 => d.type === d0.type).y(d.anomaly) ]})`)
.style("display", (d, i, e) => i === e.length - 1 ? "block" : "none");
tipLabel.append("circle")
.attr("r", r);
tipLabel.append("polyline")
.attr("points", [[0, -r], [0, -r - 5]]);
const tipLabelText = tipLabel.append("g")
.attr("class", "label-text-g")
.attr("text-anchor", d => d.year >= 2010 ? "end" : d.year <= 1930 ? "start" : "middle")
.attr("transform", d => `translate(${d.year >= 2010 ? 5 : d.year <= 1930 ? -5 : 0}, -28)`)
tipLabelText.append("text")
.attr("class", "year")
.text(d => d.year);
tipLabelText.append("text")
.attr("y", 14)
.text(d => {
const abs = Math.round(Math.abs(d.anomaly));
if (d.type === "leaf") {
return abs === 0 ? "About average" : `${abs} day${abs === 1 ? "" : "s"} ${d.anomaly < 0 ? " early" : "late"}`
}
else {
return abs === 0 ? "About average" : `${abs}${d.anomaly < 0 ? "°F colder" : "°F warmer"}`
}
});
return wrapper.node();
}