[Perf] Sticker View: Delay full media rendering

This commit is contained in:
Alexander Zinchuk 2024-09-06 15:42:36 +02:00
parent 28c9d9dc27
commit 3ac3ad394b
2 changed files with 29 additions and 23 deletions

View File

@ -1,5 +1,5 @@
import type { FC } from '../../lib/teact/teact'; import type { FC } from '../../lib/teact/teact';
import React, { memo } from '../../lib/teact/teact'; import React, { memo, useRef } from '../../lib/teact/teact';
import { getGlobal } from '../../global'; import { getGlobal } from '../../global';
import type { ApiSticker } from '../../api/types'; import type { ApiSticker } from '../../api/types';
@ -99,24 +99,30 @@ const StickerView: FC<OwnProps> = ({
&& isIntersectingForLoading && isIntersectingForLoading
); );
const shouldPlay = isIntersectingForPlaying && !noPlay; const shouldPlay = isIntersectingForPlaying && !noPlay;
const hasIntersectedForPlayingRef = useRef(isIntersectingForPlaying);
if (!hasIntersectedForPlayingRef.current && isIntersectingForPlaying) {
hasIntersectedForPlayingRef.current = true;
}
const isReadyToMountFullMedia = useMountAfterHeavyAnimation(hasIntersectedForPlayingRef.current);
const cachedPreview = mediaLoader.getFromMemory(previewMediaHash);
const shouldLoadPreview = !customColor && !cachedPreview && (
!isReadyToMountFullMedia || isUnsupportedVideo || (isStatic ? isSmall : noPlay)
);
const previewMediaData = useMedia(previewMediaHash, !shouldLoadPreview);
const thumbDataUri = useThumbnail(sticker); const thumbDataUri = useThumbnail(sticker);
// Use preview instead of thumb but only if it's already loaded or when playing an animation is disabled const thumbData = cachedPreview || previewMediaData || thumbDataUri;
const previewMediaDataFromCache: string | undefined = mediaLoader.getFromMemory(previewMediaHash);
const previewMediaData = useMedia(previewMediaHash, Boolean(previewMediaDataFromCache || !noPlay));
const thumbData = customColor ? thumbDataUri : (previewMediaData || thumbDataUri);
const shouldForcePreview = isUnsupportedVideo || (isStatic && isSmall); // No need to use full media if preview is enough and available
fullMediaHash = shouldForcePreview ? previewMediaHash : (fullMediaHash || `sticker${id}`); const shouldSkipFullMedia = Boolean(fullMediaHash === previewMediaHash && (cachedPreview || previewMediaData));
const fullMediaData = useMedia(fullMediaHash || `sticker${id}`, !shouldLoad || shouldSkipFullMedia);
// If preloaded preview is forced, it will render as thumb, so no need to load it again
const shouldSkipFullMedia = Boolean(fullMediaHash === previewMediaHash && previewMediaData);
const fullMediaData = useMedia(fullMediaHash, !shouldLoad || shouldSkipFullMedia);
// If Lottie data is loaded we will only render thumb if it's good enough (from preview) // If Lottie data is loaded we will only render thumb if it's good enough (from preview)
const [isPlayerReady, markPlayerReady] = useFlag(Boolean(isLottie && fullMediaData && !previewMediaData)); const [isPlayerReady, markPlayerReady] = useFlag(Boolean(isLottie && fullMediaData && !previewMediaData));
const isReadyToMount = useMountAfterHeavyAnimation();
const isFullMediaReady = isReadyToMount && fullMediaData && (isStatic || isPlayerReady); const shouldRenderFullMedia = isReadyToMountFullMedia && fullMediaData;
const isFullMediaReady = isReadyToMountFullMedia && fullMediaData && (isStatic || isPlayerReady);
const isThumbOpaque = sharedCanvasRef && !withTranslucentThumb; const isThumbOpaque = sharedCanvasRef && !withTranslucentThumb;
const thumbClassNames = useMediaTransition(thumbData && !isFullMediaReady); const thumbClassNames = useMediaTransition(thumbData && !isFullMediaReady);
@ -152,7 +158,7 @@ const StickerView: FC<OwnProps> = ({
alt="" alt=""
draggable={false} draggable={false}
/> />
{isReadyToMount && (isLottie ? ( {shouldRenderFullMedia && (isLottie ? (
<AnimatedSticker <AnimatedSticker
key={renderId} key={renderId}
renderId={renderId} renderId={renderId}

View File

@ -1,19 +1,19 @@
import { useEffect } from '../lib/teact/teact'; import { useEffect, useSignal } from '../lib/teact/teact';
import useFlag from './useFlag'; import useDerivedState from './useDerivedState';
import { getIsHeavyAnimating } from './useHeavyAnimationCheck'; import { getIsHeavyAnimating } from './useHeavyAnimationCheck';
export default function useMountAfterHeavyAnimation() { export default function useMountAfterHeavyAnimation(hasIntersected: boolean) {
const [isReadyToMount, markReadyToMount] = useFlag(false); const [getNoHeavyAnimation, setNoHeavyAnimation] = useSignal(false);
const $getIsHeavyAnimating = getIsHeavyAnimating; const $getIsHeavyAnimating = getIsHeavyAnimating;
// Animation is often started right after the mount, so we use effect to check for it on the next frame // Animation is usually started right after the mount, so we use effect to check for it on the next frame
useEffect(() => { useEffect(() => {
if (!$getIsHeavyAnimating()) { if (!$getIsHeavyAnimating()) {
markReadyToMount(); setNoHeavyAnimation(true);
} }
}, [$getIsHeavyAnimating]); }, [$getIsHeavyAnimating, setNoHeavyAnimation]);
return isReadyToMount; return useDerivedState(() => (getNoHeavyAnimation() && hasIntersected), [getNoHeavyAnimation, hasIntersected]);
} }