Skip to content

Modifying Zoom Plugin to work with useGesture library. #56

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