Reputation: 1925
I am exploring the d3js (version 4) library and ran into the following issue while playing around with the zoom behavior.
When using the mousewheel in order to zoom after triggering a programmatic zoom by clicking at the svg it causes a laagy/jumpy behavior where it loses it's position.
I found this Stack Overflow resource: d3.js pan and zoom jumps when using mouse after programatic zoom and figured that might help me out. But it did not.
I've set up a simple example so that you can see what I mean. What am I missing here?
$(document).ready(function() {
var svg = d3.select('svg');
var group = d3.select('g#content');
svg.call(
d3.zoom().scaleExtent([1, 30]).on('zoom', function() {
group.attr('transform', d3.event.transform);
})
);
svg.on('click', function() {
group
.transition()
.duration(1000)
.attr("transform", "translate(100,100) scale(2)");
});
});
<script src="https://d3js.org/d3.v4.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<svg width="1200" height="500" viewBox="0 0 1200 500">
<g id="content">
<path d="M10,10 l50,0 0,50 -50,0 0,-50 Z" />
<path d="M70,10 l50,0 0,50 -50,0 0,-50 Z" />
<path d="M130,10 l50,0 0,50 -50,0 0,-50 Z" />
<path d="M190,10 l50,0 0,50 -50,0 0,-50 Z" />
<path d="M250,10 l50,0 0,50 -50,0 0,-50 Z" />
<path d="M310,10 l50,0 0,50 -50,0 0,-50 Z" />
</g>
</svg>
Edit By adding the following lines to my event listener the behavior improves while zooming in. It still jumps while I zoom out;
var transform = d3.zoomTransform(group.node());
transform.x = m.x;
transform.y = m.y;
transform.k = scale;
Full updated snippet:
$(document).ready(function() {
var svg = d3.select('svg');
var group = d3.select('g#content');
svg.call(
d3.zoom().scaleExtent([1, 30]).on('zoom', function() {
group.attr('transform', d3.event.transform);
})
);
svg.on('click', function() {
group
.transition()
.duration(1000)
.attr("transform", "translate(100,100) scale(2)");
var transform = d3.zoomTransform(group.node());
transform.x = 100;
transform.y = 100;
transform.k = 2;
});
});
<script src="https://d3js.org/d3.v4.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<svg width="1200" height="500" viewBox="0 0 1200 500">
<g id="content">
<path d="M10,10 l50,0 0,50 -50,0 0,-50 Z" />
<path d="M70,10 l50,0 0,50 -50,0 0,-50 Z" />
<path d="M130,10 l50,0 0,50 -50,0 0,-50 Z" />
<path d="M190,10 l50,0 0,50 -50,0 0,-50 Z" />
<path d="M250,10 l50,0 0,50 -50,0 0,-50 Z" />
<path d="M310,10 l50,0 0,50 -50,0 0,-50 Z" />
</g>
</svg>
Thanks in advance!
Upvotes: 3
Views: 872
Reputation: 8509
When you apply zoom programmatically you should do it this way:
svg.on('click', function() {
svg.transition()
.duration(750)
.call(zoom.transform, d3.zoomIdentity
.translate(100, 100) // set x and y transition
.scale(2) // set scale value
);
});
Note, that the translate must be applied before the scale.
Also, pay attention that d3-zoom have a default behaviour (zooming) for double-click event. If you do not want it, you can disable it with this code:
svg.on("dblclick.zoom", null);
Working demo:
$(document).ready(function() {
var svg = d3.select('svg');
var group = d3.select('g#content');
var zoom = d3.zoom().scaleExtent([1, 30]).on('zoom', function() {
group.attr('transform', d3.event.transform);
});
svg.call(zoom);
svg.on("dblclick.zoom", null);
svg.on('click', function() {
svg.transition()
.duration(750)
.call(zoom.transform, d3.zoomIdentity
.translate(100, 100)
.scale(2)
);
});
});
<script src="https://d3js.org/d3.v4.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<svg width="1200" height="500" viewBox="0 0 1200 500">
<g id="content">
<path d="M10,10 l50,0 0,50 -50,0 0,-50 Z" />
<path d="M70,10 l50,0 0,50 -50,0 0,-50 Z" />
<path d="M130,10 l50,0 0,50 -50,0 0,-50 Z" />
<path d="M190,10 l50,0 0,50 -50,0 0,-50 Z" />
<path d="M250,10 l50,0 0,50 -50,0 0,-50 Z" />
<path d="M310,10 l50,0 0,50 -50,0 0,-50 Z" />
</g>
</svg>
Upvotes: 2
Reputation: 239
This is because you are setting zoom values from event.transform (mouse wheel zoom events) while initializing zoom whereas in your click event handler you're setting a static value for transform attribute.
So the next time, a zoom event happens, event.transform value and your svg transform value will be different. So it jumps.
For eg: You zoom using mouse wheel. event.transform will be (500x, 500y, 5k). You do a mouse click and transform attribute will be set to (100x, 100y, 2k). The next time you do a mouse wheel event, event.transform will change from (500x, 500y,5k) say (600x, 600y, 6k). But since your svg transform attribute is (100x, 100y, 2k), it seems like it jumps.
Upvotes: 1