Files
OpenSign/apps/OpenSign/src/hook/usePdfPinchZoom.js
T
nxglabs 71b9bd6367 Merge pull request #937 from nxglabs/tenant_mailtemplate
fix: if user not provide default value use it from template in createdocument from template API
2025-07-03 16:04:35 +00:00

111 lines
3.9 KiB
JavaScript

import { useEffect, useRef } from "react";
export default function usePdfPinchZoom(ref, scale, setScale, setZoomPercent) {
const initialDistanceRef = useRef(null);
const startScaleRef = useRef(scale);
const currentScaleRef = useRef(scale);
const pinchScaleRef = useRef(null); // stores relative scale during a gesture
const frameRef = useRef(null);
// keep a reference to the latest scale value
useEffect(() => {
const element = ref.current;
currentScaleRef.current = scale;
// once React commits the new scale after a pinch we clear the transform
if (element && !pinchScaleRef.current) {
element.style.transform = "";
}
}, [scale, ref]);
useEffect(() => {
const element = ref.current;
if (!element) return;
const getDistance = (touches) => {
const [touch1, touch2] = touches;
const dx = touch1.clientX - touch2.clientX;
const dy = touch1.clientY - touch2.clientY;
return Math.hypot(dx, dy);
};
const handleTouchStart = (e) => {
if (e.touches.length === 2) {
initialDistanceRef.current = getDistance(e.touches);
startScaleRef.current = currentScaleRef.current;
element.style.transformOrigin = "0 0";
}
};
const handleTouchMove = (e) => {
if (e.touches.length === 2 && initialDistanceRef.current) {
e.preventDefault();
const newDistance = getDistance(e.touches);
const pinchScale = newDistance / initialDistanceRef.current;
const newScale = Math.max(1, startScaleRef.current * pinchScale);
// store relative scale so it can be applied on top of current width
pinchScaleRef.current = newScale / startScaleRef.current;
if (!frameRef.current) {
frameRef.current = requestAnimationFrame(() => {
element.style.transform = `scale(${pinchScaleRef.current})`;
frameRef.current = null;
});
}
}
};
const handleTouchEnd = () => {
if (pinchScaleRef.current) {
let relativeScale = pinchScaleRef.current;
const inlineTransform = element.style.transform;
if (inlineTransform && inlineTransform.startsWith("scale(")) {
const value = parseFloat(inlineTransform.slice(6, -1));
if (!isNaN(value)) {
relativeScale = value;
}
}
const finalScale = Math.max(1, startScaleRef.current * relativeScale);
const percent = parseFloat(((finalScale - 1) * 100).toFixed(2));
const unchanged =
Math.abs(finalScale - currentScaleRef.current) < 0.001;
pinchScaleRef.current = null;
if (frameRef.current) {
cancelAnimationFrame(frameRef.current);
frameRef.current = null;
}
if (unchanged) {
// if scale didn't actually change, reset transform right away
element.style.transform = "";
} else {
// keep transform applied until state update reflects the new scale
element.style.transform = `scale(${relativeScale})`;
}
setScale(finalScale);
setZoomPercent(percent);
// ensure refs reflect the latest value before next gesture starts
currentScaleRef.current = finalScale;
startScaleRef.current = finalScale;
}
initialDistanceRef.current = null;
};
element.addEventListener("touchstart", handleTouchStart, {
passive: false
});
element.addEventListener("touchmove", handleTouchMove, { passive: false });
element.addEventListener("touchend", handleTouchEnd);
element.addEventListener("touchcancel", handleTouchEnd);
return () => {
element.removeEventListener("touchstart", handleTouchStart);
element.removeEventListener("touchmove", handleTouchMove);
element.removeEventListener("touchend", handleTouchEnd);
element.removeEventListener("touchcancel", handleTouchEnd);
};
}, [ref, setScale, setZoomPercent]);
}