Files
frontend/packages/common/business_components/src/ui/map/GisZoomLevel.js
T

114 lines
2.9 KiB
JavaScript

import React, { useEffect, useRef, useState } from 'react';
import { useGisContext } from './Gis';
export default React.memo(function GisZoomLevel() {
const { gisMap } = useGisContext();
const trackRef = useRef(null);
const [zoomInfo, setZoomInfo] = useState({
zoom: 0,
minZoom: 0,
maxZoom: 0,
});
useEffect(() => {
if (!gisMap) return undefined;
const view = gisMap.getView();
if (!view) return undefined;
const updateZoomInfo = () => {
setZoomInfo({
zoom: view.getZoom() ?? 0,
minZoom: view.getMinZoom?.() ?? 0,
maxZoom: view.getMaxZoom?.() ?? 20,
});
};
updateZoomInfo();
view.on('change:resolution', updateZoomInfo);
return () => {
view.un('change:resolution', updateZoomInfo);
};
}, [gisMap]);
if (!gisMap) return <></>;
const { zoom, minZoom, maxZoom } = zoomInfo;
const range = Math.max(maxZoom - minZoom, 1);
const normalized = Math.min(Math.max((zoom - minZoom) / range, 0), 1);
const fillHeight = `${normalized * 100}%`;
const thumbBottom = `calc(${fillHeight} - 1px)`;
const updateZoomFromClientY = clientY => {
if (!gisMap || !trackRef.current) return;
const view = gisMap.getView();
const rect = trackRef.current.getBoundingClientRect();
const offsetY = rect.bottom - clientY;
const nextNormalized = Math.min(Math.max(offsetY / rect.height, 0), 1);
const nextZoom = minZoom + nextNormalized * range;
view.setZoom(nextZoom);
};
const zoomIn = () => {
const view = gisMap.getView();
if (view.getMaxZoom() > view.getZoom()) {
view.animate({
zoom: view.getZoom() + 1,
duration: 250,
});
}
};
const zoomOut = () => {
const view = gisMap.getView();
if (view.getMinZoom() < view.getZoom()) {
view.animate({
zoom: view.getZoom() - 1,
duration: 250,
});
}
};
const handleTrackPointerDown = event => {
event.preventDefault();
updateZoomFromClientY(event.clientY);
const handlePointerMove = moveEvent => {
updateZoomFromClientY(moveEvent.clientY);
};
const handlePointerUp = () => {
window.removeEventListener('pointermove', handlePointerMove);
window.removeEventListener('pointerup', handlePointerUp);
};
window.addEventListener('pointermove', handlePointerMove);
window.addEventListener('pointerup', handlePointerUp);
};
return (
<div className="zoom-level-control" title={`현재 줌 레벨 ${zoom.toFixed(1)}`}>
<div className="zoom-level-panel">
<button type="button" className="zoom-level-btn" onClick={zoomIn} aria-label="확대">
+
</button>
<div className="zoom-level-gauge">
<div
ref={trackRef}
className="zoom-level-track"
onPointerDown={handleTrackPointerDown}
>
<div className="zoom-level-fill" style={{ height: fillHeight }} />
<div className="zoom-level-thumb" style={{ bottom: thumbBottom }} />
</div>
</div>
<button type="button" className="zoom-level-btn" onClick={zoomOut} aria-label="축소">
-
</button>
</div>
</div>
);
});