diff --git a/src/components/common/ImageLoadingWrapper/index.tsx b/src/components/common/ImageLoadingWrapper/index.tsx new file mode 100644 index 00000000..3941a41b --- /dev/null +++ b/src/components/common/ImageLoadingWrapper/index.tsx @@ -0,0 +1,52 @@ +import React, { + CSSProperties, + FC, + useCallback, + useMemo, + useReducer, + useState, +} from 'react'; + +import classNames from 'classnames'; + +import { LoaderCircle } from '~/components/input/LoaderCircle'; +import { DivProps } from '~/utils/types'; + +import styles from './styles.module.scss'; + +interface ImageLoadingWrapperProps extends Omit { + children: (props: { loading: boolean; onLoad: () => void }) => void; + preview?: string; +} + +const ImageLoadingWrapper: FC = ({ + className, + children, + preview, + color, + ...props +}) => { + const [loading, onLoad] = useReducer((v) => false, true); + + const style = useMemo( + () => ({ + backgroundImage: `url('${preview}')`, + backgroundColor: color || 'var(--color-primary)', + }), + [preview, color], + ); + + return ( +
+ {!!loading && !!preview && ( +
+
+ +
+ )} + {children({ loading, onLoad })} +
+ ); +}; + +export { ImageLoadingWrapper }; diff --git a/src/components/common/ImageLoadingWrapper/styles.module.scss b/src/components/common/ImageLoadingWrapper/styles.module.scss new file mode 100644 index 00000000..7b2ced09 --- /dev/null +++ b/src/components/common/ImageLoadingWrapper/styles.module.scss @@ -0,0 +1,26 @@ +@import 'src/styles/variables'; + +.wrapper { + position: relative; +} + +.preview { + position: absolute; + inset: 0; + pointer-events: none; + touch-action: none; + display: flex; + align-items: flex-end; + justify-content: flex-end; + padding: $gap; +} + +.thumbnail { + filter: blur(10px); + position: absolute; + inset: 0; + border-radius: $radius; + background-size: cover; + background-position: 50% 50%; + box-sizing: border-box; +} diff --git a/src/components/node/NodeImageSwiperBlock/index.tsx b/src/components/node/NodeImageSwiperBlock/index.tsx index 07b4807b..50a9c2db 100644 --- a/src/components/node/NodeImageSwiperBlock/index.tsx +++ b/src/components/node/NodeImageSwiperBlock/index.tsx @@ -12,12 +12,14 @@ import SwiperCore, { import { Swiper, SwiperSlide } from 'swiper/react'; import SwiperClass from 'swiper/types/swiper-class'; -import { ImagePreloader } from '~/components/media/ImagePreloader'; +import { ImageLoadingWrapper } from '~/components/common/ImageLoadingWrapper/index'; import { INodeComponentProps } from '~/constants/node'; +import { imagePresets } from '~/constants/urls'; 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 { getFileSrcSet } from '~/utils/srcset'; import styles from './styles.module.scss'; @@ -101,18 +103,6 @@ const NodeImageSwiperBlock: FC = observer(({ node }) => { return null; } - if (images.length === 1) { - return ( -
- onOpenPhotoSwipe(0)} - className={styles.image} - /> -
- ); - } - return (
= observer(({ node }) => { watchSlidesProgress lazy={lazy} > - {images.map((file, i) => ( + {images.map((file, index) => ( - onOpenPhotoSwipe(i)} - className={classNames(styles.image, 'swiper-lazy')} - color={normalizeBrightColor(file?.metadata?.dominant_color)} - alt="" - sizes="(max-width: 560px) 100vw, 50vh" - /> + + {({ loading, onLoad }) => ( + onOpenPhotoSwipe(index)} + className={classNames(styles.image, 'swiper-lazy', { + [styles.loading]: loading, + })} + color={normalizeBrightColor(file?.metadata?.dominant_color)} + alt="" + sizes="(max-width: 560px) 100vw, 50vh" + /> + )} + ))} diff --git a/src/components/node/NodeImageSwiperBlock/styles.module.scss b/src/components/node/NodeImageSwiperBlock/styles.module.scss index 58d023ac..4e59a912 100644 --- a/src/components/node/NodeImageSwiperBlock/styles.module.scss +++ b/src/components/node/NodeImageSwiperBlock/styles.module.scss @@ -124,6 +124,10 @@ transition: box-shadow 1s; box-shadow: transparentize(black, 0.7) 0 3px 5px; + &.loading { + opacity: 0; + } + :global(.swiper-slide-active) & { box-shadow: transparentize(black, 0.9) 0 10px 5px 4px, transparentize(black, 0.7) 0 5px 5px, $gray_90 0 -1px 2px, $gray_90 0 -1px;