- If it is possible, HTMLImageElement.loading is used a lightweight native browser lazy loading solution - Custom lazyLoad library is created depending on IntersectionObserver. If IntersectionObserver is not available, the images are eagerly loaded - removed react-lazyload from the dependencies
This commit is contained in:
parent
b590413e76
commit
152b4c7e34
File diff suppressed because it is too large
Load Diff
|
@ -13,7 +13,6 @@
|
||||||
"react": "^17.0.1",
|
"react": "^17.0.1",
|
||||||
"react-dom": "^17.0.1",
|
"react-dom": "^17.0.1",
|
||||||
"react-helmet": "^6.1.0",
|
"react-helmet": "^6.1.0",
|
||||||
"react-lazyload": "^3.1.0",
|
|
||||||
"react-router-dom": "^5.2.0",
|
"react-router-dom": "^5.2.0",
|
||||||
"react-spring": "^8.0.27",
|
"react-spring": "^8.0.27",
|
||||||
"semantic-ui-css": "^2.4.1",
|
"semantic-ui-css": "^2.4.1",
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import React, { useCallback } from 'react'
|
import React, { useCallback, useEffect } from 'react'
|
||||||
import ReactRouterPropTypes from 'react-router-prop-types'
|
import ReactRouterPropTypes from 'react-router-prop-types'
|
||||||
import { useQuery, gql } from '@apollo/client'
|
import { useQuery, gql } from '@apollo/client'
|
||||||
import AlbumGallery from '../../components/albumGallery/AlbumGallery'
|
import AlbumGallery from '../../components/albumGallery/AlbumGallery'
|
||||||
|
@ -7,6 +7,7 @@ import Layout from '../../Layout'
|
||||||
import useURLParameters from '../../hooks/useURLParameters'
|
import useURLParameters from '../../hooks/useURLParameters'
|
||||||
import useScrollPagination from '../../hooks/useScrollPagination'
|
import useScrollPagination from '../../hooks/useScrollPagination'
|
||||||
import PaginateLoader from '../../components/PaginateLoader'
|
import PaginateLoader from '../../components/PaginateLoader'
|
||||||
|
import lazyLoad from '../../helpers/LazyLoad'
|
||||||
|
|
||||||
const albumQuery = gql`
|
const albumQuery = gql`
|
||||||
query albumQuery(
|
query albumQuery(
|
||||||
|
@ -124,6 +125,16 @@ function AlbumPage({ match }) {
|
||||||
[setOnlyFavorites, refetch]
|
[setOnlyFavorites, refetch]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
lazyLoad(document.querySelectorAll('img[data-src]'))
|
||||||
|
})
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!loading) {
|
||||||
|
lazyLoad(document.querySelectorAll('img[data-src]'))
|
||||||
|
}
|
||||||
|
}, [finishedLoadingMore, onlyFavorites, loading])
|
||||||
|
|
||||||
if (error) return <div>Error</div>
|
if (error) return <div>Error</div>
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
import React from 'react'
|
import React, { useEffect } from 'react'
|
||||||
import AlbumBoxes from '../../components/albumGallery/AlbumBoxes'
|
import AlbumBoxes from '../../components/albumGallery/AlbumBoxes'
|
||||||
import Layout from '../../Layout'
|
import Layout from '../../Layout'
|
||||||
import { useQuery, gql } from '@apollo/client'
|
import { useQuery, gql } from '@apollo/client'
|
||||||
|
import lazyLoad from '../../helpers/LazyLoad'
|
||||||
|
|
||||||
const getAlbumsQuery = gql`
|
const getAlbumsQuery = gql`
|
||||||
query getMyAlbums {
|
query getMyAlbums {
|
||||||
|
@ -20,6 +21,12 @@ const getAlbumsQuery = gql`
|
||||||
const AlbumsPage = () => {
|
const AlbumsPage = () => {
|
||||||
const { loading, error, data } = useQuery(getAlbumsQuery)
|
const { loading, error, data } = useQuery(getAlbumsQuery)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!loading) {
|
||||||
|
lazyLoad(document.querySelectorAll('img[data-src]'))
|
||||||
|
}
|
||||||
|
}, [loading])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Layout title="Albums">
|
<Layout title="Albums">
|
||||||
<h1>Albums</h1>
|
<h1>Albums</h1>
|
||||||
|
|
|
@ -2,7 +2,6 @@ import React, { useState } from 'react'
|
||||||
import { useMutation, gql } from '@apollo/client'
|
import { useMutation, gql } from '@apollo/client'
|
||||||
import PropTypes from 'prop-types'
|
import PropTypes from 'prop-types'
|
||||||
import styled from 'styled-components'
|
import styled from 'styled-components'
|
||||||
import LazyLoad from 'react-lazyload'
|
|
||||||
import { Icon } from 'semantic-ui-react'
|
import { Icon } from 'semantic-ui-react'
|
||||||
import { ProtectedImage } from './ProtectedMedia'
|
import { ProtectedImage } from './ProtectedMedia'
|
||||||
|
|
||||||
|
@ -35,31 +34,21 @@ const StyledPhoto = styled(ProtectedImage)`
|
||||||
transition: opacity 300ms;
|
transition: opacity 300ms;
|
||||||
`
|
`
|
||||||
|
|
||||||
const PhotoImg = photoProps => {
|
const LazyPhoto = photoProps => {
|
||||||
const [loaded, setLoaded] = useState(false)
|
const [loaded, setLoaded] = useState(false)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<StyledPhoto
|
<StyledPhoto
|
||||||
{...photoProps}
|
{...photoProps}
|
||||||
|
loading="lazy"
|
||||||
loaded={loaded ? 1 : 0}
|
loaded={loaded ? 1 : 0}
|
||||||
onLoad={() => {
|
onLoad={e => {
|
||||||
setLoaded(true)
|
!e.target.dataset.src && setLoaded(true)
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
const LazyPhoto = React.memo(
|
|
||||||
props => {
|
|
||||||
return (
|
|
||||||
<LazyLoad scrollContainer="#layout-content">
|
|
||||||
<PhotoImg {...props} />
|
|
||||||
</LazyLoad>
|
|
||||||
)
|
|
||||||
},
|
|
||||||
(prevProps, nextProps) => prevProps.src === nextProps.src
|
|
||||||
)
|
|
||||||
|
|
||||||
LazyPhoto.propTypes = {
|
LazyPhoto.propTypes = {
|
||||||
src: PropTypes.string,
|
src: PropTypes.string,
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import React, { useContext, useEffect } from 'react'
|
import React, { useContext } from 'react'
|
||||||
import styled from 'styled-components'
|
import styled from 'styled-components'
|
||||||
import { Loader } from 'semantic-ui-react'
|
import { Loader } from 'semantic-ui-react'
|
||||||
import { MediaThumbnail, PhotoThumbnail } from './MediaThumbnail'
|
import { MediaThumbnail, PhotoThumbnail } from './MediaThumbnail'
|
||||||
|
@ -6,7 +6,6 @@ import PresentView from './presentView/PresentView'
|
||||||
import PropTypes from 'prop-types'
|
import PropTypes from 'prop-types'
|
||||||
import { SidebarContext } from '../sidebar/Sidebar'
|
import { SidebarContext } from '../sidebar/Sidebar'
|
||||||
import MediaSidebar from '../sidebar/MediaSidebar'
|
import MediaSidebar from '../sidebar/MediaSidebar'
|
||||||
import { forceCheck } from 'react-lazyload'
|
|
||||||
|
|
||||||
const Gallery = styled.div`
|
const Gallery = styled.div`
|
||||||
display: flex;
|
display: flex;
|
||||||
|
@ -78,10 +77,6 @@ const PhotoGallery = ({
|
||||||
return photoElements
|
return photoElements
|
||||||
}
|
}
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
!loading && forceCheck()
|
|
||||||
}, [loading])
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ClearWrap>
|
<ClearWrap>
|
||||||
<Gallery>
|
<Gallery>
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import PropTypes from 'prop-types'
|
import PropTypes from 'prop-types'
|
||||||
|
|
||||||
|
const isNativeLazyLoadSupported = false //'loading' in HTMLImageElement.prototype
|
||||||
|
const placeholder = 'data:image/gif;base64,R0lGODlhAQABAPAAAAAAAAAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw=='
|
||||||
|
|
||||||
const getProtectedUrl = url => {
|
const getProtectedUrl = url => {
|
||||||
if (url == null) return null
|
if (url == null) return null
|
||||||
|
|
||||||
|
@ -18,17 +21,24 @@ const getProtectedUrl = url => {
|
||||||
/**
|
/**
|
||||||
* An image that needs authorization to load
|
* An image that needs authorization to load
|
||||||
*/
|
*/
|
||||||
export const ProtectedImage = ({ src, ...props }) => (
|
export const ProtectedImage = ({ src, loading, ...props }) => {
|
||||||
|
if (!isNativeLazyLoadSupported && loading === 'lazy') {
|
||||||
|
props['data-src'] = getProtectedUrl(src)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
<img
|
<img
|
||||||
key={src}
|
key={src}
|
||||||
{...props}
|
{...props}
|
||||||
src={getProtectedUrl(src)}
|
src={loading === 'lazy' ? placeholder : getProtectedUrl(src)}
|
||||||
crossOrigin="use-credentials"
|
crossOrigin="use-credentials"
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
|
}
|
||||||
|
|
||||||
ProtectedImage.propTypes = {
|
ProtectedImage.propTypes = {
|
||||||
src: PropTypes.string,
|
src: PropTypes.string,
|
||||||
|
loading: PropTypes.string,
|
||||||
}
|
}
|
||||||
|
|
||||||
export const ProtectedVideo = ({ media, ...props }) => (
|
export const ProtectedVideo = ({ media, ...props }) => (
|
||||||
|
|
|
@ -29,6 +29,7 @@ const PresentMedia = ({ media, imageLoaded, ...otherProps }) => {
|
||||||
<StyledPhoto
|
<StyledPhoto
|
||||||
style={{ display: 'none' }}
|
style={{ display: 'none' }}
|
||||||
src={media.highRes?.url}
|
src={media.highRes?.url}
|
||||||
|
loading="eager"
|
||||||
onLoad={e => {
|
onLoad={e => {
|
||||||
e.target.style.display = 'initial'
|
e.target.style.display = 'initial'
|
||||||
imageLoaded && imageLoaded()
|
imageLoaded && imageLoaded()
|
||||||
|
|
|
@ -9,6 +9,7 @@ import useURLParameters from '../../hooks/useURLParameters'
|
||||||
import { FavoritesCheckbox } from '../AlbumFilter'
|
import { FavoritesCheckbox } from '../AlbumFilter'
|
||||||
import useScrollPagination from '../../hooks/useScrollPagination'
|
import useScrollPagination from '../../hooks/useScrollPagination'
|
||||||
import PaginateLoader from '../PaginateLoader'
|
import PaginateLoader from '../PaginateLoader'
|
||||||
|
import lazyLoad from '../../helpers/LazyLoad'
|
||||||
|
|
||||||
const MY_TIMELINE_QUERY = gql`
|
const MY_TIMELINE_QUERY = gql`
|
||||||
query myTimeline($onlyFavorites: Boolean, $limit: Int, $offset: Int) {
|
query myTimeline($onlyFavorites: Boolean, $limit: Int, $offset: Int) {
|
||||||
|
@ -156,6 +157,12 @@ const TimelineGallery = () => {
|
||||||
}
|
}
|
||||||
}, [onlyFavorites])
|
}, [onlyFavorites])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!loading) {
|
||||||
|
lazyLoad(document.querySelectorAll('img[data-src]'))
|
||||||
|
}
|
||||||
|
}, [finishedLoadingMore, onlyFavorites, loading])
|
||||||
|
|
||||||
if (error) {
|
if (error) {
|
||||||
return error
|
return error
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,32 @@
|
||||||
|
const lazyLoad = elements => {
|
||||||
|
function setSrcAttribute(element) {
|
||||||
|
if (element.hasAttribute('data-src')) {
|
||||||
|
const src = element.getAttribute('data-src')
|
||||||
|
element.removeAttribute('data-src')
|
||||||
|
element.setAttribute('src', src)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function loadImage(observer, element) {
|
||||||
|
setSrcAttribute(element)
|
||||||
|
observer.unobserve(element)
|
||||||
|
}
|
||||||
|
|
||||||
|
const images = Array.from(elements)
|
||||||
|
if (images.length) {
|
||||||
|
if (!('IntersectionObserver' in window)) {
|
||||||
|
images.forEach(image => setSrcAttribute(image))
|
||||||
|
} else {
|
||||||
|
const observer = new IntersectionObserver(entries => {
|
||||||
|
entries.forEach(entry => {
|
||||||
|
if (entry.isIntersecting || entry.intersectionRatio > 0) {
|
||||||
|
loadImage(observer, entry.target)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
images.forEach(image => observer.observe(image))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default lazyLoad
|
Loading…
Reference in New Issue