mapbox / mapbox-gl-js

Interactive, thoroughly customizable maps in the browser, powered by vector tiles and WebGL
https://docs.mapbox.com/mapbox-gl-js/
Other
11.19k stars 2.22k forks source link

ReactJs - map size issue at first load (until I resize the screen) #8982

Closed Emixam23 closed 4 years ago

Emixam23 commented 4 years ago

Hi !

I saw another question related to my issue but it didn't solve it (maybe because my react approach is wrong, I don't know...)

I have this component:

import * as React from 'react';
import {Location} from "app/models/location";
// @ts-ignore
import * as style from './style.css'
import mapboxgl, {LngLat} from "mapbox-gl";

export interface LocationMapboxProps {
    locations: Location[];
}

export interface LocationMapboxState {
    lng: number;
    lat: number;
    zoom: number
    selectedLocation: Location
}

const mapboxStyleUrl = "----";
mapboxgl.accessToken = "my-token";

export class LocationMapbox extends React.Component<LocationMapboxProps, LocationMapboxState> {
    mapContainer: HTMLDivElement = null;

    constructor(props: LocationMapboxProps, context: any) {
        super(props, context);
        this.state = {
            lng: 5,
            lat: 34,
            zoom: 2,
            selectedPlace: null
        };
    }

    componentDidMount() {
        let map = new mapboxgl.Map({
            container: this.mapContainer,
            style: mapboxStyleUrl,
            center: [this.state.lng, this.state.lat],
            zoom: this.state.zoom,
        });

        map.on('move', () => {
            this.setState({
                lng: parseFloat(map.getCenter().lng.toFixed(4)),
                lat: parseFloat(map.getCenter().lat.toFixed(4)),
                zoom: parseFloat(map.getZoom().toFixed(2))
            });
        });

        this.props.locations.map(function(location) {
            console.log(location);

            // create a HTML element for each feature
            let el = document.createElement('div');
            el.className = style["marker"];

            el.onclick = () => {
                map.flyTo({
                    center: new LngLat(location.longitude, location.latitude),
                    zoom: 15,
                    speed: .75
                });
            };

            // make a marker for each feature and add to the map
            new mapboxgl.Marker(el, {
                    anchor: "bottom"
                })
                .setLngLat(new LngLat(location.longitude,location.latitude))
                .addTo(map);
        });
    }

    render() {
        return (
            <div className={style["container"]}>
                <div className={`${style["sidebarStyle"]}`}>
                    <div>Longitude: {this.state.lng} | Latitude: {this.state.lat} | Zoom: {this.state.zoom}</div>
                </div>
                <div ref={el => this.mapContainer = el} className={style["container"]}/>
            </div>
        )
    }
}

export default LocationMapbox

and its css:

.container {
    height: 100%;
    width: 100%;
}

.sidebarStyle {
    display: inline-block;
    position: absolute;
    top: 0;
    left: 0;
    margin: 12px;
    background-color: #404040;
    color: #ffffff;
    z-index: 1 !important;
    padding: 6px;
    font-weight: bold;
}

.marker {
    background-image: url('../../../assets/pins/green.png');
    background-size: cover;
    background-color: red;
    width: 50px;
    height: 50px;
    border-radius: 50%;
    cursor: pointer;
    border: 1px solid gray;
    offset-anchor: top;
}

THE ISSUE

While using my component, I want to be able to use it as full screen or in a small container (on the main view). However, in full screen, I have a weird behavior:

Screenshot 2019-11-17 at 13 36 38

RELATED QUESTION: https://github.com/mapbox/mapbox-gl-js/issues/3265

Thanks for any help

mourner commented 4 years ago

Call map.resize() after the container element changes size programmatically.

Emixam23 commented 4 years ago

Hey!

Can you be a bit more specific, please? I added your code by the end of componentDidMount and I still have the same behavior.

Thanks,

mourner commented 4 years ago

If it's still the same behavior, it means the map container didn't change the size at this moment. The reason I'm not more specific is that this is not a GL JS issue — you use the repo to get help with your application, and this is not a good place to do this. For React questions, I would suggest StackOverflow.

Emixam23 commented 4 years ago

Alright thank @mourner for your feedback, I will then get on SO and if I find any solution, I will post it here

Emixam23 commented 4 years ago

I finally found out..

this.map.once('load', () => {
    this.map.resize();
});
Diaver commented 3 years ago

For react version:

import React from 'react';
import ReactMapboxGl, {Marker} from 'react-mapbox-gl';
import * as MapboxGl from 'mapbox-gl';
import 'mapbox-gl/dist/mapbox-gl.css';

export const MapBox = React.memo(function MapBox() {

    const Map = ReactMapboxGl({
        accessToken: 'Token',
        trackResize: true,

    });

    const onLoaded =(map: MapboxGl.Map) =>{
        map.resize();
    }

    return (
        <div className="main-map-container" style={{height: "100%"}}>
            <Map
                style="mapbox://styles/mapbox/streets-v9"
                containerStyle={{
                    height: "calc(100vh - 130px)",
                    width: "100%"
                }}
                center={[19.9449799, 50.06465]}
                zoom={[13]}

                onStyleLoad={(map)=>onLoaded(map)}
                onClick={()=>alert("click")}
            >
                <Marker coordinates={[19.9449799, 50.0646501]} anchor="bottom">
                    <div >test</div>
                </Marker>
            </Map>
        </div>
    );
});
shahnad commented 3 years ago
map.on('idle',function(){
map.resize()
})

try this. it works fine for me.

uselessscope commented 3 years ago
map.on('idle',function(){
map.resize()
})

try this. it works fine for me.

ty, short and true

TiagoJeronimo commented 3 years ago
map.on('idle',function(){
map.resize()
})

try this. it works fine for me.

With the above, mapbox will be calling map.resize() every time the map is on idle. Not sure if this can have some performance implications. Maybe using once will work as well and only calls map.resize() once:

map.once('idle', function () { map.resize() })

vdemcak commented 2 years ago

Is it possible to force it to be the correct size right on load? I don't really like the fact that there is still a split second where the map has wrong size.

hxf31891 commented 1 year ago

@vdemcak did you ever find a solution here?

mattkraemer commented 1 year ago

By using the render event instead of load or idle the map should be displayed in the correct size without any weird glitches.

This works for me:

map.current?.once('render',function(){
map.current?.resize()
});
faseehahmed1 commented 2 months ago

This worked for me:

import React, { useState, lazy, Suspense, useRef, useEffect } from 'react';
import ReactMapGL, { ViewState, MapRef } from 'react-map-gl';

...

const mapRef = useRef<MapRef | null>(null);

useEffect(() => {
    if (!displayAsHidden && mapRef.current) {
      mapRef.current.getMap().resize();
    }
  }, [displayAsHidden]);

  return (
    <ReactMapGL
      {...viewport}
      ref={mapRef}
      style={{ width: '100%', height: '100%' }}
      mapStyle={'mapbox://styles/...'}
      onMove={(evt): void => setViewport(evt.viewState)}
      mapboxAccessToken={mapBoxToken}
      onLoad={(): void => setIsLoaded(true)}
    >
      <Suspense fallback={null}>
        <MapDrawControls
          drawControls={mapDrawControls}
          setGeoJsonData={setGeoJsonData}
          geoJsonData={geoJsonData}
          isLoaded={isLoaded}
          updateGeoJsonCallback={updateInputValueFromMap}
        />
      </Suspense>
    </ReactMapGL>