[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 React, { memo } from '../../lib/teact/teact';
import React, { memo, useRef } from '../../lib/teact/teact';
import { getGlobal } from '../../global';
import type { ApiSticker } from '../../api/types';
@ -99,24 +99,30 @@ const StickerView: FC<OwnProps> = ({
&& isIntersectingForLoading
);
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);
// Use preview instead of thumb but only if it's already loaded or when playing an animation is disabled
const previewMediaDataFromCache: string | undefined = mediaLoader.getFromMemory(previewMediaHash);
const previewMediaData = useMedia(previewMediaHash, Boolean(previewMediaDataFromCache || !noPlay));
const thumbData = customColor ? thumbDataUri : (previewMediaData || thumbDataUri);
const thumbData = cachedPreview || previewMediaData || thumbDataUri;
const shouldForcePreview = isUnsupportedVideo || (isStatic && isSmall);
fullMediaHash = shouldForcePreview ? previewMediaHash : (fullMediaHash || `sticker${id}`);
// 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);
// No need to use full media if preview is enough and available
const shouldSkipFullMedia = Boolean(fullMediaHash === previewMediaHash && (cachedPreview || previewMediaData));
const fullMediaData = useMedia(fullMediaHash || `sticker${id}`, !shouldLoad || shouldSkipFullMedia);
// 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 isReadyToMount = useMountAfterHeavyAnimation();
const isFullMediaReady = isReadyToMount && fullMediaData && (isStatic || isPlayerReady);
const shouldRenderFullMedia = isReadyToMountFullMedia && fullMediaData;
const isFullMediaReady = isReadyToMountFullMedia && fullMediaData && (isStatic || isPlayerReady);
const isThumbOpaque = sharedCanvasRef && !withTranslucentThumb;
const thumbClassNames = useMediaTransition(thumbData && !isFullMediaReady);
@ -152,7 +158,7 @@ const StickerView: FC<OwnProps> = ({
alt=""
draggable={false}
/>
{isReadyToMount && (isLottie ? (
{shouldRenderFullMedia && (isLottie ? (
<AnimatedSticker
key={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';
export default function useMountAfterHeavyAnimation() {
const [isReadyToMount, markReadyToMount] = useFlag(false);
export default function useMountAfterHeavyAnimation(hasIntersected: boolean) {
const [getNoHeavyAnimation, setNoHeavyAnimation] = useSignal(false);
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(() => {
if (!$getIsHeavyAnimating()) {
markReadyToMount();
setNoHeavyAnimation(true);
}
}, [$getIsHeavyAnimating]);
}, [$getIsHeavyAnimating, setNoHeavyAnimation]);
return isReadyToMount;
return useDerivedState(() => (getNoHeavyAnimation() && hasIntersected), [getNoHeavyAnimation, hasIntersected]);
}