React zooming and dragging image

36 Views Asked by At

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:

  1. When I zoom, then I can only drag the image horizontally and not vertically
  2. I want the zoom to happen where the mouse pointer is, but it zooms always to (0, 0)
  3. 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

0

There are 0 best solutions below