I have made a graph using d3 in JavaScript. The code allows the user to scale the x and y axes separately using pinch gestures within a certain degree from the desired axis. This works well.
Unfortunately, I have an issue where the domains of the axes 'snap' to match each other when any gesture transform is applied like a pinch zoom away from a single axis or a single finger gesture pan. I'd like some help figuring out how to stop this 'snapping' domain behavior. Thanks. Here is the code:
const staticBaseX = d3.scaleLinear().domain([-10, 10]).range([margin, svgWidth - margin]);
const staticBaseY = d3.scaleLinear().domain([-10, 10]).range([svgHeight - margin, margin]);
let baseX = staticBaseX.copy();
let baseY = staticBaseY.copy();
const ANGLE_TOLERANCE = 10; // degrees
const zoomBehavior = d3.zoom()
.filter((event) => {
const t = event.type;
return t === 'wheel'
|| t === 'mousedown'
|| t === 'mousemove'
|| t === 'touchstart'
|| t === 'touchmove';
})
.scaleExtent([1e-6, 1e6])
.on('zoom', (event) => {
const t = event.transform;
const se = event.sourceEvent;
let newX = baseX.domain();
let newY = baseY.domain();
if (se?.touches && se.touches.length === 2) {
// Two-finger pinch gesture — detect angle
const [p1, p2] = se.touches;
const dx = p2.clientX - p1.clientX;
const dy = p2.clientY - p1.clientY;
let angle = Math.abs(Math.atan2(dy, dx)) * 180 / Math.PI;
if (angle > 180) angle -= 180;
const nearH = angle <= ANGLE_TOLERANCE || angle >= 180 - ANGLE_TOLERANCE;
const nearV = angle >= 90 - ANGLE_TOLERANCE && angle <= 90 + ANGLE_TOLERANCE;
if (nearH) {
// Only update X, keep Y fixed
newX = t.rescaleX(staticBaseX).domain();
} else if (nearV) {
// Only update Y, keep X fixed
newY = t.rescaleY(staticBaseY).domain();
} else {
// Diagonal: update both
newX = t.rescaleX(staticBaseX).domain();
newY = t.rescaleY(staticBaseY).domain();
}
} else {
// Pan, wheel, or single-finger: update both
newX = t.rescaleX(staticBaseX).domain();
newY = t.rescaleY(staticBaseY).domain();
}
// Store the current domains
currentXRange = newX;
currentYRange = newY;
baseX.domain(newX);
baseY.domain(newY);
plotGraph(currentExpr, currentXRange, currentYRange, true);
});
plotGraph is just a function to update the graph to the user.
javascript(and appropriate names for other languages) to the opening fence line, for example:~~~javascript.