im trying to get in touch with the D3 library in my React project and so far so good. But i have one problem: i want to animate a graph/line step by step as defined in the filteredData depending on the CurrentStepIndex, which is triggerd by the Scrollama Library with .
useEffect(() => {
const svg = d3.select(svgRef.current).select("svg").remove();
const observer = new IntersectionObserver((entries) => {
const entry = entries[0];
setMyGraphIsVisible(entry.isIntersecting);
console.log(myGraphIsVisible);
});
observer.observe(svgRef.current);
const svgContainer = d3
.select(svgRef.current)
.append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", `translate(${margin.left},${margin.top})`); //svg sits nice and coozy in the container
const tooltip = svgContainer
.append("foreignObject")
.attr("class", "tooltip")
.style("display", "none") // Tooltip zunächst ausblenden
.attr("width", 80) // Breite des Tooltip-Bereichs
.attr("height", 60); // Höhe des Tooltip-Bereichs
const tooltipContent = tooltip
.append("xhtml:div")
.attr("class", "tooltip-content") // Klasse für Styling-Zwecke
.style("background-color", "steelblue")
.style("color", "white")
.style("border-radius", "5px")
.style("font-size", "12px");
const x = d3
.scaleLinear()
.domain([0, d3.max(data2, (d) => d.year)])
.range([0, width]);
const y = d3.scaleLinear().domain([0, 100]).range([height, 0]);
svgContainer
.append("g")
.attr("transform", `translate(0,${height})`)
.call(d3.axisBottom(x).ticks(9).tickFormat(d3.format("d")));
svgContainer.append("g").call(d3.axisLeft(y)).attr("class", "y-axis");
svgContainer
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 0 - margin.left)
.attr("x", 0 - height / 2)
.attr("dy", "1em")
.style("text-anchor", "middle")
.style("font-size", "14px")
.style("fill", "#777")
.style("font-family", "sans-serif")
.text("Erwerbsquote in %");
svgContainer
.append("text")
.attr("class", "source-credit")
.attr("x", width - 300)
.attr("y", height + margin.bottom)
.style("font-size", "9px")
.style("font-family", "sans-serif")
.style("fill", "#777")
.text("Quelle: https://doku.iab.de/kurzber/2024/kb2024-10.pdf");
svgContainer
.selectAll("xGrid")
.data(x.ticks().slice(1))
.join("line")
.attr("x1", (d) => x(d))
.attr("x2", (d) => x(d))
.attr("y1", 0)
.attr("y2", height)
.attr("stroke", "#e0e0e0")
.attr("stroke-width", 0.1);
// Add horizontal gridlines
svgContainer
.selectAll("yGrid")
.data(y.ticks().slice(1))
.join("line")
.attr("x1", 0)
.attr("x2", width)
.attr("y1", (d) => y(d))
.attr("y2", (d) => y(d))
.attr("stroke", "#e0e0e0")
.attr("stroke-width", 0.1);
const line = d3
.line()
.x((d) => x(d.year))
.y((d) => y(d.number));
const filteredData =
currentStepIndex === 1
? data2.slice(0, 4)
: currentStepIndex === 2
? data2.slice(0, 7)
: currentStepIndex === 3
? data2.slice(0, 9)
: data2.slice(0, 0);
const path = svgContainer
.append("path")
.datum(filteredData)
.attr("fill", "none")
.attr("stroke", "yellow")
.attr("stroke-width", 1.5)
.attr("d", line);
const totalLength = path.node().getTotalLength();
path
.attr("stroke-dasharray", totalLength)
.attr("stroke-dashoffset", totalLength)
.transition()
.duration(2000)
.attr("stroke-dashoffset", 0);
svg.selectAll("rect").remove(); // Remove any existing rectangles
if (currentStepIndex === 4) {
svgContainer
.append("path")
.datum(data2)
.attr("fill", "none")
.attr("stroke", "yellow")
.attr("stroke-width", 1.5)
.attr("d", line);
const path2 = svgContainer
.append("path")
.datum(data3)
.attr("fill", "none")
.attr("stroke", "white")
.attr("stroke-width", 1.5)
.attr("d", line);
const totalLength2 = path2.node().getTotalLength();
path2
.attr("stroke-dasharray", totalLength2)
.attr("stroke-dashoffset", totalLength2)
.transition()
.duration(2000)
.attr("stroke-dashoffset", 0);
}
});
}, [myGraphIsVisible, currentStepIndex]);
Data:
const data2 = [
{ year: 0, number: 0 },
{ year: 1, number: 9 },
{ year: 2, number: 26 },
{ year: 3, number: 44 },
{ year: 4, number: 51 },
{ year: 5, number: 60 },
{ year: 6, number: 70 },
{ year: 7, number: 75 },
{ year: 8, number: 86 },
];
const data3 = [
{ year: 0, number: 77.6 },
{ year: 1, number: 77.7 },
{ year: 2, number: 77.8 },
{ year: 3, number: 77.7 },
{ year: 4, number: 78.0 },
{ year: 5, number: 78.9 },
{ year: 6, number: 79.6 },
{ year: 7, number: 80.5 },
{ year: 8, number: 79.0 },
];
I know its saying in the first line
const svg = d3.select(svgRef.current).select("svg").remove();
which cause the issue, but if i get rid of this line it will render each Step a new SVG.
I tried different things, like an if clause where im asking if there is an existing SVG, but then my SVG is not working properly, like “Uncaught TypeError: Cannot read properties of undefined (reading ’empty’)”
Any suggestion to solve this Problem?
Thanks in Advance!