When using the zoom component I would like to apply the transform matrix to my axis so that they represent the new visible domain. How would I go about this using vx?
// Scales aren't necessarily linear and time
const xScale = scaleTime({
range: [0, xMax],
domain: extent(data, x)
});
const yScale = scaleLinear({
range: [yMax, 0],
domain: [0, max(data, y)]
});
<Zoom
width={width}
height={height}
scaleXMin={1}
scaleXMax={4}
scaleYMin={1}
scaleYMax={4}
transformMatrix={initialTransform}
>
{zoom =>
<svg width={width} height={height}>
<Group>
<Group transform={zoom.toString()}>
{renderLine()} // Code rendering actual graph that can be zoomed
</Group>
// Axis live outside group with zoom transform
<AxisLeft
scale={???} // What goes here?
top={0}
left={0}
/>
<AxisBottom
scale={???} // What goes here?
top={yMax}
/>
</Group>
}
</Zoom>
Anyone made progress on this?
@s-hood I also could not think of a way to do this with zoom, so instead I used drag to draw a rectangle
over a part of my chart, subset the data to that part and then re-rendered. It's less dynamic than zooming but is good enough for my use case.
I have managed to do this with a linear scale, but struggling with time scale:
const yScale = scaleLinear({
range: [yMax, 0],
domain: [0, max(data, y)]
});
<AxisLeft
scale={scaleLinear({
range: [yMax, 0],
domain: [
yScale.invert((yMax - zoom.transformMatrix.translateY) / zoom.transformMatrix.scaleY), (max(data, y) / zoom.transformMatrix.scaleY) + yScale.invert((yMax - zoom.transformMatrix.translateY) / zoom.transformMatrix.scaleY),
],
})}
top={0}
left={0}
/>
Correction, this is working for me for both linear and time. The trick was just to convert everything to pixel values, apply the transforms, then convert back to the domain.
const xScaleTransformed = scaleTime({
range: [0, xMax],
domain: [
xScale.invert((xScale(extent(data, x)[0]) - zoom.transformMatrix.translateX) / zoom.transformMatrix.scaleX),
xScale.invert((xScale(extent(data, x)[1]) - zoom.transformMatrix.translateX) / zoom.transformMatrix.scaleX),
],
});
const yScaleTransformed = scaleLinear({
range: [yMax, 0],
domain: [
yScale.invert((yScale(0) - zoom.transformMatrix.translateY) / zoom.transformMatrix.scaleY),
yScale.invert((yScale(max(data, y)) - zoom.transformMatrix.translateY) / zoom.transformMatrix.scaleY),
],
});
Yup, here is a method I'm using to scale my Axis based on the zoom.
Its all about updating the domain to what is now visible due to the zoom area.
const rescaleYAxis = (scale, zoom) => {
let newDomain = scale.range().map((r) => {
return scale.invert((r - zoom.transformMatrix.translateY)/zoom.transformMatrix.scaleY)
})
return scale.copy().domain(newDomain)
}
@sseira Trying to figure this stuff out myself now. Where would you call rescaleYAxis from?
Most helpful comment
Correction, this is working for me for both linear and time. The trick was just to convert everything to pixel values, apply the transforms, then convert back to the domain.