Skip to content

Modifying Zoom Plugin to work with useGesture library. #56

@RetroThorium

Description

@RetroThorium

Hey,

First and foremost thank you so much for this amazing project.
This is one the most well structured and modular systems I have had the pleasure to work with.

However, I would like to modify the zoom plugin.
I am trying to implement pinch to zoom (for trackpads and mouse (wheelEvent)).
This is similar to in browser experience.

For this I am using useGesture Library (https://use-gesture.netlify.app/)..
This is because hammer.js looks unmaintained and also doesn't support pinch to zoom for trackpads.

Currently my use-gesture-zoom hooks looks like the following.

import { useRef } from 'react';
import { useZoomCapability } from './use-zoom';
import { useCapability } from '@embedpdf/core/react';
import type { ViewportPlugin } from '@embedpdf/plugin-viewport';
import { useGesture } from '@use-gesture/react';
import type { ZoomState } from '../../lib';

export function useGestureZoom() {
    const { provides: viewportProvides } = useCapability<ViewportPlugin>('viewport');
    const { provides: zoomProvides } = useZoomCapability();
    const elementRef = useRef<HTMLDivElement>(null);

    const initialZoomRef = useRef(1);
    const lastCenterRef = useRef({ x: 0, y: 0 });

    const updateTransform = (scale: number) => {
        if (!elementRef.current) return;
        elementRef.current.style.transform = `scale(${scale})`;
    };

    const resetTransform = () => {
        if (!elementRef.current) return;
        elementRef.current.style.transform = 'none';
        elementRef.current.style.transformOrigin = '0 0';
    };

    useGesture(
        {
            onPinchStart: ({ event, origin }) => {
                if (!elementRef.current || !zoomProvides || !viewportProvides) return;

                initialZoomRef.current = zoomProvides.getState().currentZoomLevel;

                const viewerRect = elementRef.current.getBoundingClientRect();

               
                lastCenterRef.current = {
                    x: origin[0] - viewerRect.left,
                    y: origin[1] - viewerRect.top,
                };

                elementRef.current.style.transformOrigin = `${lastCenterRef.current.x}px ${lastCenterRef.current.y}px`;

                if (event.cancelable) {
                    event.preventDefault();
                    event.stopPropagation();
                }
            },

            onPinch: ({ event, offset: [scale] }) => {
                updateTransform(scale);
                if (event.cancelable) {
                    event.preventDefault();
                    event.stopPropagation();
                }
            },

            onPinchEnd: ({ offset: [scale], origin }) => {
                if (!zoomProvides) return;

                const delta = (scale - 1) * initialZoomRef.current;
                const center = lastCenterRef.current;

                zoomProvides.requestZoomBy(delta, { vx: center.x, vy: center.y });

                resetTransform();
            }
        },
        {
            target: elementRef,
            eventOptions: { passive: false },
            pinch: {
                scaleBounds: { min: 0.5, max: 3 },
                rubberband: true,
                from: () => [1, 0],
            },
        }
    );

    return { elementRef };
}

While zooming the transform is applied focusing at the correct coordinates but when requestZoomBy call is issued, the viewer jumps to someplace else. I don't understand why this might be the case.

Any help is appreciated.

Thanks again!

Metadata

Metadata

Assignees

Labels

enhancementNew feature or requestgood first issueGood for newcomersquestionFurther information is requested

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions