diff --git a/src/components/_shared/Lightbox/Lightbox.module.scss b/src/components/_shared/Lightbox/Lightbox.module.scss index 6f99e359..de7910d2 100644 --- a/src/components/_shared/Lightbox/Lightbox.module.scss +++ b/src/components/_shared/Lightbox/Lightbox.module.scss @@ -10,6 +10,9 @@ justify-content: center; z-index: 10000; + animation: 300ms fadeIn; + animation-fill-mode: forwards; + .image { max-width: 90%; max-height: 80%; @@ -72,3 +75,28 @@ } } } + +.fadeOut { + animation: 300ms fadeOut; + animation-fill-mode: backwards; +} + +@keyframes fadeIn { + 0% { + opacity: 0; + } + + 100% { + opacity: 1; + } +} + +@keyframes fadeOut { + 0% { + opacity: 1; + } + + 100% { + opacity: 0; + } +} diff --git a/src/components/_shared/Lightbox/Lightbox.tsx b/src/components/_shared/Lightbox/Lightbox.tsx index 56092245..89c7d94b 100644 --- a/src/components/_shared/Lightbox/Lightbox.tsx +++ b/src/components/_shared/Lightbox/Lightbox.tsx @@ -1,5 +1,5 @@ import { clsx } from 'clsx' -import { createSignal } from 'solid-js' +import { createMemo, createSignal, onCleanup } from 'solid-js' import { useEscKeyDownHandler } from '../../../utils/useEscKeyDownHandler' import { Icon } from '../Icon' @@ -8,40 +8,109 @@ import styles from './Lightbox.module.scss' type Props = { class?: string + imageAlt?: string image: string onClose: () => void } const ZOOM_STEP = 1.08 +const TRANSITION_SPEED = 300 + export const Lightbox = (props: Props) => { const [zoomLevel, setZoomLevel] = createSignal(1) + const [translateX, setTranslateX] = createSignal(0) + const [translateY, setTranslateY] = createSignal(0) + const [transitionEnabled, setTransitionEnabled] = createSignal(false) + + const lightboxRef: { + current: HTMLElement + } = { + current: null, + } const closeLightbox = () => { - props.onClose() + lightboxRef.current?.classList.add(styles.fadeOut) + + setTimeout(() => { + props.onClose() + }, 300) } const zoomIn = (event) => { event.stopPropagation() + setTransitionEnabled(true) setZoomLevel(zoomLevel() * ZOOM_STEP) + setTimeout(() => setTransitionEnabled(false), TRANSITION_SPEED) } + const zoomOut = (event) => { event.stopPropagation() + setTransitionEnabled(true) setZoomLevel(zoomLevel() / ZOOM_STEP) + setTimeout(() => setTransitionEnabled(false), TRANSITION_SPEED) } + const zoomReset = (event) => { event.stopPropagation() setZoomLevel(1) } - const lightboxStyle = () => ({ - transform: `scale(${zoomLevel()})`, - transition: 'transform 0.3s ease', - }) + const handleWheelZoom = (event) => { + event.preventDefault() + + let scale = zoomLevel() + + scale += event.deltaY * -0.01 + + scale = Math.min(Math.max(0.125, scale), 4) + + setZoomLevel(scale * ZOOM_STEP) + } useEscKeyDownHandler(closeLightbox) + let startX: number = 0 + let startY: number = 0 + let isDragging: boolean = false + + const onMouseDown: (event: MouseEvent) => void = (event) => { + startX = event.clientX - translateX() + startY = event.clientY - translateY() + isDragging = true + event.preventDefault() + } + + const onMouseMove: (event: MouseEvent) => void = (event) => { + if (isDragging) { + setTranslateX(event.clientX - startX) + setTranslateY(event.clientY - startY) + } + } + + const onMouseUp: () => void = () => { + isDragging = false + } + + document.addEventListener('mousemove', onMouseMove) + document.addEventListener('mouseup', onMouseUp) + + onCleanup(() => { + document.removeEventListener('mousemove', onMouseMove) + document.removeEventListener('mouseup', onMouseUp) + }) + + const lightboxStyle = createMemo(() => ({ + transform: `translate(${translateX()}px, ${translateY()}px) scale(${zoomLevel()})`, + transition: transitionEnabled() ? `transform ${TRANSITION_SPEED}ms ease-in-out` : '', + cursor: 'grab', + })) + return ( -