diff --git a/src/components/node/NodeImageLazy/index.tsx b/src/components/node/NodeImageLazy/index.tsx index 0e1d62ab..ecc12381 100644 --- a/src/components/node/NodeImageLazy/index.tsx +++ b/src/components/node/NodeImageLazy/index.tsx @@ -1,51 +1,46 @@ -import { FC } from 'react'; +import { FC, ImgHTMLAttributes } from 'react'; import Image from 'next/future/image'; -import { imagePresets } from '~/constants/urls'; -import { IFile } from '~/types'; -import { normalizeBrightColor } from '~/utils/color'; -import { getURL } from '~/utils/dom'; - -interface NodeImageLazyProps { - className?: string; - file: IFile; +interface NodeImageLazyProps extends ImgHTMLAttributes { + color?: string; + quality?: number; onLoad?: () => void; - onClick?: () => void; } /** Separates SVG-s and raster images to be used in NodeImageSwiperBlock */ const NodeImageLazy: FC = ({ - file, + src, + color, onLoad, - className, - onClick, + width, + height, + sizes, + quality, + ...rest }) => { - if (file.url.endsWith('svg')) { + if (src?.endsWith('svg')) { return ( - + ); } + if (!src) { + return null; + } + return ( ); }; diff --git a/src/components/node/NodeImageSwiperBlock/helpers.ts b/src/components/node/NodeImageSwiperBlock/helpers.ts new file mode 100644 index 00000000..d8e438ee --- /dev/null +++ b/src/components/node/NodeImageSwiperBlock/helpers.ts @@ -0,0 +1,24 @@ +import { IFile } from '~/types'; + +const imageHeightPadding = 160; // px, as in styles.module.scss calc(100vh - Npx) + +/** Calculates image sizes for images with their aspect ration taken in account */ +export const getNodeSwiperImageSizes = ( + file: IFile, + windowWidth: number, + windowHeight: number, +) => { + const height = file.metadata?.height; + const width = file.metadata?.width; + + if (!height || !width) { + return undefined; + } + + const maxHeight = Math.min(height, windowHeight - imageHeightPadding); + const maxWidth = Math.min((maxHeight / height) * width, windowWidth); + + const size = Math.floor(Math.min((maxWidth / windowWidth) * 100, 100)); + + return `${size}vw`; +}; diff --git a/src/components/node/NodeImageSwiperBlock/index.tsx b/src/components/node/NodeImageSwiperBlock/index.tsx index f9063883..e78bf12e 100644 --- a/src/components/node/NodeImageSwiperBlock/index.tsx +++ b/src/components/node/NodeImageSwiperBlock/index.tsx @@ -2,52 +2,35 @@ import { FC, useCallback, useEffect, useMemo, useState } from 'react'; import classNames from 'classnames'; import { observer } from 'mobx-react-lite'; -import SwiperCore, { - Keyboard, - Lazy, - Navigation, - Pagination, - SwiperOptions, -} from 'swiper'; +import SwiperCore, { Keyboard, Lazy, Navigation, Pagination } from 'swiper'; import { Swiper, SwiperSlide } from 'swiper/react'; import SwiperClass from 'swiper/types/swiper-class'; import { ImageLoadingWrapper } from '~/components/common/ImageLoadingWrapper/index'; import { NodeComponentProps } from '~/constants/node'; import { imagePresets } from '~/constants/urls'; +import { useWindowSize } from '~/hooks/dom/useWindowSize'; import { useModal } from '~/hooks/modal/useModal'; import { useImageModal } from '~/hooks/navigation/useImageModal'; import { useNodeImages } from '~/hooks/node/useNodeImages'; +import { normalizeBrightColor } from '~/utils/color'; import { getURL } from '~/utils/dom'; import { NodeImageLazy } from '../NodeImageLazy'; +import { getNodeSwiperImageSizes } from './helpers'; +import { NODE_SWIPER_OPTIONS } from './options'; import styles from './styles.module.scss'; SwiperCore.use([Navigation, Pagination, Keyboard, Lazy]); interface IProps extends NodeComponentProps {} -const breakpoints: SwiperOptions['breakpoints'] = { - 599: { - spaceBetween: 20, - }, -}; - -const pagination = { type: 'fraction' as const }; - -const lazy = { - enabled: true, - loadPrevNextAmount: 1, - loadOnTransitionStart: true, - loadPrevNext: true, - checkInView: true, -}; - const NodeImageSwiperBlock: FC = observer(({ node }) => { const [controlledSwiper, setControlledSwiper] = useState(); const showPhotoSwiper = useImageModal(); const { isOpened: isModalActive } = useModal(); + const { innerWidth, innerHeight } = useWindowSize(); const images = useNodeImages(node); @@ -59,16 +42,6 @@ const NodeImageSwiperBlock: FC = observer(({ node }) => { [isModalActive], ); - const updateSwiper = useCallback(() => { - if (!controlledSwiper) return; - - controlledSwiper.updateSlides(); - controlledSwiper.updateSize(); - controlledSwiper.update(); - controlledSwiper.updateAutoHeight(); - controlledSwiper.updateProgress(); - }, [controlledSwiper]); - const onOpenPhotoSwipe = useCallback( (index: number) => { if ( @@ -108,8 +81,8 @@ const NodeImageSwiperBlock: FC = observer(({ node }) => { initialSlide={0} slidesPerView="auto" onSwiper={setControlledSwiper} - breakpoints={breakpoints} - pagination={pagination} + breakpoints={NODE_SWIPER_OPTIONS.breakpoints} + pagination={NODE_SWIPER_OPTIONS.pagination} centeredSlides observeSlideChildren observeParents @@ -123,7 +96,7 @@ const NodeImageSwiperBlock: FC = observer(({ node }) => { zoom navigation watchSlidesProgress - lazy={lazy} + lazy={NODE_SWIPER_OPTIONS.lazy} > {images.map((file, index) => ( @@ -133,12 +106,17 @@ const NodeImageSwiperBlock: FC = observer(({ node }) => { > {({ loading, onLoad }) => ( onOpenPhotoSwipe(index)} className={classNames(styles.image, 'swiper-lazy', { [styles.loading]: loading, })} + sizes={getNodeSwiperImageSizes(file, innerWidth, innerHeight)} + quality={90} /> )} diff --git a/src/components/node/NodeImageSwiperBlock/options.ts b/src/components/node/NodeImageSwiperBlock/options.ts new file mode 100644 index 00000000..6833aa34 --- /dev/null +++ b/src/components/node/NodeImageSwiperBlock/options.ts @@ -0,0 +1,17 @@ +import { SwiperOptions } from 'swiper'; + +export const NODE_SWIPER_OPTIONS: SwiperOptions = { + breakpoints: { + 599: { + spaceBetween: 20, + }, + }, + pagination: { type: 'fraction' as const }, + lazy: { + enabled: true, + loadPrevNextAmount: 1, + loadOnTransitionStart: true, + loadPrevNext: true, + checkInView: true, + }, +};