mirror of
https://github.com/fluxerapp/fluxer
synced 2026-10-18 21:45:31 +09:00
perf(app): cut idle renderer CPU and unbounded memory growth (#1724)
This commit is contained in:
1 parent
b4a5eb77a8
commit
5660972c71
13 files changed
+453
-345
No files matched your search
@@ -21,6 +21,7 @@ import {
|
||||
reduceGIFVRenderState,
|
||||
} from '@app/features/channel/components/embeds/media/GIFVRenderStateMachine';
|
||||
import {GifIndicator} from '@app/features/channel/components/embeds/media/GifIndicator';
|
||||
import {useGifViewportGate} from '@app/features/channel/components/embeds/media/GifViewportGate';
|
||||
import {getMediaButtonVisibility} from '@app/features/channel/components/embeds/media/MediaButtonUtils';
|
||||
import {MediaContainer, shouldShowOverlays} from '@app/features/channel/components/embeds/media/MediaContainer';
|
||||
import type {BaseMediaProps} from '@app/features/channel/components/embeds/media/MediaTypes';
|
||||
@@ -985,9 +986,15 @@ export const EmbedGif: FC<
|
||||
const imgRef = useRef<HTMLImageElement>(null);
|
||||
const animatedImageRef = useRef<HTMLImageElement>(null);
|
||||
const freezeCanvasRef = useRef<HTMLCanvasElement>(null);
|
||||
const {ref: visibilityRef, isNearViewport} = useNearViewport<HTMLDivElement>({
|
||||
disabled: !isMobile,
|
||||
const {shouldBlur, gateReason, canReveal, reveal: revealSensitiveMedia} = useMatureMedia(nsfw, channelId);
|
||||
const {
|
||||
ref: visibilityRef,
|
||||
loadMedia: shouldLoadMedia,
|
||||
animate: shouldAnimate,
|
||||
} = useGifViewportGate<HTMLDivElement>({
|
||||
element: containerElement,
|
||||
rememberKey: proxyURL,
|
||||
shouldBlur,
|
||||
});
|
||||
const setContainerRef = useCallback(
|
||||
(node: HTMLDivElement | null) => {
|
||||
@@ -1018,8 +1025,6 @@ export const EmbedGif: FC<
|
||||
Math.round(displayHeight * 2),
|
||||
);
|
||||
const animatedImageSourceRef = useRef(optimizedAnimatedURL);
|
||||
const {shouldBlur, gateReason, canReveal, reveal: revealSensitiveMedia} = useMatureMedia(nsfw, channelId);
|
||||
const shouldLoadMedia = isNearViewport && !shouldBlur;
|
||||
const {
|
||||
loaded,
|
||||
error,
|
||||
@@ -1049,7 +1054,7 @@ export const EmbedGif: FC<
|
||||
status: ImageCacheUtils.hasImage(optimizedAnimatedURL) ? 'ready' : 'idle',
|
||||
});
|
||||
const [decoderRequested, setDecoderRequested] = useState(() => getAnimatedMediaPlaybackAllowed());
|
||||
const shouldUseDecoder = shouldLoadMedia && hasStartedAnimating;
|
||||
const shouldUseDecoder = shouldAnimate && hasStartedAnimating;
|
||||
const decoderPlaying =
|
||||
shouldUseDecoder && decoderRequested && animatedMediaPlaybackAllowed && (gifAutoPlay || isHoveredState);
|
||||
const decoderState = useAnimatedImageDecoder({
|
||||
@@ -1061,7 +1066,7 @@ export const EmbedGif: FC<
|
||||
const useDecoder =
|
||||
shouldUseDecoder && decoderRequested && decoderState.supported && hasStartedAnimating && !decoderState.error;
|
||||
const decoderCanvasVisible = useDecoder && decoderState.loaded;
|
||||
const shouldRenderFreezeFrame = !decoderCanvasVisible && (gifAutoPlay || hasStartedAnimating);
|
||||
const shouldRenderFreezeFrame = shouldAnimate && !decoderCanvasVisible && (gifAutoPlay || hasStartedAnimating);
|
||||
const setAnimatedImageRef = useCallback(
|
||||
(node: HTMLImageElement | null) => {
|
||||
animatedImageRef.current = node;
|
||||
@@ -1406,7 +1411,7 @@ export const EmbedGif: FC<
|
||||
...(aspectRatio ? {aspectRatio} : {}),
|
||||
} as React.CSSProperties;
|
||||
const shouldUseAnimatedImage =
|
||||
shouldLoadMedia &&
|
||||
shouldAnimate &&
|
||||
animatedMediaPlaybackAllowed &&
|
||||
!decoderCanvasVisible &&
|
||||
(gifAutoPlay || (hasStartedAnimating && isHoveredState));
|
||||
|
||||
@@ -356,9 +356,9 @@ const EmbedVideo: FC<EmbedVideoProps> = observer(
|
||||
() => {
|
||||
if (!active) return;
|
||||
setLoadedPosterSrc(posterSrc);
|
||||
const image = ImageCacheUtils.getImage(posterSrc);
|
||||
if (active && image && image.naturalWidth > 0 && image.naturalHeight > 0) {
|
||||
setPosterNaturalDimensions({src: posterSrc, width: image.naturalWidth, height: image.naturalHeight});
|
||||
const posterSize = ImageCacheUtils.getImageSize(posterSrc);
|
||||
if (active && posterSize) {
|
||||
setPosterNaturalDimensions({src: posterSrc, width: posterSize.width, height: posterSize.height});
|
||||
}
|
||||
},
|
||||
() => {
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
import {useNearViewport} from '@app/features/messaging/hooks/useNearViewport';
|
||||
import {observeIntersection} from '@app/features/platform/utils/SharedIntersectionObserver';
|
||||
import {useEffect, useState} from 'react';
|
||||
|
||||
const ANIMATION_ROOT_MARGIN = '600px 0px';
|
||||
|
||||
export interface GifViewportGate {
|
||||
loadMedia: boolean;
|
||||
animate: boolean;
|
||||
}
|
||||
|
||||
export function resolveGifViewportGate({
|
||||
isNearViewport,
|
||||
isInViewport,
|
||||
shouldBlur,
|
||||
}: {
|
||||
isNearViewport: boolean;
|
||||
isInViewport: boolean;
|
||||
shouldBlur: boolean;
|
||||
}): GifViewportGate {
|
||||
if (shouldBlur) return {loadMedia: false, animate: false};
|
||||
return {loadMedia: isNearViewport, animate: isNearViewport && isInViewport};
|
||||
}
|
||||
|
||||
export function useGifViewportGate<T extends Element>({
|
||||
element,
|
||||
rememberKey,
|
||||
shouldBlur,
|
||||
}: {
|
||||
element: Element | null;
|
||||
rememberKey: string;
|
||||
shouldBlur: boolean;
|
||||
}): GifViewportGate & {ref: (node: T | null) => void} {
|
||||
const {ref, isNearViewport} = useNearViewport<T>({rememberKey});
|
||||
const [isInViewport, setIsInViewport] = useState(() => typeof IntersectionObserver === 'undefined');
|
||||
useEffect(() => {
|
||||
if (typeof IntersectionObserver === 'undefined') {
|
||||
setIsInViewport(true);
|
||||
return;
|
||||
}
|
||||
if (element == null) {
|
||||
setIsInViewport(false);
|
||||
return;
|
||||
}
|
||||
return observeIntersection(
|
||||
element,
|
||||
(entry) => {
|
||||
setIsInViewport(entry.isIntersecting || entry.intersectionRatio > 0);
|
||||
},
|
||||
{rootMargin: ANIMATION_ROOT_MARGIN},
|
||||
);
|
||||
}, [element]);
|
||||
return {ref, ...resolveGifViewportGate({isNearViewport, isInViewport, shouldBlur})};
|
||||
}
|
||||
Reference in new issue
Block a user