{
const svg = d3
.create("svg")
.attr("width", (width + margin.left + margin.right) * 6 + margin.right)
.attr("height", (height + margin.top + margin.bottom) * 4 + margin.bottom)
.attr(
"style",
"max-width: 100%; height: auto; font-size: 10px; font-family: 'Inter', sans-serif;"
);
d3.select("#startAnimationButton").on("click", function () {
startAnimation();
});
const defs = svg.append("defs");
defs
.append("linearGradient")
.attr("id", "gradientBlue")
.attr("gradientTransform", "rotate(0)")
.selectAll("stop")
.data([
{ offset: "25%", color: "white", opacity: "0" },
{ offset: "100%", color: "#3f6bee", opacity: "0.7" }
])
.enter()
.append("stop")
.attr("offset", (d) => d.offset)
.attr("stop-color", (d) => d.color)
.attr("stop-opacity", (d) => d.opacity);
defs
.append("linearGradient")
.attr("id", "gradientRed")
.attr("gradientTransform", "rotate(0)")
.selectAll("stop")
.data([
{ offset: "25%", color: "white", opacity: "0" },
{ offset: "100%", color: "#e61f00", opacity: "0.7" }
])
.enter()
.append("stop")
.attr("offset", (d) => d.offset)
.attr("stop-color", (d) => d.color)
.attr("stop-opacity", (d) => d.opacity);
// Setup the scale for the x-axis
const xScale = d3
.scaleTime()
.domain(d3.extent(data, (d) => d.year)) // Assuming 'year' is already a Date object
.range([0, width]);
// Function to start the animation on SVG click
function startAnimation() {
// Group data by labels to create facets
const nestedData = d3.group(data, (d) => d.labels);
const facets = svg
.selectAll("g")
.data(nestedData)
.enter()
.append("g")
.attr("opacity", 0)
.attr(
"transform",
(d, i) =>
`translate(${
(i % 6) * (width + margin.left + margin.right) + margin.left
},${
Math.floor(i / 6) * (height + margin.top + margin.bottom) +
margin.top
})`
);
let surfacetempLine;
// For each facet, sort appearance and animation
facets.each(function (d, i) {
const [label, values] = d;
// Create local y-scale for each facet
const yScale = d3
.scaleLinear()
.domain(d3.extent(values, (v) => v.values))
.range([height, 0])
.nice();
const line1950 = d3
.select(this)
.append("line") // Append line to each facet group
.attr("x1", xScale(new Date("1950")))
.attr("x2", xScale(new Date("1950")))
.attr("y1", 0)
.attr("y2", height)
.attr("stroke", "gray")
.attr("stroke-width", 1)
.attr("stroke-dasharray", "5,5")
.attr("opacity", 0);
// After setting up the axes
if (label === "Surface Temperature") {
surfacetempLine = d3
.select(this)
.append("line")
.attr("x1", 0)
.attr("x2", width)
.attr("y1", yScale(0))
.attr("y2", yScale(0))
.attr("stroke", "black")
.attr("stroke-width", 1)
// .attr("stroke-dasharray", "5,5")
.attr("opacity", 0);
}
d3.select(this)
.transition()
.duration(animationInterval * 0.2)
.delay(animationInterval * i)
.attr("opacity", 1)
.on("end", function () {
// Append y-axis for this facet and set initial opacity to 0
const yAxis = d3
.select(this)
.append("g")
.attr("opacity", 0)
.call(d3.axisLeft(yScale).tickSize(0).tickPadding(6).ticks(4));
// Append x-axis and set initial opacity to 0, with specific ticks
const xAxis = d3
.select(this)
.append("g")
.attr("opacity", 0)
.attr("transform", `translate(0,${height})`)
.call(d3.axisBottom(xScale).tickValues(specificTicks));
// Conditionally remove the x-axis line
if (yScale.domain()[0] !== 0) {
xAxis.select(".domain").remove();
}
// Fade in both axes
yAxis.transition().duration(500).attr("opacity", 1);
xAxis.transition().duration(500).attr("opacity", 1);
surfacetempLine
.transition()
.duration(animationInterval * 1.2)
.attr("opacity", 1);
line1950
.transition()
.duration(animationInterval * 1.2)
.attr("opacity", 1);
// Add grid lines for the y-axis
d3.select(this)
.selectAll(".grid-line")
.data(yScale.ticks(4))
.enter()
.append("line")
.attr("class", "grid-line")
.attr("x1", 0)
.attr("x2", width)
.attr("y1", (d) => yScale(d))
.attr("y2", (d) => yScale(d))
.attr("stroke", "#ccc")
.attr("stroke-width", 1)
.attr("opacity", 0)
.transition()
.duration(500)
.attr("opacity", 0.5);
// Define the area generator for shading under the line
const areaGenerator = d3
.area()
.x((d) => xScale(d.year))
.y0(height)
.y1((d) => yScale(d.values))
.curve(d3.curveNatural);
// Draw the area beneath the line
const area = d3
.select(this)
.append("path")
.datum(values)
.attr("fill", function (d) {
return d[0].trend_type === "Socio-economic Trends"
? "url(#gradientBlue)"
: "url(#gradientRed)";
})
.attr("opacity", 0) // Set initial opacity to 0
.attr("d", areaGenerator);
// Animate the area drawing
area
.transition()
.duration(animationInterval * 1.2) // Sync animation with line
.attr("opacity", 1); // Fade in the area
const whitePath = d3
.select(this)
.append("path")
.datum(values)
.attr("fill", "none")
.attr("stroke", "white")
.attr("stroke-width", 3)
.attr(
"d",
d3
.line()
.x((d) => xScale(d.year))
.y((d) => yScale(d.values))
.curve(d3.curveNatural)
)
.attr("stroke-dasharray", function () {
return `${this.getTotalLength()} ${this.getTotalLength()}`;
})
.attr("stroke-dashoffset", function () {
return this.getTotalLength();
});
whitePath
.transition()
.duration(animationInterval * 0.8) // Draw line over the same interval
.attr("stroke-dashoffset", 0);
// Draw and animate the line
const path = d3
.select(this)
.append("path")
.datum(values)
.attr("fill", "none")
//.attr("stroke", "steelblue")
.attr(
"stroke",
values[0].trend_type === "Socio-economic Trends"
? "#3f6bee"
: "#e61f00"
)
.attr("stroke-width", 1.5)
.attr(
"d",
d3
.line()
.x((d) => xScale(d.year))
.y((d) => yScale(d.values))
.curve(d3.curveNatural)
)
.attr("stroke-dasharray", function () {
return `${this.getTotalLength()} ${this.getTotalLength()}`;
})
.attr("stroke-dashoffset", function () {
return this.getTotalLength();
});
// Sync the line animation with the area animation
path
.transition()
.duration(animationInterval * 0.8) // Draw line over the same interval
.attr("stroke-dashoffset", 0);
// Add the label to the top-left of the facet
const labelFade = d3
.select(this)
.append("text")
.attr("x", 2.5)
.attr("y", 4)
.attr("font-size", "13.5px")
.attr("font-weight", "bold")
.style(
"text-shadow",
"-1px -1px 0 #fff, 1px -1px 0 #fff, -1px 1px 0 #fff, 1px 1px 0 #fff"
)
.text(label)
.call(wrapText, 10);
labelFade.transition().duration(500).attr("opacity", 1);
// Add the Y axis labels
const yLabel = d3
.select(this)
.append("text")
.attr("x", -20)
.attr("y", -12)
.text(values[0].yAxisLabels);
yLabel.transition().duration(500).attr("opacity", 1);
});
});
}
return svg.node();
}