1
0
mirror of https://github.com/upscayl/upscayl.git synced 2025-02-22 05:29:31 +01:00

171 lines
5.9 KiB
TypeScript
Raw Normal View History

2024-11-25 07:30:28 +05:30
import React, { useMemo, useRef, useState } from "react";
const LensViewer = ({
sanitizedImagePath,
sanitizedUpscaledImagePath,
}: {
sanitizedImagePath: string;
sanitizedUpscaledImagePath: string;
}) => {
2024-11-25 07:30:28 +05:30
const originalImageContainerRef = useRef<HTMLDivElement>(null);
const originalImageRef = useRef<HTMLImageElement>(null);
const [hoverPosition, setHoverPosition] = useState({
2024-11-25 10:08:51 +05:30
relativeMouseX: 0, // To show the lens horizontally within the image
mouseY: 0, // To show the lens vertically in the viewport
relativeMouseXPercent: 0,
relativeMouseYPercent: 0,
originalImageMouseX: 0,
originalImageMouseY: 0,
2024-11-25 07:30:28 +05:30
});
const zoomLevel = 4;
2024-10-30 20:18:32 +05:30
const handleMouseMove = (e: React.MouseEvent) => {
2024-11-25 07:30:28 +05:30
if (!originalImageRef.current || !originalImageContainerRef.current) return;
const containerRect =
originalImageContainerRef.current.getBoundingClientRect();
2024-11-25 10:08:51 +05:30
// Image w-h is actually equal to container w-h, even in object-fit: contain
let viewportImageWidth = originalImageRef.current.width;
let viewportImageHeight = originalImageRef.current.height;
let imageTop = 0;
const mouseX = e.clientX;
const mouseY = e.clientY;
const containerHeight = containerRect.height;
const containerLeft = containerRect.left; // Since sidebar pushes the container to the right
const containerTop = containerRect.top;
// Image is width-constrained
const originalImageAspectRatio =
2024-11-25 07:30:28 +05:30
originalImageRef.current.naturalWidth /
originalImageRef.current.naturalHeight;
2024-11-25 10:08:51 +05:30
viewportImageHeight = viewportImageWidth / originalImageAspectRatio;
// Divide by 2 because image is centered
imageTop = (containerHeight - viewportImageHeight) / 2;
2024-11-25 07:30:28 +05:30
2024-11-25 10:08:51 +05:30
// Find relative mouse position within the image
const relativeMouseX = mouseX - containerLeft;
const relativeMouseY = mouseY - imageTop;
2024-11-25 07:30:28 +05:30
// Check if the mouse is within the actual image boundaries
const isWithinImage =
2024-11-25 10:08:51 +05:30
relativeMouseX >= 0 &&
relativeMouseX <= viewportImageWidth &&
relativeMouseY >= 0 &&
relativeMouseY <= viewportImageHeight;
2024-11-25 07:30:28 +05:30
if (!isWithinImage) {
2024-11-25 10:08:51 +05:30
// Hide the lens if the mouse is outside the image
2024-11-25 07:30:28 +05:30
setHoverPosition({
2024-11-25 10:08:51 +05:30
relativeMouseXPercent: -1000,
relativeMouseYPercent: -1000,
relativeMouseX: -1000,
mouseY: -1000,
originalImageMouseX: -1000,
originalImageMouseY: -1000,
});
2024-11-25 07:30:28 +05:30
return;
}
2024-11-25 10:08:51 +05:30
// Find image-relative mouse position as percentage
const relativeMouseXPercent = (relativeMouseX / viewportImageWidth) * 100;
const relativeMouseYPercent = (relativeMouseY / viewportImageHeight) * 100;
2024-11-25 07:30:28 +05:30
2024-11-25 10:08:51 +05:30
// Find the pixel position within the image relative to the original image
2024-11-25 07:30:28 +05:30
const bgPosX =
2024-11-25 10:08:51 +05:30
(relativeMouseX / viewportImageWidth) *
originalImageRef.current.naturalWidth;
2024-11-25 07:30:28 +05:30
const bgPosY =
2024-11-25 10:08:51 +05:30
(relativeMouseY / viewportImageHeight) *
originalImageRef.current.naturalHeight;
2024-11-25 07:30:28 +05:30
setHoverPosition({
2024-11-25 10:08:51 +05:30
relativeMouseXPercent,
relativeMouseYPercent,
relativeMouseX,
mouseY,
originalImageMouseX: bgPosX,
originalImageMouseY: bgPosY,
2024-11-25 07:30:28 +05:30
});
};
2024-11-25 07:30:28 +05:30
const originalImage = useMemo(
() => "file:///" + sanitizedImagePath,
[sanitizedImagePath],
);
const upscaledImage = useMemo(
() => "file:///" + sanitizedUpscaledImagePath,
[sanitizedUpscaledImagePath],
);
2024-10-30 20:06:14 +05:30
return (
2024-11-25 07:30:28 +05:30
<div className="group relative flex h-full flex-col items-center">
2024-10-30 20:06:14 +05:30
{/* Main image container */}
<div
2024-11-25 07:30:28 +05:30
className="relative h-full w-full cursor-crosshair"
ref={originalImageContainerRef}
>
2024-12-16 16:18:57 +05:30
ORIGINAL IMAGE
2024-10-30 20:06:14 +05:30
<img
src={originalImage}
alt="Original"
className="h-full w-full object-contain"
2024-11-25 07:30:28 +05:30
onMouseMove={handleMouseMove}
ref={originalImageRef}
2024-10-30 20:06:14 +05:30
/>
2024-12-16 16:18:57 +05:30
{/* Lens */}
2024-10-30 20:18:32 +05:30
<div
2024-12-16 16:18:57 +05:30
className="pointer-events-none absolute hidden cursor-cell border border-primary bg-black/10 group-hover:block"
2024-10-30 20:18:32 +05:30
style={{
2024-11-25 10:08:51 +05:30
left: `${hoverPosition.relativeMouseX}px`,
top: `${hoverPosition.mouseY}px`,
2024-10-30 20:18:32 +05:30
transform: "translate(-50%, -50%)",
2024-12-16 16:18:57 +05:30
height: `48px`,
width: `48px`,
2024-10-30 20:18:32 +05:30
}}
/>
</div>
2024-10-30 20:06:14 +05:30
{/* Enlarged views for original and upscaled images */}
2024-10-30 20:18:32 +05:30
<div
2024-11-25 07:30:28 +05:30
className="pointer-events-none absolute hidden group-hover:flex"
2024-10-30 20:18:32 +05:30
style={{
2024-11-25 10:08:51 +05:30
left: `${hoverPosition.relativeMouseX}px`,
top: `${hoverPosition.mouseY + 25}px`,
2024-10-30 20:18:32 +05:30
transform: "translate(-50%, 0)",
}}
>
2024-12-16 16:18:57 +05:30
{/* ORIGINAL IMAGE */}
2024-10-30 20:06:14 +05:30
<div
2024-10-30 20:18:32 +05:30
className="relative h-48 w-48 border border-gray-300 bg-cover bg-no-repeat"
2024-10-30 20:06:14 +05:30
style={{
2024-10-30 20:18:32 +05:30
backgroundImage: `url(${originalImage})`,
2024-11-25 10:08:51 +05:30
backgroundPosition: `-${hoverPosition.originalImageMouseX * zoomLevel - 96}px -${hoverPosition.originalImageMouseY * zoomLevel - 96}px`,
2024-11-25 07:30:28 +05:30
backgroundSize: `${originalImageRef.current?.naturalWidth * zoomLevel}px ${originalImageRef.current?.naturalHeight * zoomLevel}px`,
2024-10-30 20:06:14 +05:30
}}
>
2024-11-25 07:30:28 +05:30
<span className="absolute bottom-0 w-full bg-black bg-opacity-60 px-2 py-1 text-center text-xs text-white">
2024-10-30 20:18:32 +05:30
Original
</span>
2024-10-30 20:06:14 +05:30
</div>
2024-12-16 16:18:57 +05:30
{/* UPSCALED IMAGE */}
2024-10-30 20:18:32 +05:30
<div
className="relative h-48 w-48 border border-gray-300 bg-cover bg-no-repeat"
style={{
backgroundImage: `url(${upscaledImage})`,
2024-11-25 10:08:51 +05:30
backgroundPosition: `-${hoverPosition.originalImageMouseX * zoomLevel - 96}px -${hoverPosition.originalImageMouseY * zoomLevel - 96}px`,
2024-11-25 07:30:28 +05:30
backgroundSize: `${originalImageRef.current?.naturalWidth * zoomLevel}px ${originalImageRef.current?.naturalHeight * zoomLevel}px`,
2024-10-30 20:18:32 +05:30
}}
>
2024-11-25 07:30:28 +05:30
<span className="absolute bottom-0 w-full bg-black bg-opacity-60 px-2 py-1 text-center text-xs text-white">
Upscayl AI
2024-10-30 20:18:32 +05:30
</span>
</div>
</div>
</div>
);
};
export default LensViewer;