mirror of
https://github.com/haiwen/seahub.git
synced 2025-08-16 06:03:35 +00:00
fix: gallery render (#6742)
* fix: gallery render * fix: gallery render --------- Co-authored-by: 杨国璇 <ygx@Hello-word.local>
This commit is contained in:
parent
9e78a7aa93
commit
acbd02d071
@ -10,18 +10,14 @@ import toaster from '../../../../../components/toast';
|
|||||||
|
|
||||||
import './index.css';
|
import './index.css';
|
||||||
|
|
||||||
const CONCURRENCY_LIMIT = 3;
|
|
||||||
const IMAGE_GAP = 2;
|
const IMAGE_GAP = 2;
|
||||||
|
|
||||||
const Gallery = () => {
|
const Gallery = () => {
|
||||||
const imageRefs = useRef([]);
|
|
||||||
const containerRef = useRef(null);
|
const containerRef = useRef(null);
|
||||||
const [isFirstLoading, setFirstLoading] = useState(true);
|
const [isFirstLoading, setFirstLoading] = useState(true);
|
||||||
const [isLoadingMore, setLoadingMore] = useState(false);
|
const [isLoadingMore, setLoadingMore] = useState(false);
|
||||||
const [zoomGear, setZoomGear] = useState(0);
|
const [zoomGear, setZoomGear] = useState(0);
|
||||||
const [containerWidth, setContainerWidth] = 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 [overScan, setOverScan] = useState({ top: 0, bottom: 0 });
|
||||||
const renderMoreTimer = useRef(null);
|
const renderMoreTimer = useRef(null);
|
||||||
|
|
||||||
@ -66,22 +62,27 @@ const Gallery = () => {
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
let _groups = [];
|
let _groups = [];
|
||||||
|
const imageHeight = imageSize + IMAGE_GAP;
|
||||||
init.forEach((_init, index) => {
|
init.forEach((_init, index) => {
|
||||||
const { children } = _init;
|
const { children, ...__init } = _init;
|
||||||
const childrenCount = children.length;
|
|
||||||
const value = childrenCount / columns;
|
|
||||||
const rows = childrenCount % columns ? Math.ceil(value) : ~~(value);
|
|
||||||
const height = rows * (imageSize + IMAGE_GAP);
|
|
||||||
let top = 0;
|
let top = 0;
|
||||||
|
let rows = [];
|
||||||
if (index > 0) {
|
if (index > 0) {
|
||||||
const lastGroup = _groups[index - 1];
|
const lastGroup = _groups[index - 1];
|
||||||
const { top: lastGroupTop, height: lastGroupHeight } = lastGroup;
|
const { top: lastGroupTop, height: lastGroupHeight } = lastGroup;
|
||||||
top = lastGroupTop + lastGroupHeight;
|
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({
|
_groups.push({
|
||||||
..._init,
|
...__init,
|
||||||
top,
|
top,
|
||||||
height,
|
height,
|
||||||
|
children: rows
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
return _groups;
|
return _groups;
|
||||||
@ -105,35 +106,6 @@ const Gallery = () => {
|
|||||||
|
|
||||||
}, [isLoadingMore, metadata, store]);
|
}, [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(() => {
|
useEffect(() => {
|
||||||
const gear = window.sfMetadataContext.localStorage.getItem('zoom-gear', 0) || 0;
|
const gear = window.sfMetadataContext.localStorage.getItem('zoom-gear', 0) || 0;
|
||||||
setZoomGear(gear);
|
setZoomGear(gear);
|
||||||
@ -152,8 +124,8 @@ const Gallery = () => {
|
|||||||
|
|
||||||
// resize
|
// resize
|
||||||
const handleResize = () => {
|
const handleResize = () => {
|
||||||
if (!containerRef.current) return;
|
if (!container) return;
|
||||||
setContainerWidth(containerRef.current.offsetWidth);
|
setContainerWidth(container.offsetWidth);
|
||||||
};
|
};
|
||||||
const resizeObserver = new ResizeObserver(handleResize);
|
const resizeObserver = new ResizeObserver(handleResize);
|
||||||
container && resizeObserver.observe(container);
|
container && resizeObserver.observe(container);
|
||||||
@ -162,17 +134,13 @@ const Gallery = () => {
|
|||||||
const modifyGalleryZoomGearSubscribe = window.sfMetadataContext.eventBus.subscribe(EVENT_BUS_TYPE.MODIFY_GALLERY_ZOOM_GEAR, (zoomGear) => {
|
const modifyGalleryZoomGearSubscribe = window.sfMetadataContext.eventBus.subscribe(EVENT_BUS_TYPE.MODIFY_GALLERY_ZOOM_GEAR, (zoomGear) => {
|
||||||
window.sfMetadataContext.localStorage.setItem('zoom-gear', zoomGear);
|
window.sfMetadataContext.localStorage.setItem('zoom-gear', zoomGear);
|
||||||
setZoomGear(zoomGear);
|
setZoomGear(zoomGear);
|
||||||
|
setTimeout(() => {
|
||||||
|
container.scrollTop += zoomGear * 10;
|
||||||
|
}, 200);
|
||||||
});
|
});
|
||||||
return () => {
|
return () => {
|
||||||
container && resizeObserver.unobserve(container);
|
container && resizeObserver.unobserve(container);
|
||||||
modifyGalleryZoomGearSubscribe();
|
modifyGalleryZoomGearSubscribe();
|
||||||
|
|
||||||
// Cleanup image references on unmount
|
|
||||||
imageRefs.current.forEach(img => {
|
|
||||||
img.onload = null;
|
|
||||||
img.onerror = null;
|
|
||||||
});
|
|
||||||
imageRefs.current = [];
|
|
||||||
renderMoreTimer.current && clearTimeout(renderMoreTimer.current);
|
renderMoreTimer.current && clearTimeout(renderMoreTimer.current);
|
||||||
};
|
};
|
||||||
}, []);
|
}, []);
|
||||||
@ -194,17 +162,12 @@ const Gallery = () => {
|
|||||||
}
|
}
|
||||||
}, [imageSize, loadMore, renderMoreTimer]);
|
}, [imageSize, loadMore, renderMoreTimer]);
|
||||||
|
|
||||||
const addToQueue = (image) => {
|
|
||||||
setLoadingQueue(prev => [...prev, image]);
|
|
||||||
loadNextImage();
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="sf-metadata-container">
|
<div className="sf-metadata-container">
|
||||||
<div className="sf-metadata-gallery-container" ref={containerRef} onScroll={handleScroll} >
|
<div className="sf-metadata-gallery-container" ref={containerRef} onScroll={handleScroll} >
|
||||||
{!isFirstLoading && (
|
{!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>)}
|
{isLoadingMore && (<div className="sf-metadata-gallery-loading-more"><CenteredLoading /></div>)}
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
@ -1,25 +1,25 @@
|
|||||||
import React, { useCallback } from 'react';
|
import React, { useCallback, useMemo } from 'react';
|
||||||
import PropTypes from 'prop-types';
|
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 renderDisplayGroup = useCallback((group) => {
|
||||||
const { top: overScanTop, bottom: overScanBottom } = overScan;
|
const { top: overScanTop, bottom: overScanBottom } = overScan;
|
||||||
const { name, children, top, height } = group;
|
const { name, children, height, top } = group;
|
||||||
let childrenStartIndex = children.findIndex((r, i) => {
|
|
||||||
const rTop = ~~(i / columns) * (size + 2) + top;
|
// group not in render area, return empty div
|
||||||
return rTop >= overScanTop;
|
if (top >= overScanBottom || top + height <= overScanTop) {
|
||||||
});
|
return (<div key={name} className="w-100" style={{ height, flexShrink: 0 }}></div>);
|
||||||
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 childrenStartIndex = children.findIndex(r => r.top >= overScanTop);
|
||||||
|
let childrenEndIndex = children.findIndex(r => r.top >= overScanBottom);
|
||||||
if (childrenEndIndex === -1) {
|
if (childrenEndIndex === -1) {
|
||||||
childrenEndIndex = children.length - 1;
|
childrenEndIndex = children.length;
|
||||||
|
}
|
||||||
|
if (childrenEndIndex > 0) {
|
||||||
|
childrenEndIndex = childrenEndIndex - 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -29,24 +29,23 @@ const Main = ({ groups, overScan, columns, onLoad, size, gap }) => {
|
|||||||
className="metadata-gallery-image-list"
|
className="metadata-gallery-image-list"
|
||||||
style={{
|
style={{
|
||||||
gridTemplateColumns: `repeat(${columns}, 1fr)`,
|
gridTemplateColumns: `repeat(${columns}, 1fr)`,
|
||||||
paddingTop: ~~(childrenStartIndex / columns) * (size + gap),
|
paddingTop: childrenStartIndex * imageHeight,
|
||||||
paddingBottom: ~~((children.length - 1 - childrenEndIndex) / columns) * (size + gap),
|
paddingBottom: (children.length - 1 - childrenEndIndex) * imageHeight,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{children.slice(childrenStartIndex, childrenEndIndex).map((img) => (
|
{children.slice(childrenStartIndex, childrenEndIndex + 1).map((row) => {
|
||||||
<div key={img.src} tabIndex={1} className='metadata-gallery-image-item' style={{ width: size, height: size }}>
|
return row.children.map(img => {
|
||||||
<img
|
return (
|
||||||
className="metadata-gallery-grid-image"
|
<div key={img.src} tabIndex={1} className="metadata-gallery-image-item" style={{ width: size, height: size, background: '#f1f1f1' }}>
|
||||||
src={img.src}
|
<img className="metadata-gallery-grid-image" src={img.src} alt={img.name} />
|
||||||
alt={img.name}
|
</div>
|
||||||
onLoad={() => onLoad(img)}
|
);
|
||||||
/>
|
});
|
||||||
</div>
|
})}
|
||||||
))}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}, [overScan, columns, onLoad, size, gap]);
|
}, [overScan, columns, size, imageHeight]);
|
||||||
|
|
||||||
if (!Array.isArray(groups) || groups.length === 0) return null;
|
if (!Array.isArray(groups) || groups.length === 0) return null;
|
||||||
return groups.map((group, index) => {
|
return groups.map((group, index) => {
|
||||||
@ -58,7 +57,6 @@ Main.propTypes = {
|
|||||||
groups: PropTypes.array,
|
groups: PropTypes.array,
|
||||||
overScan: PropTypes.object,
|
overScan: PropTypes.object,
|
||||||
columns: PropTypes.number,
|
columns: PropTypes.number,
|
||||||
onLoad: PropTypes.func,
|
|
||||||
size: PropTypes.number,
|
size: PropTypes.number,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user