I have a path element that I am updating with new data. It is then repositioned and also reflected. All of that is working correctly. The part that is not working correctly is the transition. Due to the way I am reflecting (using scale and translate), it moves past the correct position before returning.
How can I transition from the initial x-axis position to the new x-axis position without moving past the new position?
HTML:
<button id="reflect">reflect</button>
<div id="container"></div>
JS:
import * as d3 from "https://cdn.jsdelivr.net/npm/d3@7/+esm";
const width = 500;
const height = 200;
const x = d3.scaleLinear()
.domain([0, 100])
.range([0, width]);
const y = d3.scaleLinear()
.domain([0, 50])
.range([height, 0]);
const svg = d3.create("svg")
.attr("width", width)
.attr("height", height);
svg.append("g")
.attr("transform", `translate(0,${height})`)
.call(d3.axisBottom(x));
svg.append("g")
.call(d3.axisLeft(y));
const data = [{
x: 60,
y: 20
}];
const reflectedData = [{
x: 30,
y: 20
}];
svg.selectAll('arrow')
.data(data)
.join('path')
.attr('class', 'arrow')
.attr('d', 'M0,0 L80,0 L80, 50z')
d3.select('#reflect')
.on('click', () => {
svg.selectAll('.arrow')
.data(reflectedData)
updateArrow(2000)
})
updateArrow(0);
function updateArrow(duration) {
const midpoint = x.domain()[1] / 2
svg.selectAll('.arrow')
.transition()
.duration(duration)
.style('scale', d => d.x < midpoint ? '-1 1' : '1 1')
.style('transform', d => {
const translateX = d.x < midpoint ? -x(d.x) : x(d.x);
return `translate(${translateX}px, ${y(d.y)}px)`;
})
}
container.append(svg.node());
Two problems:
transformdoesn't really play nice with other transform-like attributes likescale. (At least, it's very hard to reason about.) Much easier to just do everything intransform.d.x < midpoint; only the scale should.The triangle flips over the vertical as it moves from right to left.