1
0
mirror of https://github.com/haiwen/seahub.git synced 2025-08-01 23:38:37 +00:00

fix: gallery render (#6742)

* fix: gallery render

* fix: gallery render

---------

Co-authored-by: 杨国璇 <ygx@Hello-word.local>
This commit is contained in:
杨国璇 2024-09-10 12:00:51 +08:00 committed by GitHub
parent 9e78a7aa93
commit acbd02d071
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 44 additions and 83 deletions

View File

@ -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 (
<div className="sf-metadata-container">
<div className="sf-metadata-gallery-container" ref={containerRef} onScroll={handleScroll} >
{!isFirstLoading && (
<>
<Main groups={groups} size={imageSize} onLoad={addToQueue} columns={columns} overScan={overScan} gap={IMAGE_GAP} />
<Main groups={groups} size={imageSize} columns={columns} overScan={overScan} gap={IMAGE_GAP} />
{isLoadingMore && (<div className="sf-metadata-gallery-loading-more"><CenteredLoading /></div>)}
</>
)}

View File

@ -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 (<div key={name} className="w-100" style={{ height, flexShrink: 0 }}></div>);
}
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) => (
<div key={img.src} tabIndex={1} className='metadata-gallery-image-item' style={{ width: size, height: size }}>
<img
className="metadata-gallery-grid-image"
src={img.src}
alt={img.name}
onLoad={() => onLoad(img)}
/>
</div>
))}
{children.slice(childrenStartIndex, childrenEndIndex + 1).map((row) => {
return row.children.map(img => {
return (
<div key={img.src} tabIndex={1} className="metadata-gallery-image-item" style={{ width: size, height: size, background: '#f1f1f1' }}>
<img className="metadata-gallery-grid-image" src={img.src} alt={img.name} />
</div>
);
});
})}
</div>
</div>
);
}, [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,
};