From 0ca6f6b9e06ac43f9f1c5fed912b9f26914538ae Mon Sep 17 00:00:00 2001 From: izawartka <59137928+izawartka@users.noreply.github.com> Date: Wed, 13 Aug 2025 13:36:13 +0200 Subject: [PATCH] Optimized track rendering *Optimized track rendering to not always render both scaled and unscaled versions of the track --- src/components/map/ZoomPanWrapper.js | 6 ++- .../map/object-renderers/TrackRenderer.js | 47 ++++++++++++++----- src/helpers/constants.js | 3 +- src/services/mapZoomService.js | 13 +++++ 4 files changed, 55 insertions(+), 14 deletions(-) create mode 100644 src/services/mapZoomService.js diff --git a/src/components/map/ZoomPanWrapper.js b/src/components/map/ZoomPanWrapper.js index a7fd089..597daf5 100644 --- a/src/components/map/ZoomPanWrapper.js +++ b/src/components/map/ZoomPanWrapper.js @@ -2,8 +2,9 @@ import React, { useRef, useEffect, useCallback } from 'react'; import './ZoomPanWrapper.css'; import Constants from '../../helpers/constants'; import { useZoomPanSubscriber, viewBox$, clientRect$, camera$ } from '../../hooks/useZoomPubSub'; -import {mapRotation$} from '../../services/mapRotationService'; +import {setMapRotation} from '../../services/mapRotationService'; import AngleHelper from "../../helpers/angleHelper"; +import { setMapZoom } from '../../services/mapZoomService'; function boundZoom(zoom) { return Math.max(Constants.map.zoomMin, Math.min(Constants.map.zoomMax, zoom)); @@ -44,7 +45,8 @@ export default function ZoomPanWrapper({children}) { rafScheduledRef.current = false; cameraWrapperRef.current.setAttribute('transform', getCameraTransformString()); camera$.next(cameraRef.current); - mapRotation$.next(cameraRef.current.rotation); + setMapRotation(cameraRef.current.rotation); + setMapZoom(cameraRef.current.zoom); }); }, []); diff --git a/src/components/map/object-renderers/TrackRenderer.js b/src/components/map/object-renderers/TrackRenderer.js index 5fbd1ca..3e23eb8 100644 --- a/src/components/map/object-renderers/TrackRenderer.js +++ b/src/components/map/object-renderers/TrackRenderer.js @@ -1,4 +1,4 @@ -import React, {useContext} from "react"; +import React, {useEffect, useCallback, useContext} from "react"; import SettingsContext from "../../../contexts/SettingsContext"; import Constants from "../../../helpers/constants"; import {ElectrificationStatus} from "../../../model/electrification-status"; @@ -7,6 +7,7 @@ import {setHoveredTrack, unsetHoveredTrack} from "../../../services/trackHoverIn import GradientsContext from "../../../contexts/GradientsContext"; import { TrackShape, TrackSource } from "../../../model/tracks/track"; import {useZoomPanEmitter} from "../../../hooks/useZoomPubSub"; +import { mapZoomTrackDetails$ } from "../../../services/mapZoomService"; export default function TrackRenderer(props) { const { object } = props; @@ -32,25 +33,47 @@ const MemoizedTrackRenderer = React.memo(StatelessTrackRenderer); function StatelessTrackRenderer(props) { const {object, trackColorMode, gradientDef, onAlign} = props; + const unscaledPathRef = React.useRef(null); + const scaledPathRef = React.useRef(null); - if (object.points.start.distanceSq(object.points.end) < 0.001) { - return null; - } - - const onMouseEnter = () => { + const onMouseEnter = useCallback(() => { setHoveredTrack(object); - }; + }, [object]); - const onMouseLeave = () => { + const onMouseLeave = useCallback(() => { unsetHoveredTrack(null); - }; + }, []); - const onClick = (event) => { + const onClick = useCallback((event) => { if (object.shape === TrackShape.STRAIGHT && event.detail === 2) { event.preventDefault(); onAlign(event); } - }; + }, [object, onAlign]); + + const handleDetailsUpdate = useCallback((details) => { + if (!unscaledPathRef.current || !scaledPathRef.current) return; + + if(details) { + unscaledPathRef.current.style.display = 'none'; + scaledPathRef.current.style.display = 'block'; + } else { + unscaledPathRef.current.style.display = 'block'; + scaledPathRef.current.style.display = 'none'; + } + }, []); + + useEffect(() => { + const subscription = mapZoomTrackDetails$.subscribe(details => { + handleDetailsUpdate(details); + }); + + return () => subscription.unsubscribe(); + }, [handleDetailsUpdate]); + + if (object.points.start.distanceSq(object.points.end) < 0.001) { + return null; + } const path = getTrackPath(object); const color = getTrackColor(object, trackColorMode, gradientDef); @@ -60,11 +83,13 @@ function StatelessTrackRenderer(props) { {defs} Constants.map.mapZoomTrackDetailsThreshold; + if (mapZoomTrackDetails$.getValue() !== newZoomTrackDetails) { + mapZoomTrackDetails$.next(newZoomTrackDetails); + } +}