Merge pull request #9442 from tchak/fix-conditional-map-display

fix(carte): initialize map only when container is visible
This commit is contained in:
Colin Darie 2023-09-04 08:41:36 +00:00 committed by GitHub
commit 1ba28cc8d3
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
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 { useStyle } from './hooks';
import { useStyle, useElementVisible } from './hooks';
import { StyleControl } from './StyleControl';
const Context = createContext<{ map?: Map | null }>({});
@ -35,6 +35,7 @@ export function MapLibre({ children, layers }: MapLibreProps) {
[]
);
const containerRef = useRef<HTMLDivElement>(null);
const visible = useElementVisible(containerRef);
const [map, setMap] = useState<Map | null>();
const onStyleChange = useCallback(
@ -48,7 +49,7 @@ export function MapLibre({ children, layers }: MapLibreProps) {
const { style, ...mapStyleProps } = useStyle(layers, onStyleChange);
useEffect(() => {
if (isSupported && !map) {
if (isSupported && visible && !map) {
invariant(containerRef.current, 'Map container not found');
const map = new Map({
container: containerRef.current,
@ -59,7 +60,7 @@ export function MapLibre({ children, layers }: MapLibreProps) {
setMap(map);
});
}
}, [map, style, isSupported]);
}, [map, style, visible, isSupported]);
if (!isSupported) {
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 {
LngLatBoundsLike,
LngLat,
@ -118,3 +124,30 @@ export function useStyle(
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;
}