Leff
Leff

Reputation: 1350

Zooming out of the google map glitchy

I am using google maps in my project. And I have a select field consisting of a country and it's counties. On changing the location with the select I zoom in to that location and draw a polygon on the map. It works fine when I am drawing a polygon for one location. But, if I select a country with, in this case 20 polygons that needs to be drawn and then zoom out from the map to be visible, the whole animation process becomes glitchy. I have tried to wrap the zooming into a Promise object and wait until it is finished and then center the map and draw polygons in hope it would help make it a smooth animation. Below I have the select onchange event, where depending on the value that was selected I zoom in or zoom out from the map and draw polygons. If the selected value was a county I zoom in to the map and draw polygon just for that county. If the country was selected I zoom out and draw polygons of all counties of that country:

        let regions = geodata;
        let center = MAP_CENTER;
        let zoomLevel = 7;

        if (select.value !== country) {
            const location = geodata.find(({ Name }) => Name.includes(select.value));
            regions = [location];
            center = { lat: Number(location.Lat), lng: Number(location.Lng) };
            zoomLevel = 9;
        }

        smoothZoomPromise(map, zoomLevel, map.getZoom()).then((googleMap) => {
            googleMap.panTo(center);
            if (!(select.value === country && polygons.state.arrayOfPolygons.length === geodata.length)) {
                drawRegions(googleMap, regions);
            }
        });

This is the smoothZoomPromise function:

const smoothZoomPromise = (map, wantedLevel, startingLevel) => {
    return new Promise((resolve) => {
        if (wantedLevel === startingLevel) {
            return resolve(map);
        }
        const smoothZoom = (googleMap, finishLevel, startLevel) => {
            if (finishLevel === startLevel) {
                return;
            }
            const current = startLevel > finishLevel ? startLevel - 1 : startLevel + 1;
            const z = google.maps.event.addListener(googleMap, 'zoom_changed', event => {
                google.maps.event.removeListener(z);
                smoothZoom(googleMap, finishLevel, current);
            })

            doZoom(googleMap, current, resolve, current === finishLevel);
        }

        smoothZoom(map, wantedLevel, startingLevel);
    });
}

const doZoom = (map, cnt, resolve, last) => {
    setTimeout(() => {
        map.setZoom(cnt);
        if(last) {resolve(map)}
    }, 80);
}

And this is how I draw polygons:

export const polygons = {
    state: {
        arrayOfPolygons: []
    },
    clearState: function () {
        this.state.arrayOfPolygons.forEach(polygon => polygon.setMap(null));
        this.state.arrayOfPolygons = []
    }
};

export const drawRegions = (map, regions) => {
    polygons.clearState();
    regions.forEach(region => {
        const coords = region.Coordinates.map(item => ({
            lat: item[1], lng: item[0],
        }))

        // Construct the polygon.
        const color = colorMapper(getStatsData(region.Name));
        const polygon = new google.maps.Polygon({
            paths: coords,
            strokeColor: color,
            strokeOpacity: 0.8,
            strokeWeight: 2,
            fillColor: color,
            fillOpacity: 0.35
        });
        polygon.setMap(map);
        polygons.state.arrayOfPolygons.push(polygon)
    })
}

But, like I mentioned, the problem that I have is that when I select a country and zoom out from 9 to 7 zoom level, and draw 20 polygons for it, the zooming out of the map becomes glitchy. Is there a way I can make this smooth and fix it?

I made an example codesanbox. The only thing that is needed to have it working, is a valid API_KEY in index.js:

if (document.querySelector(".js-map")) {
  gmap.GMap(".js-map", "YOUR_API_KEY");
}

I would like to achieve the same transition as it is done here with google maps. Where if you zoom all the way in, and then change location with select, the map is not rendered until it is zoomed out. How can I achieve that effect?

Upvotes: 4

Views: 590

Answers (1)

Joe - Check out my books
Joe - Check out my books

Reputation: 16923

The trick is to wrap the polygon drawing inside of a one-time idle listener, i.e:

smoothZoomPromise(map, zoomLevel, map.getZoom()).then((googleMap) => {
  googleMap.panTo(center);
  if (
    !(
      select.value === "Croatia" &&
      polygons.state.arrayOfPolygons.length === geodata.length
    )
  ) {

    //  ----|
    //      |
    //      v
    google.maps.event.addListenerOnce(googleMap, "idle", () => {
      drawRegions(googleMap, regions);
    });

  }
});

A sandbox is here -- you'll need to put your own api key in.

I think that's what google does anyways -- it's standard practice. You can check the different types of gmap events in action here.

Upvotes: 1

Related Questions