I've rendered a d3 map that has pan and zoom enabled, but when scrolling down the viewport either on desktop or mobile, the window gets stuck zooming in the map.
Is there a way to temporarily disable d3.zoom, while the window is scrolling?
I've seen ways of toggling the zoom/pan using a button as seen here: http://jsfiddle.net/0xncswrk/, but I wanted to know if it's possible without having to add a button. Here's my current zoom logic.
Thanks!
this.zoom = d3.zoom()
.scaleExtent([1, 8])
.on('zoom', () => {
this.svg.attr('transform', d3.event.transform);
});
this.svg = d3.select(this.el).append('svg')
.attr('width', '100%')
.attr('height', this.height)
.attr('class', 'bubble-map__svg-us')
.call(this.zoom)
.append('g');
EDIT: Wow old answer but never saw your comment. Sorry about that. Yeah sorry I forgot to consider mobile zooming.
In the documentation, perhaps this is new, but they recommend having more granular control of what zoom you allow by using zoom.filter. For touch related events, they also support zoom.touchable.
As specified in the d3-zoom documentation
To disable just wheel-driven zooming (say to not interfere with native scrolling), you can remove the zoom behavior’s wheel event listener after applying the zoom behavior to the selection:
selection
.call(zoom)
.on("wheel.zoom", null);
You can also consider just setting the scaleExtent to be [1,1] especially if it's just temporary so it locks the zoom to only one possible scale but preferably you opt for what the documentation says :P
Got here because I was dealing with a similar problem. Perhaps for anyone coming after this, a simple way to deal with this might be to use the filter() method that a zoom() instance provides. That will allow you to toggle between applying or ignoring zoom events altogether. It works a little better than temporarily setting null to watchers because - at least in my experience - the events then still get recorded and stacked. As a consequence, you would zoom in or out in unexpected leaps once you re-enabled the handler. The filter actually really ignores what's going on, it seems. To implement:
let isZooming = true; // Use controls to set to true or false
zoom()
// Make sure we only apply zoom events if zooming is enabled by the user
.filter(() => isZooming)
.on('zoom', event => {
// Specify whatever you want to do with the event
});
Doc: https://github.com/d3/d3-zoom#zoom_filter
Related
I've been trying different tactics with leaflet map to zoom ignoring the default fixed zooming steps of the library, but without any luck so far. Through the following function:
window.addEventListener('mousewheel', function(e){
if (!e) e = event;
var direction = (e.detail<0 || e.wheelDelta>0) ? 1 : -1;
},
false);
I would like to achieve a smooth zooming effect like the one in Google Maps, which tracks the zoom based on deltaY and mouse scroll event. Do you know how to solve this issue or do you know a reference I could look at?
Thanks in advance for your replies!
Set the zoomSnap option of the map to zero. See also the wheelPxPerZoomLevel option.
Please note that fractional zoom is a feature introduced in Leaflet 1.0.0. This means that previous versions are not able to use non-integer zoom levels at all.
I have a zoomable map of the world with a single point on it (in reality there are multiple points from a separate resource but I have simplified it). The block is here.
When I try and zoom in it jumps to a certain scale and then usually doesn't allow any more zooming movements. I have experimented with various different values for the transition, scale and scaleExtent taken from this example and this one (with the latter being very close to what I want overall) but nothing has worked. It seems to get quite close to the actual size at height/6 for minimum zoom but still behaves badly.
I suspect the main problem is with scaleExtent. I actually want the minimum zoom to be the size of the map and so it isn't possible to pan around unless zoomed in.
The other problem is, as you can see in the bl.ock that the circle disappears when you zoom. I want the circle to maintain position and size (so it doesn't get bigger when I zoom).
Can any one help with
The zoom problem on the map, so the map minimum zoom is the actual size map and I can zoom in to about 6x that
Preventing the map from panning unless zoomed in
Maintaining the size and position of the circle on the map
I've put an example of what I think you're after on this bl.ock which is based on the first example you pointed to. It looks as though this line .scaleExtent([height, height*6]) is limiting the scale (well that the purpose of it) to something that was incompatible with your expectations and the initial scale you set, so when you zoom in past a certain level (in this case height) you get stick between height and height * 6.
If you set your minimum zoom and your initial zoom I think you'll get around some of your issues.
The issue with the dots was that they weren't referenced in the redraw function, so when you zoomed d3 / the browser didn't know what to do with them. In my example I've put the following snippet in to address this:
g.selectAll("circle")
.attr("cx", function (d,i) { return projection(d)[0]; })
.attr("cy", function (d,i) { return projection(d)[1]; })
.attr("r", "10px")
.style("fill", "red");
I'm trying to remove zoom completely from a svg.
zoom = d3.behavior.zoom()
.x(userNodesScaleX)
.y(userNodesScaleY)
.on("zoom", zoomed);
userMapSvg.call(zoom);
And this has added a 'rect.background' to the top of the SVG, which prevent the mouse event from reaching the other elements in the SVG.
So I decide to remove the zoom completely. remove the event, remove that rect. How can I do that?
Current code is
removeZoom = d3.behavior.zoom()
.on("zoom", null);
which doesn't work. It only toggles the event.
To stop any future zooming from transforming the page, remove the listener:
zoom.on("zoom", null)
To undo previous zoom transformations:
zoom.scale(1).translate([0,0]).event(userMapSvg)
http://bl.ocks.org/1wheel/6414125
The buttons at the top of the bl.ocks show both behaviors.
If neither work/are what you're looking for, posting a working example of the problem would be extremely helpful. You might also want to look through the zoom documentation.
Another way that I found to work is to set the zoom's extent and .extent and translateExtent to the width and height element ( thus disabling the zoom altogether ). And of course to set the scaleExtent to [1,1].
Try
userMapSvg.on(".zoom", null);
I would like to be able to fitBounds to a feature when you click on it, but when it zooms in I'd like it to take into account a control layer that will appear once zoomed in, and zoom in but just about 150px to the left. Currently I can accomplish this with the following code, but unfortunately it's not a smooth zoom because my current method will zoom using fitBounds and then once zoomed it uses panBy to pan 150px to the left. This wouldn't be so bad if the panning was smooth, perhaps maybe after a 250ms wait. Ideally I would like to be able to do some math on the bounds passed to the fitBounds method to simply account for the 150px shift to the left.
Here is an example of what I currently have working.
Here is a simplified version of the code I'm using: (you may click here for a fully working version with all of the source code)
when you click
function clickFeature(e) {
var layer = e.target;
map.fitBounds(layer.getBounds());
}
map on zoomEnd:
map.on({
zoomend: function() {
map.panBy([150, 0]);
}
});
So, I've achieved the desired function, but it's just not smooth.
Is there a way to just do some math on the bounds that we're getting for the clicked feature so that when we zoom we zoom into an already modified coordinate, thus eliminating the two-step animation process?
First of all, you can control the animation using pan options. This could help you make the transition smoother.
You can see those here.
Second, you can calculate the offset that you need by using the conversion functions. These can be seen here.
For example, you could do something like (off the top of my head) use getBoundsZoom for the map object on the polygon bounds to figure out your future zoom, then use that zoom in the project function with the polygon and create a new LatLngBound from the polygon bound that is slightly offset.
Hope this helps!
I had this same issue, and if was easier than I had thought!
You can set padding on the fitBounds method (and all the pan/zoom methods for that matter)
http://leafletjs.com/reference.html#map-fitboundsoptions
so:
map.fitBounds(layer.getBounds(),{paddingBottomRight:[150,0]});
I'm new to D3 and would like to implement a click-drag-zoom similar to what is shown here: http://www.highcharts.com/demo/line-time-series
I already have a line graph I have built, but am confused as to how to implement this.
I guess I need some JS event handlers to find where my mousedown and mouseup happens. But how do I create the shading that occurs on the graph when the user is dragging?
You'll probably want to use a brush to do this in d3.js. You can see an example that I put together at http://bl.ocks.org/1962173 which does something similar.
The relevant code is:
var brush = d3.svg.brush()
.x(x)
.extent([d3.time.monday(now),d3.time.saturday.ceil(now)])
.on("brush", display);
where display is a function that redraws data based on the current extent of brush. This way you don't need to try and hook your own handlers or even worry about resizing the highlighted region at all.