diff --git a/workloads/ecommerce/components/product/product-gallery.tsx b/workloads/ecommerce/components/product/product-gallery.tsx index cf2e7f3..e75689a 100644 --- a/workloads/ecommerce/components/product/product-gallery.tsx +++ b/workloads/ecommerce/components/product/product-gallery.tsx @@ -7,38 +7,49 @@ import { Dialog, DialogContent, DialogTrigger } from "@/components/ui/dialog"; interface Offset { x: number; y: number } interface DragState { pointerId: number; startX: number; startY: number; originX: number; originY: number } -interface HoverZoomOrigin { x: number; y: number } +interface HoverZoomState { lensLeft: number; lensTop: number; lensSize: number; bgX: number; bgY: number } -// Zoom automatico al pasar el cursor (estilo lupa clasico de e-commerce) -// sobre la imagen principal -- solo mouse (mousemove no dispara de forma -// continua en touch, asi que en mobile simplemente no aparece el efecto, -// sin romper nada). Independiente del zoom interactivo del modal -// (click -> Dialog con scale/drag), que sigue igual. +// Zoom estilo "lupa" de Amazon: un recuadro semitransparente sobre la +// imagen (la lupa) marca la region que se esta mirando, y un panel +// aparte a la derecha muestra esa misma region ampliada -- sin recortar +// nada de la pantalla ni pedirle al usuario que haga scroll para verlo +// completo (a diferencia de un primer intento que escalaba la imagen +// in-place con transform, donde para ver el borde superior había que +// mover el cursor fuera del viewport visible). Solo mouse (mousemove no +// dispara de forma continua en touch); en mobile no aparece el efecto, +// sin romper nada. Independiente del zoom interactivo del modal (click +// -> Dialog con scale/drag), que sigue igual. // -// El overlay de hover usa background-image (no transform:scale sobre el -// ) a proposito: escalar la imagen ya recortada por -// object-cover nunca iba a revelar las partes recortadas (ej. la cabeza -// de una figura de accion en una foto vertical) -- el recorte pasa antes -// del zoom, en el propio render. Con background-size en un solo valor -// (ancho%, alto en "auto") se preserva el aspect ratio real de la foto -// completa, y background-position 0%-100% permite recorrer TODA la -// imagen con el cursor, no solo la porcion que object-cover dejaba -// visible en reposo. +// El panel usa background-image con la foto completa (no la version ya +// recortada por object-cover) a proposito -- escalar la imagen recortada +// nunca iba a revelar las partes recortadas (ej. la cabeza de una figura +// de accion en una foto vertical). background-size en un solo valor +// (ancho%, alto en "auto") preserva el aspect ratio real de la foto +// completa. const HOVER_ZOOM_SCALE = 2.2; +const LENS_RATIO = 1 / HOVER_ZOOM_SCALE; export function ProductGallery({ images, name }: { images: string[]; name: string }) { const [selected, setSelected] = useState(images[0] ?? ""); const [open, setOpen] = useState(false); const [scale, setScale] = useState(1); const [offset, setOffset] = useState({ x: 0, y: 0 }); - const [hoverZoom, setHoverZoom] = useState(null); + const [hoverZoom, setHoverZoom] = useState(null); const drag = useRef(null); const trackHoverZoom = (event: MouseEvent) => { const rect = event.currentTarget.getBoundingClientRect(); + const lensSize = rect.width * LENS_RATIO; + const cursorX = event.clientX - rect.left; + const cursorY = event.clientY - rect.top; + const lensLeft = Math.min(Math.max(cursorX - lensSize / 2, 0), rect.width - lensSize); + const lensTop = Math.min(Math.max(cursorY - lensSize / 2, 0), rect.height - lensSize); setHoverZoom({ - x: ((event.clientX - rect.left) / rect.width) * 100, - y: ((event.clientY - rect.top) / rect.height) * 100, + lensLeft, + lensTop, + lensSize, + bgX: (lensLeft / (rect.width - lensSize)) * 100, + bgY: (lensTop / (rect.height - lensSize)) * 100, }); }; @@ -69,37 +80,50 @@ export function ProductGallery({ images, name }: { images: string[]; name: strin
{images.map((image) => )}
- - - - - +
+ + + + +
applyScale(scale > 1 ? 1 : 2)} style={{ touchAction: "none", cursor: scale > 1 ? "grab" : "zoom-in" }}>
@@ -115,8 +139,22 @@ export function ProductGallery({ images, name }: { images: string[]; name: strin {Math.round(scale * 100)}%
- -
+ +
+ + {hoverZoom && ( +
+ )} +
); }