I am using D3 to set up my chart area that makes use of the zoomable behavior. The chart includes both x and y axis.
The chart area should be responsive when window is resized. In this case, I need to reset the x and y axis domain and range on window resizing.
The issue happened with window resizing. I've noticed the zoom focus isn't lined up with the mouse anymore after the following steps:
After that, the above problem happened. See the following jsfiddle which has this issue.
So what's the right way of handing this? I've noticed a couple discussions about this issue such as:
d3 Preserve scale/translate after resetting range
I tried this approach but I couldn't make it working using D3 V4 API.
The main idea is the use the window.addEventListener
to update the plot:
window.addEventListener('resize', () => {
// obtain the current transformation by calling the `d3.zoomTransform` function
const t0 = d3.zoomTransform(
chart.node());
// obtain the client width by evaluating the `div` (class: `zoom-chart`)
width = document.getElementById('zoom-chart').clientWidth;
height = 480;
// update the base scale
baseX.range([0, width])
.interpolate(d3.interpolateNumber);
baseY.range([height, 0])
.interpolate(d3.interpolateNumber);
// update the view port
vb = [0,0, width,height];
chart
.attr('viewBox', vb);
// replot with the original transform
chart.call(zoom.transform, t0);
});
I also added the whole solution here:
const data = [
{x:1 , y:1},
{x:9 , y:1},
{x:9 , y:9},
{x:1 , y:9},
];
var width = document.getElementById('zoom-chart').clientWidth;
var height = 480;
var baseX = d3.scaleLinear().domain([0,10])
.range([0, width])
.interpolate(d3.interpolateNumber);
var baseY = d3.scaleLinear().domain([0,10])
.range([height, 0])
.interpolate(d3.interpolateNumber);
var scaleX = baseX;
var scaleY = baseY;
const zoom = d3.zoom()
.on('zoom', () => {
const transform = d3.event.transform;
scaleX = transform.rescaleX(baseX)
.interpolate(d3.interpolateNumber)
scaleY = transform.rescaleY(baseY)
.interpolate(d3.interpolateNumber)
render();
});
const chart = d3.select('#zoom-chart')
.append('svg')
.attr('class', 'chart')
.call(zoom);
const rect = chart
.append('rect')
.attr('rx', 10).attr('ry', 10)
.attr('x', 10).attr('y', 10)
.attr('fill', '#00222b')
.attr('width', width-20)
.attr('height', height-20);
const plot = chart
.append('g')
.attr('class', 'plot-area');
function render() {
plot.selectAll('.data')
.data(data)
.join(
enter => enter.append('circle')
.attr('class', 'data')
.attr('r', 10)
.attr('stroke', '#004455')
.attr('stroke-width', 5),
update => update,
exit => exit.remove()
)
.attr('cx', d => scaleX(d.x))
.attr('cy', d => scaleY(d.y));
}
window.addEventListener('resize', () => {
const t0 = d3.zoomTransform(
chart.node());
width = document.getElementById('zoom-chart').clientWidth;
height = 480;
baseX.range([0, width])
.interpolate(d3.interpolateNumber);
baseY.range([height, 0])
.interpolate(d3.interpolateNumber);
vb = [0,0, width,height];
chart
.attr('viewBox', vb);
rect
.attr('width', width-20)
.attr('height', height-20);
chart.call(zoom.transform, t0);
});
let vb = [0,0, width,height];
chart
.attr('viewBox', vb);
chart.call(zoom.transform, d3.zoomIdentity);
<script src="https://unpkg.com/[email protected]/dist/d3.min.js"></script>
<div id="zoom-chart" class="chart large">
</div>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With