diff --git a/frontend/src/metadata/metadata-view/components/view/gallery/index.js b/frontend/src/metadata/metadata-view/components/view/gallery/index.js index 91384521ee..beaa25fc80 100644 --- a/frontend/src/metadata/metadata-view/components/view/gallery/index.js +++ b/frontend/src/metadata/metadata-view/components/view/gallery/index.js @@ -10,18 +10,14 @@ import toaster from '../../../../../components/toast'; import './index.css'; -const CONCURRENCY_LIMIT = 3; const IMAGE_GAP = 2; const Gallery = () => { - const imageRefs = useRef([]); const containerRef = useRef(null); const [isFirstLoading, setFirstLoading] = useState(true); const [isLoadingMore, setLoadingMore] = useState(false); const [zoomGear, setZoomGear] = useState(0); const [containerWidth, setContainerWidth] = useState(0); - const [loadingQueue, setLoadingQueue] = useState([]); - const [concurrentLoads, setConcurrentLoads] = useState(0); const [overScan, setOverScan] = useState({ top: 0, bottom: 0 }); const renderMoreTimer = useRef(null); @@ -66,22 +62,27 @@ const Gallery = () => { }, []); let _groups = []; + const imageHeight = imageSize + IMAGE_GAP; init.forEach((_init, index) => { - const { children } = _init; - const childrenCount = children.length; - const value = childrenCount / columns; - const rows = childrenCount % columns ? Math.ceil(value) : ~~(value); - const height = rows * (imageSize + IMAGE_GAP); + const { children, ...__init } = _init; let top = 0; + let rows = []; if (index > 0) { const lastGroup = _groups[index - 1]; const { top: lastGroupTop, height: lastGroupHeight } = lastGroup; top = lastGroupTop + lastGroupHeight; } + children.forEach((child, childIndex) => { + const rowIndex = ~~(childIndex / columns); + if (!rows[rowIndex]) rows[rowIndex] = { top: top + rowIndex * imageHeight, children: [] }; + rows[rowIndex].children.push(child); + }); + const height = rows.length * imageHeight; _groups.push({ - ..._init, + ...__init, top, height, + children: rows }); }); return _groups; @@ -105,35 +106,6 @@ const Gallery = () => { }, [isLoadingMore, metadata, store]); - const loadNextImage = useCallback(() => { - if (loadingQueue.length === 0 || concurrentLoads >= CONCURRENCY_LIMIT) return; - - const nextImage = loadingQueue.shift(); - setConcurrentLoads(prev => prev + 1); - - const img = new Image(); - imageRefs.current.push(img); - img.src = nextImage.src; - img.onload = () => { - setConcurrentLoads(prev => { - const newCount = prev - 1; - return newCount; - }); - loadNextImage(); - }; - img.onerror = () => { - setConcurrentLoads(prev => { - const newCount = prev - 1; - return newCount; - }); - loadNextImage(); - }; - }, [loadingQueue, concurrentLoads]); - - useEffect(() => { - loadNextImage(); - }, [loadingQueue, concurrentLoads, loadNextImage]); - useEffect(() => { const gear = window.sfMetadataContext.localStorage.getItem('zoom-gear', 0) || 0; setZoomGear(gear); @@ -152,8 +124,8 @@ const Gallery = () => { // resize const handleResize = () => { - if (!containerRef.current) return; - setContainerWidth(containerRef.current.offsetWidth); + if (!container) return; + setContainerWidth(container.offsetWidth); }; const resizeObserver = new ResizeObserver(handleResize); container && resizeObserver.observe(container); @@ -162,17 +134,13 @@ const Gallery = () => { const modifyGalleryZoomGearSubscribe = window.sfMetadataContext.eventBus.subscribe(EVENT_BUS_TYPE.MODIFY_GALLERY_ZOOM_GEAR, (zoomGear) => { window.sfMetadataContext.localStorage.setItem('zoom-gear', zoomGear); setZoomGear(zoomGear); + setTimeout(() => { + container.scrollTop += zoomGear * 10; + }, 200); }); return () => { container && resizeObserver.unobserve(container); modifyGalleryZoomGearSubscribe(); - - // Cleanup image references on unmount - imageRefs.current.forEach(img => { - img.onload = null; - img.onerror = null; - }); - imageRefs.current = []; renderMoreTimer.current && clearTimeout(renderMoreTimer.current); }; }, []); @@ -194,17 +162,12 @@ const Gallery = () => { } }, [imageSize, loadMore, renderMoreTimer]); - const addToQueue = (image) => { - setLoadingQueue(prev => [...prev, image]); - loadNextImage(); - }; - return (
{!isFirstLoading && ( <> -
+
{isLoadingMore && (
)} )} diff --git a/frontend/src/metadata/metadata-view/components/view/gallery/main.js b/frontend/src/metadata/metadata-view/components/view/gallery/main.js index cba5b01559..43a80c6c6e 100644 --- a/frontend/src/metadata/metadata-view/components/view/gallery/main.js +++ b/frontend/src/metadata/metadata-view/components/view/gallery/main.js @@ -1,25 +1,25 @@ -import React, { useCallback } from 'react'; +import React, { useCallback, useMemo } from 'react'; import PropTypes from 'prop-types'; -const Main = ({ groups, overScan, columns, onLoad, size, gap }) => { +const Main = ({ groups, overScan, columns, size, gap }) => { + const imageHeight = useMemo(() => size + gap, [size, gap]); const renderDisplayGroup = useCallback((group) => { const { top: overScanTop, bottom: overScanBottom } = overScan; - const { name, children, top, height } = group; - let childrenStartIndex = children.findIndex((r, i) => { - const rTop = ~~(i / columns) * (size + 2) + top; - return rTop >= overScanTop; - }); - childrenStartIndex = Math.max(childrenStartIndex, 0); - let childrenEndIndex = children.findIndex((r, i) => { - const rTop = ~~(i / columns) * (size + gap) + top; - return rTop >= overScanBottom; - }); - if (childrenEndIndex > -1 && childrenEndIndex !== 0) { - childrenEndIndex = childrenEndIndex - 1; + const { name, children, height, top } = group; + + // group not in render area, return empty div + if (top >= overScanBottom || top + height <= overScanTop) { + return (
); } + + const childrenStartIndex = children.findIndex(r => r.top >= overScanTop); + let childrenEndIndex = children.findIndex(r => r.top >= overScanBottom); if (childrenEndIndex === -1) { - childrenEndIndex = children.length - 1; + childrenEndIndex = children.length; + } + if (childrenEndIndex > 0) { + childrenEndIndex = childrenEndIndex - 1; } return ( @@ -29,24 +29,23 @@ const Main = ({ groups, overScan, columns, onLoad, size, gap }) => { className="metadata-gallery-image-list" style={{ gridTemplateColumns: `repeat(${columns}, 1fr)`, - paddingTop: ~~(childrenStartIndex / columns) * (size + gap), - paddingBottom: ~~((children.length - 1 - childrenEndIndex) / columns) * (size + gap), + paddingTop: childrenStartIndex * imageHeight, + paddingBottom: (children.length - 1 - childrenEndIndex) * imageHeight, }} > - {children.slice(childrenStartIndex, childrenEndIndex).map((img) => ( -
- {img.name} onLoad(img)} - /> -
- ))} + {children.slice(childrenStartIndex, childrenEndIndex + 1).map((row) => { + return row.children.map(img => { + return ( +
+ {img.name} +
+ ); + }); + })}
); - }, [overScan, columns, onLoad, size, gap]); + }, [overScan, columns, size, imageHeight]); if (!Array.isArray(groups) || groups.length === 0) return null; return groups.map((group, index) => { @@ -58,7 +57,6 @@ Main.propTypes = { groups: PropTypes.array, overScan: PropTypes.object, columns: PropTypes.number, - onLoad: PropTypes.func, size: PropTypes.number, };