From 92b273f377ba2ee4933602d37cb7b4c8f4cff079 Mon Sep 17 00:00:00 2001 From: Fedor Katurov Date: Mon, 21 Jun 2021 16:38:11 +0700 Subject: [PATCH] #38 added image preloaders --- package.json | 4 +- src/components/input/LoaderCircle/index.tsx | 11 ++- .../input/LoaderCircle/styles.module.scss | 5 -- .../input/LoaderCircleInner/index.tsx | 18 ++++ .../LoaderCircleInner/styles.module.scss | 5 ++ src/components/media/ImagePreloader/index.tsx | 90 +++++++++++++++++++ .../media/ImagePreloader/styles.module.scss | 30 +++++++ .../node/NodeImageSwiperBlock/index.tsx | 66 +++++++------- .../NodeImageSwiperBlock/styles.module.scss | 4 - yarn.lock | 21 ++--- 10 files changed, 192 insertions(+), 62 deletions(-) create mode 100644 src/components/input/LoaderCircleInner/index.tsx create mode 100644 src/components/input/LoaderCircleInner/styles.module.scss create mode 100644 src/components/media/ImagePreloader/index.tsx create mode 100644 src/components/media/ImagePreloader/styles.module.scss diff --git a/package.json b/package.json index 2cfbef4e..4ac467bf 100644 --- a/package.json +++ b/package.json @@ -30,13 +30,13 @@ "react-router": "^5.1.2", "react-router-dom": "^5.1.2", "react-scripts": "3.4.4", - "react-sortable-hoc": "^1.11", + "react-sortable-hoc": "^2.0.0", "react-sticky-box": "^0.9.3", "redux": "^4.0.1", "redux-persist": "^5.10.0", "redux-saga": "^1.1.1", "sticky-sidebar": "^3.3.1", - "swiper": "^6.5.0", + "swiper": "^6.7.0", "throttle-debounce": "^2.1.0", "typescript": "^4.0.5", "typograf": "^6.11.3", diff --git a/src/components/input/LoaderCircle/index.tsx b/src/components/input/LoaderCircle/index.tsx index c555f568..97b0b9c8 100644 --- a/src/components/input/LoaderCircle/index.tsx +++ b/src/components/input/LoaderCircle/index.tsx @@ -2,16 +2,15 @@ import React, { FC } from 'react'; import styles from './styles.module.scss'; import { describeArc } from '~/utils/dom'; import classNames from 'classnames'; +import { LoaderCircleInner } from '~/components/input/LoaderCircleInner'; interface IProps { size?: number; + className?: string; } -export const LoaderCircle: FC = ({ size = 24 }) => ( -
- - - - +export const LoaderCircle: FC = ({ size = 24, className }) => ( +
+
); diff --git a/src/components/input/LoaderCircle/styles.module.scss b/src/components/input/LoaderCircle/styles.module.scss index ff660f9c..51048769 100644 --- a/src/components/input/LoaderCircle/styles.module.scss +++ b/src/components/input/LoaderCircle/styles.module.scss @@ -1,10 +1,5 @@ @import "src/styles/variables"; -.icon { - fill: transparentize(black, 0.6); - stroke: none; -} - @keyframes spin { 0% { transform: rotate(0); diff --git a/src/components/input/LoaderCircleInner/index.tsx b/src/components/input/LoaderCircleInner/index.tsx new file mode 100644 index 00000000..c7daef9a --- /dev/null +++ b/src/components/input/LoaderCircleInner/index.tsx @@ -0,0 +1,18 @@ +import React, { FC, HTMLAttributes, SVGAttributes } from 'react'; +import { describeArc } from '~/utils/dom'; +import styles from './styles.module.scss'; +import classNames from 'classnames'; + +interface IProps extends SVGAttributes { + size: number; + className?: string; +} + +const LoaderCircleInner: FC = ({ size, className, ...props }) => ( + + + + +); + +export { LoaderCircleInner }; diff --git a/src/components/input/LoaderCircleInner/styles.module.scss b/src/components/input/LoaderCircleInner/styles.module.scss new file mode 100644 index 00000000..373c610f --- /dev/null +++ b/src/components/input/LoaderCircleInner/styles.module.scss @@ -0,0 +1,5 @@ + +.icon { + fill: transparentize(black, 0.6); + stroke: none; +} diff --git a/src/components/media/ImagePreloader/index.tsx b/src/components/media/ImagePreloader/index.tsx new file mode 100644 index 00000000..6eed8472 --- /dev/null +++ b/src/components/media/ImagePreloader/index.tsx @@ -0,0 +1,90 @@ +import React, { FC, MouseEventHandler, useCallback, useEffect, useState } from 'react'; +import classNames from 'classnames'; +import { describeArc, getURL } from '~/utils/dom'; +import { PRESETS } from '~/constants/urls'; +import styles from './styles.module.scss'; +import { IFile } from '~/redux/types'; +import { LoaderCircleInner } from '~/components/input/LoaderCircleInner'; +import { LoaderCircle } from '~/components/input/LoaderCircle'; + +interface IProps { + file: IFile; + onLoad?: () => void; + onClick?: MouseEventHandler; + className?: string; +} + +const ImagePreloader: FC = ({ file, onLoad, onClick, className }) => { + const [maxHeight, setMaxHeight] = useState(window.innerHeight - 140); + const [loaded, setLoaded] = useState(false); + + const onImageLoad = useCallback(() => { + setLoaded(true); + + if (onLoad) { + onLoad(); + } + }, [setLoaded, onLoad]); + + const onResize = useCallback(() => { + setMaxHeight(window.innerHeight - 140); + }, [setMaxHeight]); + + useEffect(() => { + window.addEventListener('resize', onResize); + + return () => window.removeEventListener('resize', onResize); + }, [onResize]); + + return ( + <> + + + + + + + + + + + + + + + {!loaded && } + + ); +}; + +export { ImagePreloader }; diff --git a/src/components/media/ImagePreloader/styles.module.scss b/src/components/media/ImagePreloader/styles.module.scss new file mode 100644 index 00000000..e792017a --- /dev/null +++ b/src/components/media/ImagePreloader/styles.module.scss @@ -0,0 +1,30 @@ +@import "~/styles/variables.scss"; + +.image { + position: absolute; + opacity: 0; + + &.is_loaded { + opacity: 1; + position: static; + } +} + +.preview { + border-radius: $radius; + + &.is_loaded { + display: none; + } +} + +.icon { + position: absolute; + right: 30px; + bottom: 40px; + opacity: 0.4; + + svg { + fill: currentColor; + } +} diff --git a/src/components/node/NodeImageSwiperBlock/index.tsx b/src/components/node/NodeImageSwiperBlock/index.tsx index c44f3cb1..fcd99bef 100644 --- a/src/components/node/NodeImageSwiperBlock/index.tsx +++ b/src/components/node/NodeImageSwiperBlock/index.tsx @@ -8,7 +8,7 @@ import 'swiper/components/navigation/navigation.scss'; import 'swiper/components/lazy/lazy.min.css'; import styles from './styles.module.scss'; -import SwiperCore, { Keyboard, Navigation, Pagination, SwiperOptions, Lazy } from 'swiper'; +import SwiperCore, { Keyboard, Navigation, Pagination, SwiperOptions } from 'swiper'; import { useNodeImages } from '~/utils/hooks/node/useNodeImages'; import SwiperClass from 'swiper/types/swiper-class'; @@ -17,8 +17,9 @@ import { useDispatch } from 'react-redux'; import classNames from 'classnames'; import { getURL } from '~/utils/dom'; import { PRESETS } from '~/constants/urls'; +import { ImagePreloader } from '~/components/media/ImagePreloader'; -SwiperCore.use([Lazy, Navigation, Pagination, Keyboard]); +SwiperCore.use([Navigation, Pagination, Keyboard]); interface IProps extends INodeComponentProps {} @@ -41,21 +42,22 @@ const NodeImageSwiperBlock: FC = ({ node }) => { controlledSwiper.updateSize(); controlledSwiper.update(); controlledSwiper.updateAutoHeight(); + controlledSwiper.updateProgress(); }, [controlledSwiper]); - const resetSwiper = useCallback(() => { - if (!controlledSwiper) return; - controlledSwiper.slideTo(0, 0); - setTimeout(() => controlledSwiper.slideTo(0, 0), 100); - setTimeout(() => controlledSwiper.slideTo(0, 0), 300); - }, [controlledSwiper]); + // const resetSwiper = useCallback(() => { + // if (!controlledSwiper) return; + // controlledSwiper.slideTo(0, 0); + // setTimeout(() => controlledSwiper.slideTo(0, 0), 100); + // setTimeout(() => controlledSwiper.slideTo(0, 0), 300); + // }, [controlledSwiper]); - useEffect(() => { - updateSwiper(); - resetSwiper(); - - return () => setControlledSwiper(undefined); - }, [images, updateSwiper, resetSwiper, setControlledSwiper]); + // useEffect(() => { + // updateSwiper(); + // resetSwiper(); + // + // return () => setControlledSwiper(undefined); + // }, [images, updateSwiper, resetSwiper, setControlledSwiper]); const onOpenPhotoSwipe = useCallback(() => { dispatch(modalShowPhotoswipe(images, controlledSwiper?.activeIndex || 0)); @@ -74,13 +76,12 @@ const NodeImageSwiperBlock: FC = ({ node }) => { breakpoints={breakpoints} pagination={{ type: 'fraction' }} centeredSlides - observeSlideChildren - observeParents - resizeObserver - watchOverflow - updateOnImagesReady - onInit={resetSwiper} - lazy + // observeSlideChildren + // observeParents + // resizeObserver + // watchOverflow + // updateOnImagesReady + // onInit={resetSwiper} keyboard={{ enabled: true, onlyInViewport: false, @@ -92,20 +93,21 @@ const NodeImageSwiperBlock: FC = ({ node }) => { > {images.map(file => ( - {node.title} - -
+ {/* + {node.title} + */} ))} diff --git a/src/components/node/NodeImageSwiperBlock/styles.module.scss b/src/components/node/NodeImageSwiperBlock/styles.module.scss index c1c82fb9..eb3b14de 100644 --- a/src/components/node/NodeImageSwiperBlock/styles.module.scss +++ b/src/components/node/NodeImageSwiperBlock/styles.module.scss @@ -100,10 +100,6 @@ box-shadow: transparentize(black, 0.7) 0 3px 5px; opacity: 0; - &[src] { - opacity: 1; - } - :global(.swiper-slide-active) & { box-shadow: transparentize(black, 0.9) 0 10px 5px 4px, transparentize(black, 0.7) 0 5px 5px, diff --git a/yarn.lock b/yarn.lock index 2ec8001b..681b82ce 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9499,10 +9499,10 @@ react-scripts@3.4.4: optionalDependencies: fsevents "2.1.2" -react-sortable-hoc@^1.11: - version "1.11.0" - resolved "https://registry.yarnpkg.com/react-sortable-hoc/-/react-sortable-hoc-1.11.0.tgz#fe4022362bbafc4b836f5104b9676608a40a278f" - integrity sha512-v1CDCvdfoR3zLGNp6qsBa4J1BWMEVH25+UKxF/RvQRh+mrB+emqtVHMgZ+WreUiKJoEaiwYoScaueIKhMVBHUg== +react-sortable-hoc@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/react-sortable-hoc/-/react-sortable-hoc-2.0.0.tgz#f6780d8aa4b922a21f3e754af542f032677078b7" + integrity sha512-JZUw7hBsAHXK7PTyErJyI7SopSBFRcFHDjWW5SWjcugY0i6iH7f+eJkY8cJmGMlZ1C9xz1J3Vjz0plFpavVeRg== dependencies: "@babel/runtime" "^7.2.0" invariant "^2.2.4" @@ -9847,11 +9847,6 @@ resize-observer-polyfill@^1.5.1: resolved "https://registry.yarnpkg.com/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz#0e9020dd3d21024458d4ebd27e23e40269810464" integrity sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg== -resize-sensor@^0.0.6: - version "0.0.6" - resolved "https://registry.yarnpkg.com/resize-sensor/-/resize-sensor-0.0.6.tgz#75147dcb273de6832760e461d2e28de6dcf88c45" - integrity sha512-e+3wwdki9elemYP6AnyG2BK9/Gd7ak46wZN+Z62WwmWfhn2La1XV2rPRRIcar+PhRhfiQDXi29TapGMTIbI3Pg== - resolve-cwd@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-2.0.0.tgz#00a9f7387556e27038eae232caa372a6a59b665a" @@ -10873,10 +10868,10 @@ svgo@^1.0.0, svgo@^1.2.2: unquote "~1.1.1" util.promisify "~1.0.0" -swiper@^6.5.0: - version "6.5.9" - resolved "https://registry.yarnpkg.com/swiper/-/swiper-6.5.9.tgz#ed6caa8bd9fd44d314944210551ce297d4fb09c7" - integrity sha512-zO3UCLVEiOXZontAQWBNpWFZGV3WaXwHSgvng0qIGLVMyxYGD6w78S7YkGAu/XBam1SBQNZzxqfFc/LDjNdq/A== +swiper@^6.7.0: + version "6.7.0" + resolved "https://registry.yarnpkg.com/swiper/-/swiper-6.7.0.tgz#ec971e703d03ef6196354140fbb22b074642bf5c" + integrity sha512-zCfvWn7H7mCq7jgVurckhAwkjPUeMCkdC4rA7lagvaD3mIrNhKiaYYo2+nkxMVpiaWuCQ38e44Mya/dKb7HpyQ== dependencies: dom7 "^3.0.0" ssr-window "^3.0.0"