mirror of
https://github.com/jeffvli/feishin.git
synced 2026-05-07 04:20:12 +02:00
Improve virtual grid performance
This commit is contained in:
@@ -7,7 +7,7 @@ import { SimpleImg } from 'react-simple-img';
|
||||
import type { ListChildComponentProps } from 'react-window';
|
||||
import styled from 'styled-components';
|
||||
import { Text } from '/@/components/text';
|
||||
import type { PlayQueueAddOptions, LibraryItem, CardRow, CardRoute, Play } from '/@/types';
|
||||
import type { LibraryItem, CardRow, CardRoute, Play } from '/@/types';
|
||||
import GridCardControls from './grid-card-controls';
|
||||
|
||||
const CardWrapper = styled.div<{
|
||||
@@ -113,7 +113,6 @@ interface BaseGridCardProps {
|
||||
controls: {
|
||||
cardControls: any[];
|
||||
cardRows: CardRow[];
|
||||
handlePlayQueueAdd: (options: PlayQueueAddOptions) => void;
|
||||
itemType: LibraryItem;
|
||||
playButtonBehavior: Play;
|
||||
route: CardRoute;
|
||||
@@ -135,7 +134,7 @@ export const DefaultCard = ({
|
||||
sizes,
|
||||
}: BaseGridCardProps) => {
|
||||
const navigate = useNavigate();
|
||||
const { index, isScrolling } = listChildProps;
|
||||
const { index } = listChildProps;
|
||||
const { itemGap, itemHeight, itemWidth } = sizes;
|
||||
const { itemType, cardRows, route } = controls;
|
||||
|
||||
@@ -190,12 +189,10 @@ export const DefaultCard = ({
|
||||
</Center>
|
||||
)}
|
||||
<ControlsContainer>
|
||||
{!isScrolling && (
|
||||
<GridCardControls
|
||||
itemData={data}
|
||||
itemType={itemType}
|
||||
/>
|
||||
)}
|
||||
<GridCardControls
|
||||
itemData={data}
|
||||
itemType={itemType}
|
||||
/>
|
||||
</ControlsContainer>
|
||||
</ImageSection>
|
||||
<DetailSection>
|
||||
|
||||
@@ -42,9 +42,18 @@ const PlayButton = styled.button<PlayButtonType>`
|
||||
`;
|
||||
|
||||
const SecondaryButton = styled(_Button)`
|
||||
fill: white !important;
|
||||
svg: {
|
||||
fill: white !important;
|
||||
opacity: 0.8;
|
||||
transition: opacity 0.2s ease-in-out;
|
||||
transition: scale 0.2s linear;
|
||||
|
||||
&:hover {
|
||||
opacity: 1;
|
||||
scale: 1.1;
|
||||
}
|
||||
|
||||
&:active {
|
||||
opacity: 1;
|
||||
scale: 1;
|
||||
}
|
||||
`;
|
||||
|
||||
@@ -132,13 +141,7 @@ export const GridCardControls = ({
|
||||
{/* <TopControls /> */}
|
||||
{/* <CenterControls /> */}
|
||||
<BottomControls>
|
||||
<PlayButton
|
||||
// initial="initial"
|
||||
// variants={buttonVariants}
|
||||
// whileHover="hover"
|
||||
// whileTap="pressed"
|
||||
onClick={handlePlay}
|
||||
>
|
||||
<PlayButton onClick={handlePlay}>
|
||||
<RiPlayFill size={25} />
|
||||
</PlayButton>
|
||||
<Group spacing="xs">
|
||||
@@ -166,6 +169,7 @@ export const GridCardControls = ({
|
||||
<DropdownMenu.Target>
|
||||
<SecondaryButton
|
||||
p={5}
|
||||
sx={{ svg: { fill: 'white !important' } }}
|
||||
variant="subtle"
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
import { useMemo } from 'react';
|
||||
import { memo } from 'react';
|
||||
import type { ListChildComponentProps } from 'react-window';
|
||||
import { areEqual } from 'react-window';
|
||||
import { DefaultCard } from '/@/components/virtual-grid/grid-card/default-card';
|
||||
import { PosterCard } from '/@/components/virtual-grid/grid-card/poster-card';
|
||||
import type { GridCardData } from '/@/types';
|
||||
import { CardDisplayType } from '/@/types';
|
||||
|
||||
export const GridCard = ({ data, index, style, isScrolling }: ListChildComponentProps) => {
|
||||
export const GridCard = memo(({ data, index, style }: ListChildComponentProps) => {
|
||||
const {
|
||||
itemHeight,
|
||||
itemWidth,
|
||||
@@ -13,7 +14,6 @@ export const GridCard = ({ data, index, style, isScrolling }: ListChildComponent
|
||||
itemGap,
|
||||
itemCount,
|
||||
cardControls,
|
||||
handlePlayQueueAdd,
|
||||
cardRows,
|
||||
itemData,
|
||||
itemType,
|
||||
@@ -22,11 +22,8 @@ export const GridCard = ({ data, index, style, isScrolling }: ListChildComponent
|
||||
display,
|
||||
} = data as GridCardData;
|
||||
const cards = [];
|
||||
const startIndex = useMemo(() => index * columnCount, [columnCount, index]);
|
||||
const stopIndex = useMemo(
|
||||
() => Math.min(itemCount - 1, startIndex + columnCount - 1),
|
||||
[columnCount, itemCount, startIndex],
|
||||
);
|
||||
const startIndex = index * columnCount;
|
||||
const stopIndex = Math.min(itemCount - 1, startIndex + columnCount - 1);
|
||||
|
||||
const View = display === CardDisplayType.CARD ? DefaultCard : PosterCard;
|
||||
|
||||
@@ -38,28 +35,29 @@ export const GridCard = ({ data, index, style, isScrolling }: ListChildComponent
|
||||
controls={{
|
||||
cardControls,
|
||||
cardRows,
|
||||
handlePlayQueueAdd,
|
||||
itemType,
|
||||
playButtonBehavior,
|
||||
route,
|
||||
}}
|
||||
data={itemData[i]}
|
||||
listChildProps={{ index, isScrolling }}
|
||||
listChildProps={{ index }}
|
||||
sizes={{ itemGap, itemHeight, itemWidth }}
|
||||
/>,
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
...style,
|
||||
alignItems: 'center',
|
||||
display: 'flex',
|
||||
justifyContent: 'start',
|
||||
}}
|
||||
>
|
||||
{cards}
|
||||
</div>
|
||||
<>
|
||||
<div
|
||||
style={{
|
||||
...style,
|
||||
alignItems: 'center',
|
||||
display: 'flex',
|
||||
justifyContent: 'start',
|
||||
}}
|
||||
>
|
||||
{cards}
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
}, areEqual);
|
||||
|
||||
@@ -8,7 +8,7 @@ import type { ListChildComponentProps } from 'react-window';
|
||||
import styled from 'styled-components';
|
||||
import { Skeleton } from '/@/components/skeleton';
|
||||
import { Text } from '/@/components/text';
|
||||
import type { PlayQueueAddOptions, LibraryItem, CardRow, CardRoute, Play } from '/@/types';
|
||||
import type { LibraryItem, CardRow, CardRoute, Play } from '/@/types';
|
||||
import GridCardControls from './grid-card-controls';
|
||||
|
||||
const CardWrapper = styled.div<{
|
||||
@@ -117,7 +117,6 @@ interface BaseGridCardProps {
|
||||
columnIndex: number;
|
||||
controls: {
|
||||
cardRows: CardRow[];
|
||||
handlePlayQueueAdd: (options: PlayQueueAddOptions) => void;
|
||||
itemType: LibraryItem;
|
||||
playButtonBehavior: Play;
|
||||
route: CardRoute;
|
||||
@@ -166,7 +165,6 @@ export const PosterCard = ({
|
||||
height={sizes.itemWidth}
|
||||
importance="auto"
|
||||
placeholder={'var(--card-default-bg)'}
|
||||
// placeholder={data?.imagePlaceholderUrl || 'var(--card-default-bg)'}
|
||||
src={data?.imageUrl}
|
||||
width={sizes.itemWidth}
|
||||
/>
|
||||
@@ -184,14 +182,12 @@ export const PosterCard = ({
|
||||
/>
|
||||
</Center>
|
||||
)}
|
||||
{!listChildProps.isScrolling && (
|
||||
<ControlsContainer>
|
||||
<GridCardControls
|
||||
itemData={data}
|
||||
itemType={controls.itemType}
|
||||
/>
|
||||
</ControlsContainer>
|
||||
)}
|
||||
<ControlsContainer>
|
||||
<GridCardControls
|
||||
itemData={data}
|
||||
itemType={controls.itemType}
|
||||
/>
|
||||
</ControlsContainer>
|
||||
</ImageSection>
|
||||
</Link>
|
||||
<DetailSection>
|
||||
|
||||
@@ -1,11 +1,40 @@
|
||||
import type { Ref } from 'react';
|
||||
import { useMemo } from 'react';
|
||||
import debounce from 'lodash/debounce';
|
||||
import memoize from 'memoize-one';
|
||||
import type { FixedSizeListProps } from 'react-window';
|
||||
import { FixedSizeList } from 'react-window';
|
||||
import styled from 'styled-components';
|
||||
import { GridCard } from '/@/components/virtual-grid/grid-card';
|
||||
import type { CardRow, LibraryItem, CardDisplayType, CardRoute } from '/@/types';
|
||||
|
||||
const createItemData = memoize(
|
||||
(
|
||||
cardRows,
|
||||
columnCount,
|
||||
display,
|
||||
itemCount,
|
||||
itemData,
|
||||
itemGap,
|
||||
itemHeight,
|
||||
itemType,
|
||||
itemWidth,
|
||||
route,
|
||||
) => ({
|
||||
cardRows,
|
||||
columnCount,
|
||||
display,
|
||||
itemCount,
|
||||
itemData,
|
||||
itemGap,
|
||||
itemHeight,
|
||||
itemType,
|
||||
itemWidth,
|
||||
route,
|
||||
}),
|
||||
);
|
||||
|
||||
const createScrollHandler = memoize((onScroll) => debounce(onScroll, 250));
|
||||
|
||||
export const VirtualGridWrapper = ({
|
||||
refInstance,
|
||||
cardRows,
|
||||
@@ -35,44 +64,31 @@ export const VirtualGridWrapper = ({
|
||||
route?: CardRoute;
|
||||
rowCount: number;
|
||||
}) => {
|
||||
const memoizedItemData = useMemo(
|
||||
() => ({
|
||||
cardRows,
|
||||
columnCount,
|
||||
display,
|
||||
itemCount,
|
||||
itemData,
|
||||
itemGap,
|
||||
itemHeight,
|
||||
itemType,
|
||||
itemWidth,
|
||||
route,
|
||||
}),
|
||||
[
|
||||
cardRows,
|
||||
itemType,
|
||||
columnCount,
|
||||
itemCount,
|
||||
itemData,
|
||||
display,
|
||||
itemGap,
|
||||
itemHeight,
|
||||
route,
|
||||
itemWidth,
|
||||
],
|
||||
const memoizedItemData = createItemData(
|
||||
cardRows,
|
||||
columnCount,
|
||||
display,
|
||||
itemCount,
|
||||
itemData,
|
||||
itemGap,
|
||||
itemHeight,
|
||||
itemType,
|
||||
itemWidth,
|
||||
route,
|
||||
);
|
||||
|
||||
const memoizedOnScroll = createScrollHandler(onScroll);
|
||||
|
||||
return (
|
||||
<FixedSizeList
|
||||
ref={refInstance}
|
||||
{...rest}
|
||||
useIsScrolling
|
||||
initialScrollOffset={initialScrollOffset}
|
||||
itemCount={rowCount}
|
||||
itemData={memoizedItemData}
|
||||
itemSize={itemHeight}
|
||||
overscanCount={5}
|
||||
onScroll={onScroll}
|
||||
onScroll={memoizedOnScroll}
|
||||
>
|
||||
{GridCard}
|
||||
</FixedSizeList>
|
||||
|
||||
@@ -86,7 +86,7 @@ export const VirtualInfiniteGrid = ({
|
||||
[columnCount, fetchFn, itemData],
|
||||
);
|
||||
|
||||
const debouncedLoadMoreItems = debounce(loadMoreItems, 400);
|
||||
const debouncedLoadMoreItems = debounce(loadMoreItems, 500);
|
||||
|
||||
useEffect(() => {
|
||||
if (loader.current) {
|
||||
@@ -110,7 +110,6 @@ export const VirtualInfiniteGrid = ({
|
||||
>
|
||||
{({ onItemsRendered, ref: infiniteLoaderRef }) => (
|
||||
<VirtualGridWrapper
|
||||
useIsScrolling
|
||||
cardRows={cardRows}
|
||||
columnCount={columnCount}
|
||||
display={display || CardDisplayType.CARD}
|
||||
|
||||
Reference in New Issue
Block a user