diff --git a/src/google_map.js b/src/google_map.js index 59a1bce..fdc96da 100644 --- a/src/google_map.js +++ b/src/google_map.js @@ -34,6 +34,9 @@ const K_GOOGLE_TILE_SIZE = 256; const K_IDLE_TIMEOUT = 100; const K_IDLE_CLICK_TIMEOUT = 300; const DEFAULT_MIN_ZOOM = 3; +// Starting with version 3.32, the maps API calls `draw()` each frame during +// a zoom animation. +const DRAW_CALLED_DURING_ANIMATION_VERSION = 32; function defaultOptions_(/* maps */) { return { @@ -566,6 +569,11 @@ export default class GoogleMap extends Component { this._setLayers(this.props.layerTypes); + // Parse `google.maps.version` to capture the major version number. + const versionMatch = maps.version.match(/^3\.(\d+)\./); + // The major version is the first (and only) captured group. + const mapsVersion = versionMatch && Number(versionMatch[1]); + // render in overlay const this_ = this; const overlay = Object.assign(new maps.OverlayView(), { @@ -588,6 +596,10 @@ export default class GoogleMap extends Component { const panes = this.getPanes(); panes.overlayMouseTarget.appendChild(div); + this_.geoService_.setMapCanvasProjection( + maps, + overlay.getProjection() + ); ReactDOM.unstable_renderSubtreeIntoContainer( this_, @@ -618,11 +630,8 @@ export default class GoogleMap extends Component { draw() { const div = overlay.div; const overlayProjection = overlay.getProjection(); - const bounds = map.getBounds(); - const ne = bounds.getNorthEast(); - const sw = bounds.getSouthWest(); const ptx = overlayProjection.fromLatLngToDivPixel( - new maps.LatLng(ne.lat(), sw.lng()) + overlayProjection.fromContainerPixelToLatLng({ x: 0, y: 0 }) ); // need round for safari still can't find what need for firefox @@ -661,25 +670,29 @@ export default class GoogleMap extends Component { this_._onZoomAnimationStart(); } - const TIMEOUT_ZOOM = 300; + // If draw() is not called each frame during a zoom animation, + // simulate it. + if (mapsVersion < DRAW_CALLED_DURING_ANIMATION_VERSION) { + const TIMEOUT_ZOOM = 300; - if ( - new Date().getTime() - this.zoomControlClickTime_ < TIMEOUT_ZOOM - ) { - // there is strange Google Map Api behavior in chrome when zoom animation of map - // is started only on second raf call, if was click on zoom control - // or +- keys pressed, so i wait for two rafs before change state + if ( + new Date().getTime() - this.zoomControlClickTime_ < TIMEOUT_ZOOM + ) { + // there is strange Google Map Api behavior in chrome when zoom animation of map + // is started only on second raf call, if was click on zoom control + // or +- keys pressed, so i wait for two rafs before change state - // this does not fully prevent animation jump - // but reduce it's occurence probability - raf(() => - raf(() => { - this_.updateCounter_++; - this_._onBoundsChanged(map, maps); - })); - } else { - this_.updateCounter_++; - this_._onBoundsChanged(map, maps); + // this does not fully prevent animation jump + // but reduce it's occurence probability + raf(() => + raf(() => { + this_.updateCounter_++; + this_._onBoundsChanged(map, maps); + })); + } else { + this_.updateCounter_++; + this_._onBoundsChanged(map, maps); + } } } }); @@ -720,11 +733,8 @@ export default class GoogleMap extends Component { const div = overlay.div; const overlayProjection = overlay.getProjection(); if (div && overlayProjection) { - const bounds = map.getBounds(); - const ne = bounds.getNorthEast(); - const sw = bounds.getSouthWest(); const ptx = overlayProjection.fromLatLngToDivPixel( - new maps.LatLng(ne.lat(), sw.lng()) + overlayProjection.fromContainerPixelToLatLng({ x: 0, y: 0 }) ); // need round for safari still can't find what need for firefox const ptxRounded = detectBrowser().isSafari diff --git a/src/google_map_markers.js b/src/google_map_markers.js index e708ca8..71abc15 100644 --- a/src/google_map_markers.js +++ b/src/google_map_markers.js @@ -265,10 +265,9 @@ export default class GoogleMapMarkers extends Component { ? child.props.latLng : { lat: child.props.lat, lng: child.props.lng }; - const pt = this.props.geoService.project( - latLng, - this.props.projectFromLeftTop - ); + const pt = this.props.projectFromLeftTop + ? this.props.geoService.fromLatLngToContainerPixel(latLng) + : this.props.geoService.project(latLng); const stylePtPos = { left: pt.x, diff --git a/src/loaders/google_map_loader.js b/src/loaders/google_map_loader.js index 5d8db44..ce29d73 100644 --- a/src/loaders/google_map_loader.js +++ b/src/loaders/google_map_loader.js @@ -1,5 +1,3 @@ -import isEmpty from '../utils/isEmpty'; - const BASE_URL = 'https://maps'; const DEFAULT_URL = `${BASE_URL}.googleapis.com`; const API_PATH = '/maps/api/js?callback=_$_google_map_initialize_$_'; @@ -67,18 +65,11 @@ export default (bootstrapURLKeys, heatmapLibrary) => { } } - let params = Object.keys(bootstrapURLKeys).reduce( + const params = Object.keys(bootstrapURLKeys).reduce( (r, key) => `${r}&${key}=${bootstrapURLKeys[key]}`, '' ); - // if no version is defined, we want to get the release version - // and not the experimental version, to do so, we set v=3.31 - // src: https://developers.google.com/maps/documentation/javascript/versions - if (isEmpty(bootstrapURLKeys.v)) { - params += '&v=3.31'; - } - const baseUrl = getUrl(bootstrapURLKeys.region); const libraries = heatmapLibrary ? '&libraries=visualization' : ''; diff --git a/src/utils/geo.js b/src/utils/geo.js index 98c8e90..967b8d2 100644 --- a/src/utils/geo.js +++ b/src/utils/geo.js @@ -24,6 +24,11 @@ export default class Geo { this.hasSize_ = true; } + setMapCanvasProjection(maps, mapCanvasProjection) { + this.maps_ = maps; + this.mapCanvasProjection_ = mapCanvasProjection; + } + canProject() { return this.hasSize_ && this.hasView_; } @@ -62,6 +67,15 @@ export default class Geo { return this.transform_.locationPoint(LatLng.convert(ptLatLng)); } + fromLatLngToContainerPixel(ptLatLng) { + if (this.mapCanvasProjection_) { + const latLng = new this.maps_.LatLng(ptLatLng.lat, ptLatLng.lng); + return this.mapCanvasProjection_.fromLatLngToContainerPixel(latLng); + } + + return this.project(ptLatLng, true); + } + getWidth() { return this.transform_.width; }