From 8e5f47d565e5e4fbb7b7c00fb3c46b0cf829a957 Mon Sep 17 00:00:00 2001 From: Andrii Andrushchyshyn Date: Tue, 6 May 2025 17:28:42 +0300 Subject: [PATCH] Fix hexagon flickering on the globe during zoom Implemented a mechanism for deferred change of the height of the base hexagons, depending on the distance from the globe. --- .../realtime/RealtimeGlobe/RealtimeGlobe.tsx | 43 ++++++++++++++++++- 1 file changed, 41 insertions(+), 2 deletions(-) diff --git a/client/src/app/[site]/realtime/RealtimeGlobe/RealtimeGlobe.tsx b/client/src/app/[site]/realtime/RealtimeGlobe/RealtimeGlobe.tsx index efbe062..6106512 100644 --- a/client/src/app/[site]/realtime/RealtimeGlobe/RealtimeGlobe.tsx +++ b/client/src/app/[site]/realtime/RealtimeGlobe/RealtimeGlobe.tsx @@ -4,8 +4,8 @@ import { useQuery } from "@tanstack/react-query"; import { useWindowSize } from "@uidotdev/usehooks"; import { scaleSequentialSqrt } from "d3-scale"; import { interpolateYlOrRd } from "d3-scale-chromatic"; -import { memoize } from "lodash"; -import { useEffect, useMemo, useRef } from "react"; +import { memoize, debounce } from "lodash"; +import { useEffect, useMemo, useRef, useState, useCallback } from "react"; import Globe from "react-globe.gl"; import { MeshPhongMaterial } from "three"; import { useGetLiveSessionLocations } from "../../../../api/analytics/useGetLiveSessionLocations"; @@ -53,6 +53,44 @@ export const World = ({ width }: { width: number }) => { } }, [globeEl.current]); + const [hexAltitude, setHexAltitude] = useState(0.001); + + // Debounced updateAltitude handler: fires once, 200ms after the last 'end' event + const updateAltitude = useCallback( + debounce(() => { + if (!globeEl.current) return; + + const controls = globeEl.current.controls(); + const distance = Math.round(controls.getDistance()); + + const low = 0.001, mid = 0.005, high = 0.02; + const near = 300, far = 600; + const nextAlt = + distance <= near ? low : + distance >= far ? high : + mid; + + // set only if changed + if (nextAlt !== hexAltitude) { + setHexAltitude(nextAlt); + } + }, 200), + [globeEl, hexAltitude, setHexAltitude] + ); + + useEffect(() => { + if (!globeEl.current) return; + const controls = globeEl.current.controls(); + // Limit distance maximal distance + controls.maxDistance = 900; + // Subscribe on change event + controls.addEventListener('end', updateAltitude); + return () => { + controls.removeEventListener('end', updateAltitude); + updateAltitude.cancel(); // cancel any pending call + }; + }, [globeEl, updateAltitude]); + const oceanBlueMaterial = useMemo( () => new MeshPhongMaterial({ @@ -106,6 +144,7 @@ export const World = ({ width }: { width: number }) => { hexPolygonMargin={0.2} hexBinResolution={3} hexBinPointsData={liveSessionLocationsData} + hexPolygonAltitude={hexAltitude} hexBinMerge={true} hexBinPointWeight={"count"} backgroundColor="rgba(0, 0, 0, 0)"