I am trying to find out if it is possible to update a data source based on a particular d3 interpolatior, a notebook here.
Before I elaborate on the problem, I don’t know why the observable notebook does not render the div
created at last. The original html
renders like following in the browser
I am running an interpolator to animate the X axis which is .xAxis>.xAxisBottom>.domain
. Now, while this interpolation takes place, can I also update the data source based on the exact same interpolator, which is src2
to capture each change in year, that is const invertVal = scaleX.invert(valX);
witihin src2
?
To elaborate, I have built 6 div
one for each category and I can use the exact same interpolator
as X-Axis
to display tweened
text per div. But I was wondering with the interpolator running, how can I keep on updating src2
based on the same interpolator?
I have tried by passing src2.map((a, i) => (a.year = invertVal))
which does not work.
The motivation behind this is to create an enter-update
animation and I came across post1, post2, post3. d3 can update the animation when source data updates and I want to update the source data using this interpolator.
But here, the data source is not updated using any interpolator that had already been used to animate any existing element.
But if I choose to use the interpolator already in play, how do I update the data based on that?
Thank you in advance.
The full code is below
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<script type="text/javascript" src="https://d3js.org/d3.v7.min.js"></script>
<body>
<svg></svg>
</body>
<script type="text/javascript">
const data = [{
Category: "Admin",
Year: 2012,
Value: 0.43
}, {
Category: "Admin",
Year: 2013,
Value: 0.396
}, {
Category: "Admin",
Year: 2014,
Value: 0.318
}, {
Category: "Admin",
Year: 2015,
Value: 0.253
}, {
Category: "Admin",
Year: 2016,
Value: 0.096
}, {
Category: "Admin",
Year: 2017,
Value: 0.07
}, {
Category: "Admin",
Year: 2018,
Value: 0.055
}, {
Category: "Admin",
Year: 2019,
Value: 0.0218
}, {
Category: "Admin",
Year: 2020,
Value: 0.0172
}, {
Category: "Admin",
Year: 2021,
Value: 0.018600000000000002
}, {
Category: "Admin",
Year: 2022,
Value: 0.0166
}, {
Category: "Tax",
Year: 2012,
Value: 0.0069999999999999993
}, {
Category: "Tax",
Year: 2013,
Value: 0.012
}, {
Category: "Tax",
Year: 2014,
Value: 0.025
}, {
Category: "Tax",
Year: 2015,
Value: 0.038
}, {
Category: "Tax",
Year: 2016,
Value: 0.049
}, {
Category: "Tax",
Year: 2017,
Value: 0.05
}, {
Category: "Tax",
Year: 2018,
Value: 0.06
}, {
Category: "Tax",
Year: 2019,
Value: 0.0663
}, {
Category: "Tax",
Year: 2020,
Value: 0.0766
}, {
Category: "Tax",
Year: 2021,
Value: 0.077800000000000008
}, {
Category: "Tax",
Year: 2022,
Value: 0.0639
}, {
Category: "Depriciation",
Year: 2012,
Value: 0.039
}, {
Category: "Depriciation",
Year: 2013,
Value: 0.059000000000000004
}, {
Category: "Depriciation",
Year: 2014,
Value: 0.07
}, {
Category: "Depriciation",
Year: 2015,
Value: 0.08
}, {
Category: "Depriciation",
Year: 2016,
Value: 0.076
}, {
Category: "Depriciation",
Year: 2017,
Value: 0.11900000000000001
}, {
Category: "Depriciation",
Year: 2018,
Value: 0.139
}, {
Category: "Depriciation",
Year: 2019,
Value: 0.17079999999999998
}, {
Category: "Depriciation",
Year: 2020,
Value: 0.21559999999999999
}, {
Category: "Depriciation",
Year: 2021,
Value: 0.1834
}, {
Category: "Depriciation",
Year: 2022,
Value: 0.22579999999999997
}, {
Category: "Amortization",
Year: 2012,
Value: 0.004
}, {
Category: "Amortization",
Year: 2013,
Value: 0.008
}, {
Category: "Amortization",
Year: 2014,
Value: 0.016
}, {
Category: "Amortization",
Year: 2015,
Value: 0.025
}, {
Category: "Amortization",
Year: 2016,
Value: 0.036000000000000004
}, {
Category: "Amortization",
Year: 2017,
Value: 0.036000000000000004
}, {
Category: "Amortization",
Year: 2018,
Value: 0.039
}, {
Category: "Amortization",
Year: 2019,
Value: 0.0408
}, {
Category: "Amortization",
Year: 2020,
Value: 0.044800000000000006
}, {
Category: "Amortization",
Year: 2021,
Value: 0.039599999999999996
}, {
Category: "Amortization",
Year: 2022,
Value: 0.0437
}, {
Category: "Fee",
Year: 2012,
Value: 0.26
}, {
Category: "Fee",
Year: 2013,
Value: 0.252
}, {
Category: "Fee",
Year: 2014,
Value: 0.285
}, {
Category: "Fee",
Year: 2015,
Value: 0.287
}, {
Category: "Fee",
Year: 2016,
Value: 0.43700000000000006
}, {
Category: "Fee",
Year: 2017,
Value: 0.415
}, {
Category: "Fee",
Year: 2018,
Value: 0.406
}, {
Category: "Fee",
Year: 2019,
Value: 0.4205
}, {
Category: "Fee",
Year: 2020,
Value: 0.3736
}, {
Category: "Fee",
Year: 2021,
Value: 0.3997
}, {
Category: "Fee",
Year: 2022,
Value: 0.39990000000000003
}, {
Category: "Interest",
Year: 2012,
Value: 0.207
}, {
Category: "Interest",
Year: 2013,
Value: 0.20800000000000002
}, {
Category: "Interest",
Year: 2014,
Value: 0.19699999999999998
}, {
Category: "Interest",
Year: 2015,
Value: 0.22399999999999998
}, {
Category: "Interest",
Year: 2016,
Value: 0.22949999999999998
}, {
Category: "Interest",
Year: 2017,
Value: 0.2289
}, {
Category: "Interest",
Year: 2018,
Value: 0.213
}, {
Category: "Interest",
Year: 2019,
Value: 0.19440000000000002
}, {
Category: "Interest",
Year: 2020,
Value: 0.1867
}, {
Category: "Interest",
Year: 2021,
Value: 0.1623
}, {
Category: "Interest",
Year: 2022,
Value: 0.174
}];
////////////////////////////////////////////////////////////
//////////////////////// 0 USER SELECTION //////////////////
////////////////////////////////////////////////////////////
const isYGridLine = "n";
const isXGridLine = "n";
const xAxisTickDefault = "9";
const yAxisTickDefault = "-9";
////////////////////////////////////////////////////////////
//////////////////////// 1 DATA WRANGLING //////////////////
////////////////////////////////////////////////////////////
const xAccessor = (d) => d.Year;
const yAccessor = (d) => d.Value;
const zAccessor = (d) => d.Category;
//get all the unique categories
const cat = [...new Set(data.map(zAccessor))];
const year = [...new Set(data.map(xAccessor))];
const count = year.length - 1;
const maxYear = Math.max(...year);
console.log(cat);
//const yr1 = [1951, 1961, 1971, 1981, 1991, 2001];
const src2 = cat.map((d, i) => {
return {
cat: d,
year: null,
val: null
};
});
////////////////////////////////////////////////////////////
//////////////////////// 2 CREATE SVG //////////////////////
////////////////////////////////////////////////////////////
//namespace
//define dimension
const width = 1536;
const height = 720;
const svgns = "http://www.w3.org/2000/svg";
const svg = d3.select("svg");
svg.attr("xmlns", svgns).attr("viewBox", `0 0 ${width} ${height}`);
svg.append("rect").attr("class", "vBoxRect")
//.style("overflow", "visible")
.attr("width", `${width}`).attr("height", `${height}`).attr("stroke", "black").attr("fill", "white");
////////////////////////////////////////////////////////////
//////////////////////// 3 CREATE BOUND ////////////////////
////////////////////////////////////////////////////////////
const padding = {
top: 70,
bottom: 100,
left: 120,
right: 120
};
const multiplierH = 1; //controls the height of the visual container
const multiplierW = 1; //controls the width of the visual container
const boundHeight = height * multiplierH - padding.top - padding.bottom;
const boundWidth = width * multiplierW - padding.right - padding.left;
//create BOUND rect -- to be deleted later
svg.append("rect").attr("class", "boundRect").attr("x", `${padding.left}`).attr("y", `${padding.top}`).attr("width", `${boundWidth}`).attr("height", `${boundHeight}`).attr("fill", "white");
//create bound element
const bound = svg.append("g").attr("class", "bound")
//specify transform, must be .style and not .attr, px needs to be mentioned
.style("transform", `translate(${padding.left}px,${padding.top}px)`);
////////////////////////////////////////////////////////////
//////////////////////// 4 CREATE SCALE ////////////////////
////////////////////////////////////////////////////////////
//scale converts a domain (data) to range (pixel)
const scaleX = d3.scaleLinear().range([0, boundWidth]).domain(d3.extent(data, xAccessor));
const scaleY = d3.scaleLinear().range([boundHeight, 0]).domain(d3.extent(data, yAccessor));
////////////////////////////////////////////////////////////
//////////////////////// 5 CREATE AXIS/ ////////////////////
////////////////////////////////////////////////////////////
//X Axis
bound.append("g").attr("class", "xAxis").append("g").attr("class", "xAxisBottom").call(d3.axisBottom(scaleX).ticks(count) //forces all the years on the x axis
.tickSizeOuter(0) //removes ticksize
.tickFormat(d3.format("d")) //tick format integer
).call((d) => isXGridLine == "n" ? d3.selectAll(".xAxisBottom>.tick>line").remove() : null).style("transform", `translateY(${boundHeight}px)`);
//formatting parent, remove unnecessary property from there
d3.select(".xAxisBottom").attr("font-size", null).attr("font-family", null).attr("text-anchor", null);
d3.select(".xAxisBottom>path").attr("stroke", "black").style("stroke-width", "2")
//.style('opacity', '.2')
.style("stroke-miterlimit", "10");
d3.selectAll(".xAxisBottom>.tick>text").style("text-anchor", function(d, i) {
return i === 0 ? "start" : i === count ? "end" : "middle";
}).attr("y", `${xAxisTickDefault}`);
//////////////////////// 5a X AXIS ANIMATION/ ////////////////////
/*animate X Axis - global*/
const duration1 = 10000;
/*Animate X AXIS*/
d3.select(".xAxis>.xAxisBottom>.domain").transition().duration(`${duration1}`).ease(d3.easeQuadIn).attrTween("stroke-dasharray", function() {
const selection = this;
const length = selection.getTotalLength();
const start = 0;
const finish = 1;
const interpolator = d3.interpolate(start, finish);
return function(t) {
const pct = interpolator(t);
const val = selection.getPointAtLength(length * pct);
const valX = val.x;
const invertVal = scaleX.invert(valX);
return `${valX} ${length}`;
};
});
// construct div
d3.select("body").append("div").classed("exp", true).selectAll("div").data(src2).join("div").attr("class", (d, i) => {
return d.cat;
}).transition().duration(`${duration1}`).textTween(function(d, i) {
const selection = d3.select(".xAxis>.xAxisBottom>.domain");
const node = selection.node();
const length = node.getTotalLength();
const start = 0;
const finish = 1;
const interpolator = d3.interpolate(start, finish);
return function(t) {
const pct = interpolator(t);
const val = node.getPointAtLength(length * pct);
const valX = val.x;
const invertVal = scaleX.invert(valX);
//src2.map((a, i) => (a.year = invertVal));
//console.log(src2);
return `${d.cat}` + ":-" + invertVal;
//console.log(d);
};
});
</script>
</html> ```