Adds touch support to lightbox

This commit is contained in:
Josh Perez 2021-10-04 16:12:14 -04:00 committed by GitHub
parent 48229332ea
commit 54e7cd21fc
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23

View file

@ -73,7 +73,14 @@ export function Lightbox({
const imageRef = useRef<HTMLImageElement | null>(null);
const [imagePanStyle, setImagePanStyle] = useState<CSSProperties>({});
const zoomCoordsRef = useRef<
| { screenWidth: number; screenHeight: number; x: number; y: number }
| {
initX: number;
initY: number;
screenWidth: number;
screenHeight: number;
x: number;
y: number;
}
| undefined
>();
@ -231,7 +238,8 @@ export function Lightbox({
};
}, [isViewOnce, isAttachmentGIF, onTimeUpdate, playVideo, videoElement]);
const positionImage = useCallback((ev?: MouseEvent) => {
const positionImage = useCallback(
(ev?: { clientX: number; clientY: number }) => {
const imageNode = imageRef.current;
const zoomCoords = zoomCoordsRef.current;
if (!imageNode || !zoomCoords) {
@ -244,7 +252,8 @@ export function Lightbox({
}
const shouldTransformX = imageNode.naturalWidth > zoomCoords.screenWidth;
const shouldTransformY = imageNode.naturalHeight > zoomCoords.screenHeight;
const shouldTransformY =
imageNode.naturalHeight > zoomCoords.screenHeight;
const nextImagePanStyle: CSSProperties = {
left: '50%',
@ -255,20 +264,30 @@ export function Lightbox({
let translateY = '-50%';
if (shouldTransformX) {
const scaleX =
(-1 / zoomCoords.screenWidth) *
(imageNode.offsetWidth - zoomCoords.screenWidth);
const offset = imageNode.offsetWidth - zoomCoords.screenWidth;
translateX = `${zoomCoords.x * scaleX}px`;
const scaleX = (-1 / zoomCoords.screenWidth) * offset;
const posX = Math.max(
0,
Math.min(zoomCoords.screenWidth, zoomCoords.x)
);
translateX = `${posX * scaleX}px`;
nextImagePanStyle.left = 0;
}
if (shouldTransformY) {
const scaleY =
(-1 / zoomCoords.screenHeight) *
(imageNode.offsetHeight - zoomCoords.screenHeight);
const offset = imageNode.offsetHeight - zoomCoords.screenHeight;
translateY = `${zoomCoords.y * scaleY}px`;
const scaleY = (-1 / zoomCoords.screenHeight) * offset;
const posY = Math.max(
0,
Math.min(zoomCoords.screenHeight, zoomCoords.y)
);
translateY = `${posY * scaleY}px`;
nextImagePanStyle.top = 0;
}
@ -276,7 +295,9 @@ export function Lightbox({
...nextImagePanStyle,
transform: `translate(${translateX}, ${translateY})`,
});
}, []);
},
[]
);
function canPanImage(): boolean {
const imageNode = imageRef.current;
@ -288,6 +309,29 @@ export function Lightbox({
);
}
const handleTouchMove = useCallback(
(ev: TouchEvent) => {
const imageNode = imageRef.current;
const zoomCoords = zoomCoordsRef.current;
ev.preventDefault();
ev.stopPropagation();
if (!imageNode || !zoomCoords) {
return;
}
const [touch] = ev.touches;
const { initX, initY } = zoomCoords;
positionImage({
clientX: initX + (initX - touch.clientX),
clientY: initY + (initY - touch.clientY),
});
},
[positionImage]
);
useEffect(() => {
const imageNode = imageRef.current;
let hasListener = false;
@ -295,14 +339,16 @@ export function Lightbox({
if (imageNode && zoomType !== ZoomType.None && canPanImage()) {
hasListener = true;
document.addEventListener('mousemove', positionImage);
document.addEventListener('touchmove', handleTouchMove);
}
return () => {
if (hasListener) {
document.removeEventListener('mousemove', positionImage);
document.removeEventListener('touchmove', handleTouchMove);
}
};
}, [positionImage, zoomType]);
}, [handleTouchMove, positionImage, zoomType]);
const caption = attachment?.caption;
@ -346,8 +392,10 @@ export function Lightbox({
if (canPanImage()) {
setZoomType(ZoomType.ZoomAndPan);
zoomCoordsRef.current = {
screenWidth: document.documentElement.clientWidth,
initX: event.clientX,
initY: event.clientY,
screenHeight: document.documentElement.clientHeight,
screenWidth: document.documentElement.clientWidth,
x: event.clientX,
y: event.clientY,
};