I am trying to add an image of a map to my website, I need to zoom on the image and move on it when it is zoomed. This is what I tried:
<div className="main-container">
<div
className="col map-column"
id="map-container"
ref={mapcontainerRef}
>
<ZoomableMap
imageUrl={map}
maxScale={15}
factor={0.5}
containerOffset={offset}
/>
</div>
<div className="col other-column">
<OtherComponent />
</div>
</div>
This is how I get the offset parameter:
const mapcontainerRef = useRef(null);
const [offset, setOffset] = useState({ left: 0, top: 0 });
useLayoutEffect(() => {
const element = mapcontainerRef.current;
setOffset({ left: element.offsetWidth, top: element.offsetHeight });
}, []);
The ZoomableMap is where I have problems:
const ZoomableMap = ({ imageUrl, maxScale, factor, containerOffset }) => {
const slideRef = useRef(null);
const [size, setSize] = useState({ w: 0, h: 0 });
const [pos, setPos] = useState({ x: 0, y: 0 });
const [scale, setScale] = useState(1);
const [isDragging, setIsDragging] = useState(false);
const [isInteractingWithImage, setIsInteractingWithImage] = useState(false);
const [lastCoords, setLastCoords] = useState({ x: null, y: null });
useLayoutEffect(() => {
const element = slideRef.current;
setSize({ w: element.offsetWidth, h: element.offsetHeight });
}, []);
useEffect(() => {
var newStyle =
"translate(" +
pos.x +
"px," +
pos.y +
"px) scale(" +
scale +
"," +
scale +
")";
slideRef.current.style.transform = newStyle;
}, [scale, pos, isDragging]);
useEffect(() => {
const wheelHandler = (event) => {
if (isInteractingWithImage) {
event.preventDefault();
// Il resto del tuo codice rimane invariato
}
};
slideRef.current.addEventListener("wheel", wheelHandler, {
passive: false,
});
return () => {
slideRef.current.removeEventListener("wheel", wheelHandler);
};
}, [isInteractingWithImage]);
const scrollZoom = (event) => {
if (isInteractingWithImage) {
event.preventDefault();
const zoomPoint = {
x: event.pageX - containerOffset.left,
y: event.pageY - containerOffset.top,
};
const evDelta =
event.delta ||
event.nativeEvent.wheelDelta ||
event.nativeEvent.detail;
const delta = Math.max(-1, Math.min(1, evDelta));
const zoomTarget = {
x: (zoomPoint.x - pos.x) / scale,
y: (zoomPoint.y - pos.y) / scale,
};
let newScale = scale + delta * factor * scale;
newScale = Math.max(1, Math.min(maxScale, newScale));
setScale(newScale);
let newPos = {
x: -zoomTarget.x * newScale + zoomPoint.x,
y: -zoomTarget.y * newScale + zoomPoint.y,
};
if (newPos.x > 0) newPos.x = 0;
if (newPos.x + size.w * newScale < size.w)
newPos.x = -size.w * (newScale - 1);
if (newPos.y > 0) newPos.y = 0;
if (newPos.y + size.h * newScale < size.h)
newPos.y = -size.h * (newScale - 1);
setPos(newPos);
}
};
const startDragging = (event) => {
event.preventDefault();
if (event.button === 0) {
setIsDragging(true);
console.log(isDragging);
setLastCoords({ x: event.pageX, y: event.pageY });
}
};
const stopDragging = (event) => {
if (event.type === "mouseleave") {
setIsInteractingWithImage(false);
}
setIsDragging(false);
};
const drag = (event) => {
event.preventDefault();
if (isDragging) {
var deltaX = event.pageX - lastCoords.x;
var deltaY = event.pageY - lastCoords.y;
// Make sure the slide stays in its container area
let newPos = { x: pos.x + deltaX, y: pos.y + deltaY };
if (newPos.x > 0) newPos.x = 0;
if (newPos.x + size.w * scale < size.w)
newPos.x = -size.w * (scale - 1);
if (newPos.y > 0) newPos.y = 0;
if (newPos.y + size.h * scale < size.h)
newPos.y = -size.h * (scale - 1);
setLastCoords({ x: event.pageX, y: event.pageY });
setPos(newPos);
}
};
return (
<div
className="zoomable-image-container"
id="slide"
ref={slideRef}
onWheelCapture={scrollZoom}
onMouseDown={startDragging}
onMouseUp={stopDragging}
onMouseEnter={() => {
setIsInteractingWithImage(true);
}}
onMouseLeave={stopDragging}
onMouseMove={drag}
>
<img src={imageUrl} alt="Zoomable" />
</div>
);
};
export default ZoomableMap;
I add here also the css, maybe the error is there and I don't see it:
.main-container {
width: 100%;
display: flex;
}
#map-container {
overflow: hidden;
}
.map-column {
flex: 1;
height: auto;
padding-left: 10px;
}
.other-column {
flex: 2;
padding-right: 10px;
}
.zoomable-image-container {
position: relative;
transform-origin: 0% 0%;
}
#slide {
width: 100%;
height: 100%;
transition: transform 0.1s;
}
.zoomable-image-container img {
width: 100%;
height: 100%;
}
My problems are the following:
- When I zoom, then I can only drag the image horizontally and not vertically
- I want the zoom to happen where the mouse pointer is, but it zooms always to (0, 0)
- When I zoom, the image enlarges a little bit...I try to explain better: when I start or refresh the webpage, the image has a certain dimension (that I thought would be 100% of the container), and when I zoom, the image occupies a little bit more of space. It looks like it was not expanded to 100% at the beginning. Also, when I drag on the image, I can drag it until the old limit. So lets say at the beginning it has dimension wxh, when I zoom it becomes (w+d)xh and the when I drag it to the very left or very right, it moves until it arrives to the wth pixel and not stopping at the *(w+d)*th one, leaving a white space of width d
I hope my explanation is clear enough, please let me know if you need further explanation or info.
Thanks