16

There is no appropriate zoom event listener in OpenLayers v5.3 yet, but there is a workaround suggested in this ticket:

You can use this.view.instance.on('change:zoom', evt => console.log(this.view.zoom)); where view is a viewChild : @ViewChild(ViewComponent) view: ViewComponent; to detect zoom changes.

Can anyone please explain to newbies how to utilize this? How to use that code with an instance of a map view which is usually got by calling map.getView()?

Taras
  • 32,823
  • 4
  • 66
  • 137
yaugenka
  • 829
  • 2
  • 12
  • 23
  • 2
    That syntax is being used with Angular. You don't need a zoom change event, if the zoom changes so does the resolution, so you can use view.on('change:resolution', function() { }); – Mike Jan 21 '19 at 15:22
  • 1
    The 'change:resolution' event is triggered about a dozen times for one zoom level change. The intermediate values are non-integer and the last one is always integer as far as I can see. Is there a way to catch just the final integer value? – yaugenka Jan 21 '19 at 16:04
  • Mousewheel operations can sometimes leave the map at a fractional zoom level, but if you are only interested in catching integer values you could test if (map.getView().getZoom()%1 == 0) { } inside the callback – Mike Jan 21 '19 at 16:23
  • Is it possible to the final value may be fractional? – yaugenka Jan 21 '19 at 16:56
  • In some situations it might https://openlayers.org/en/v4.6.5/apidoc/ol.interaction.html but you can set interactions: ol.interaction.defaults({ constrainResolution: true }), as a map option to prevent it – Mike Jan 21 '19 at 17:52

2 Answers2

27

To hook on zoom end event is fairly simple: listen to moveend event and compare old and new zoom:

var currZoom = map.getView().getZoom();
map.on('moveend', function(e) {
  var newZoom = map.getView().getZoom();
  if (currZoom != newZoom) {
    console.log('zoom end, new zoom: ' + newZoom);
    currZoom = newZoom;
  }
});

If you want to catch zoom start, it's more complicated. I found a way but I don't like it, it's not how it should be done. I noticed that exposed method view.animate is called before any animated map event. I replaced it with my own, where I check for zoom, and then call original method:

view.origAnimate = view.animate;

view.animate = function(animateSpecs) {
  if (typeof animateSpecs.resolution !== 'undefined') {
    var currZoom = this.getZoom();
    var newZoom = this.getZoomForResolution(animateSpecs.resolution);
    if (newZoom != currZoom)) {
      console.log('zoom start, new zoom: ' + newZoom);
    }
  }
  this.origAnimate(animateSpecs);
};
TomazicM
  • 25,601
  • 22
  • 29
  • 39
  • Thanks, TomazicM. This solution works equally well as the one suggested by Mike in the question comments. With moveend event you have to filter map pan events where as with change:resulution you have to filer intermediate zoom change events. – yaugenka Jan 22 '19 at 14:56
8

there is a simpler way for you to do it:

map.getView().on('change:resolution', (event) => {
    console.log("zoom changed");
});
Jayzz
  • 85
  • 1
  • 11
Pavel Pejšek
  • 91
  • 1
  • 3