fix(carte): initialize map only when container is visible

fix #9417
This commit is contained in:
Paul Chavard 2023-08-31 18:16:14 +02:00
parent 79224569a2
commit 8802911126
2 changed files with 38 additions and 4 deletions

View file

@ -13,7 +13,7 @@ import type { Style } from 'maplibre-gl';
import invariant from 'tiny-invariant'; import invariant from 'tiny-invariant';
import { useStyle } from './hooks'; import { useStyle, useElementVisible } from './hooks';
import { StyleControl } from './StyleControl'; import { StyleControl } from './StyleControl';
const Context = createContext<{ map?: Map | null }>({}); const Context = createContext<{ map?: Map | null }>({});
@ -35,6 +35,7 @@ export function MapLibre({ children, layers }: MapLibreProps) {
[] []
); );
const containerRef = useRef<HTMLDivElement>(null); const containerRef = useRef<HTMLDivElement>(null);
const visible = useElementVisible(containerRef);
const [map, setMap] = useState<Map | null>(); const [map, setMap] = useState<Map | null>();
const onStyleChange = useCallback( const onStyleChange = useCallback(
@ -48,7 +49,7 @@ export function MapLibre({ children, layers }: MapLibreProps) {
const { style, ...mapStyleProps } = useStyle(layers, onStyleChange); const { style, ...mapStyleProps } = useStyle(layers, onStyleChange);
useEffect(() => { useEffect(() => {
if (isSupported && !map) { if (isSupported && visible && !map) {
invariant(containerRef.current, 'Map container not found'); invariant(containerRef.current, 'Map container not found');
const map = new Map({ const map = new Map({
container: containerRef.current, container: containerRef.current,
@ -59,7 +60,7 @@ export function MapLibre({ children, layers }: MapLibreProps) {
setMap(map); setMap(map);
}); });
} }
}, [map, style, isSupported]); }, [map, style, visible, isSupported]);
if (!isSupported) { if (!isSupported) {
return ( return (

View file

@ -1,4 +1,10 @@
import { useCallback, useEffect, useState, useMemo } from 'react'; import {
useCallback,
useEffect,
useState,
useMemo,
type RefObject
} from 'react';
import type { import type {
LngLatBoundsLike, LngLatBoundsLike,
LngLat, LngLat,
@ -118,3 +124,30 @@ export function useStyle(
return { style, layers, setStyle, setLayerEnabled, setLayerOpacity }; return { style, layers, setStyle, setLayerEnabled, setLayerOpacity };
} }
function isElementVisible(
element: HTMLElement,
callback: (visible: boolean) => void
) {
if (element.offsetWidth > 0 && element.offsetHeight > 0) {
callback(true);
} else {
callback(false);
const observer = new IntersectionObserver(
(entries) => callback(entries[0].isIntersecting == true),
{ threshold: [0] }
);
observer.observe(element);
return () => observer.unobserve(element);
}
}
export function useElementVisible(element: RefObject<HTMLElement>) {
const [visible, setVisible] = useState(false);
useEffect(() => {
if (element.current) {
return isElementVisible(element.current, setVisible);
}
}, [element]);
return visible;
}