Use ZoomPanEmitter/Subscriber instead of a context for aligning the view
This commit is contained in:
parent
2e1947a7d4
commit
de6d3aa5dc
@ -3,7 +3,6 @@ import './ZoomPanWrapper.css';
|
|||||||
import Constants from '../../helpers/constants';
|
import Constants from '../../helpers/constants';
|
||||||
import { useZoomPanSubscriber, viewBox$, clientRect$, camera$ } from '../../hooks/useZoomPubSub';
|
import { useZoomPanSubscriber, viewBox$, clientRect$, camera$ } from '../../hooks/useZoomPubSub';
|
||||||
import {mapRotation$} from '../../services/mapRotationService';
|
import {mapRotation$} from '../../services/mapRotationService';
|
||||||
import ZoomPanContext from "../../contexts/ZoomPanContext";
|
|
||||||
import AngleHelper from "../../helpers/angleHelper";
|
import AngleHelper from "../../helpers/angleHelper";
|
||||||
|
|
||||||
export default function ZoomPanWrapper({children}) {
|
export default function ZoomPanWrapper({children}) {
|
||||||
@ -91,8 +90,19 @@ export default function ZoomPanWrapper({children}) {
|
|||||||
scheduleCameraUpdate();
|
scheduleCameraUpdate();
|
||||||
}, [scheduleCameraUpdate]);
|
}, [scheduleCameraUpdate]);
|
||||||
|
|
||||||
// Subscribe to any external "center" calls
|
const alignView = (angleDeg) => {
|
||||||
useZoomPanSubscriber(centerOn);
|
// Find such relative rotation in the range [-45, 45] degrees that aligns the track
|
||||||
|
// with y rotation angleDeg to the X or Y screen axis.
|
||||||
|
const angleDifference = -angleDeg - cameraRef.current.rotation;
|
||||||
|
let deltaAngle = AngleHelper.normalizeDegAngle(angleDifference, 90);
|
||||||
|
if (deltaAngle > 45) deltaAngle -= 90;
|
||||||
|
|
||||||
|
cameraRef.current.rotation += deltaAngle;
|
||||||
|
scheduleCameraUpdate();
|
||||||
|
};
|
||||||
|
|
||||||
|
// Subscribe to any external `center` and `alignView` calls
|
||||||
|
useZoomPanSubscriber(centerOn, alignView);
|
||||||
|
|
||||||
const onWheel = (e) => {
|
const onWheel = (e) => {
|
||||||
const { left, top } = clientRectRef.current;
|
const { left, top } = clientRectRef.current;
|
||||||
@ -149,20 +159,7 @@ export default function ZoomPanWrapper({children}) {
|
|||||||
isMouseDownRef.current = false;
|
isMouseDownRef.current = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
// `event` could be used in the future to perform the rotation around the click position
|
|
||||||
const alignView = (angleDeg, _event) => {
|
|
||||||
// Find such relative rotation in the range [-45, 45] degrees that aligns the track
|
|
||||||
// with y rotation angleDeg to the X or Y screen axis.
|
|
||||||
const angleDifference = -angleDeg - cameraRef.current.rotation;
|
|
||||||
let deltaAngle = AngleHelper.normalizeDegAngle(angleDifference, 90);
|
|
||||||
if (deltaAngle > 45) deltaAngle -= 90;
|
|
||||||
|
|
||||||
cameraRef.current.rotation += deltaAngle;
|
|
||||||
scheduleCameraUpdate();
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ZoomPanContext.Provider value={{alignView}}>
|
|
||||||
<div className="zoom-pan-wrapper" ref={wrapperRef}>
|
<div className="zoom-pan-wrapper" ref={wrapperRef}>
|
||||||
<svg
|
<svg
|
||||||
ref={svgRef}
|
ref={svgRef}
|
||||||
@ -178,6 +175,5 @@ export default function ZoomPanWrapper({children}) {
|
|||||||
</g>
|
</g>
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
</ZoomPanContext.Provider>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,18 +3,18 @@ import SettingsContext from "../../../contexts/SettingsContext";
|
|||||||
import Constants from "../../../helpers/constants";
|
import Constants from "../../../helpers/constants";
|
||||||
import {ElectrificationStatus} from "../../../model/electrification-status";
|
import {ElectrificationStatus} from "../../../model/electrification-status";
|
||||||
import {setHoveredTrack, unsetHoveredTrack} from "../../../services/trackHoverInfoService";
|
import {setHoveredTrack, unsetHoveredTrack} from "../../../services/trackHoverInfoService";
|
||||||
import ZoomPanContext from "../../../contexts/ZoomPanContext";
|
|
||||||
import GradientsContext from "../../../contexts/GradientsContext";
|
import GradientsContext from "../../../contexts/GradientsContext";
|
||||||
import MiscHelper from "../../../helpers/miscHelper";
|
import MiscHelper from "../../../helpers/miscHelper";
|
||||||
|
import {useZoomPanEmitter} from "../../../hooks/useZoomPubSub";
|
||||||
|
|
||||||
export default function TrackRenderer(props) {
|
export default function TrackRenderer(props) {
|
||||||
const {object} = props;
|
const {object} = props;
|
||||||
const {trackColorMode} = useContext(SettingsContext);
|
const {trackColorMode} = useContext(SettingsContext);
|
||||||
const {gradientDefs} = useContext(GradientsContext);
|
const {gradientDefs} = useContext(GradientsContext);
|
||||||
const {alignView} = useContext(ZoomPanContext);
|
const { alignView } = useZoomPanEmitter();
|
||||||
|
|
||||||
const onAlign = (event) => {
|
const onAlign = () => {
|
||||||
alignView(object.rot.y, event);
|
alignView(object.rot.y);
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@ -1,4 +0,0 @@
|
|||||||
import { createContext } from 'react';
|
|
||||||
|
|
||||||
const ZoomPanContext = createContext();
|
|
||||||
export default ZoomPanContext;
|
|
||||||
@ -2,6 +2,7 @@ import { useEffect } from 'react';
|
|||||||
import { Subject, BehaviorSubject } from 'rxjs';
|
import { Subject, BehaviorSubject } from 'rxjs';
|
||||||
|
|
||||||
const zoomCenter$ = new Subject();
|
const zoomCenter$ = new Subject();
|
||||||
|
const viewAlign$ = new Subject();
|
||||||
|
|
||||||
// BehaviorSubjects to store current viewBox, clientRect and camera transform
|
// BehaviorSubjects to store current viewBox, clientRect and camera transform
|
||||||
export const viewBox$ = new BehaviorSubject(null);
|
export const viewBox$ = new BehaviorSubject(null);
|
||||||
@ -32,17 +33,27 @@ export function getCurrentCamera() {
|
|||||||
/**
|
/**
|
||||||
* useZoomPanSubscriber
|
* useZoomPanSubscriber
|
||||||
*
|
*
|
||||||
* Registers a callback (onCenter) that will be invoked whenever
|
* Registers an `onCenter` callback that will be invoked whenever
|
||||||
* someone calls `center(x, y)` via the emitter. You should pass
|
* someone calls `center(x, y)` via the emitter. You should pass
|
||||||
* a function that takes (x, y) and recenters your viewBox accordingly.
|
* a function that takes (x, y) and recenters your viewBox accordingly.
|
||||||
|
*
|
||||||
|
* Similarly, registers an `onAlign` callback that will be invoked
|
||||||
|
* when someone calls `alignView(angleDeg)`.
|
||||||
*/
|
*/
|
||||||
export function useZoomPanSubscriber(onCenter) {
|
export function useZoomPanSubscriber(onCenter, onAlign) {
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const sub = zoomCenter$.subscribe(({ x, y }) => {
|
const sub = zoomCenter$.subscribe(({ x, y }) => {
|
||||||
onCenter(x, y);
|
onCenter(x, y);
|
||||||
});
|
});
|
||||||
return () => sub.unsubscribe();
|
return () => sub.unsubscribe();
|
||||||
}, [onCenter]);
|
}, [onCenter]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const sub = viewAlign$.subscribe((angleDeg) => {
|
||||||
|
onAlign(angleDeg);
|
||||||
|
});
|
||||||
|
return () => sub.unsubscribe();
|
||||||
|
}, [onAlign]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -57,5 +68,8 @@ export function useZoomPanEmitter() {
|
|||||||
center: (x, y) => {
|
center: (x, y) => {
|
||||||
zoomCenter$.next({ x, y });
|
zoomCenter$.next({ x, y });
|
||||||
},
|
},
|
||||||
|
alignView: (angleDeg) => {
|
||||||
|
viewAlign$.next(angleDeg);
|
||||||
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user