114 lines
2.9 KiB
JavaScript
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>
|
|
);
|
|
});
|