Emoji Background: Fix resize after heavy animation (#4656)
This commit is contained in:
parent
61339c3bf8
commit
1997008ba5
@ -44,7 +44,7 @@
|
|||||||
"react-hooks-static-deps/exhaustive-deps": [
|
"react-hooks-static-deps/exhaustive-deps": [
|
||||||
"error",
|
"error",
|
||||||
{
|
{
|
||||||
"additionalHooks": "(useSyncEffect|useAsync|useDebouncedCallback|useThrottledCallback|useEffectWithPrevDeps|useLayoutEffectWithPrevDeps|useDerivedState|useDerivedSignal|useThrottledResolver|useDebouncedResolver)$",
|
"additionalHooks": "(useSyncEffect|useAsync|useDebouncedCallback|useThrottledCallback|useEffectWithPrevDeps|useLayoutEffectWithPrevDeps|useDerivedState|useDerivedSignal|useThrottledResolver|useDebouncedResolver|useThrottleForHeavyAnimation)$",
|
||||||
"staticHooks": {
|
"staticHooks": {
|
||||||
"getActions": true,
|
"getActions": true,
|
||||||
"useFlag": [false, true, true],
|
"useFlag": [false, true, true],
|
||||||
|
|||||||
@ -9,9 +9,7 @@ import { preloadImage } from '../../../util/files';
|
|||||||
import { REM } from '../helpers/mediaDimensions';
|
import { REM } from '../helpers/mediaDimensions';
|
||||||
|
|
||||||
import useDynamicColorListener from '../../../hooks/stickers/useDynamicColorListener';
|
import useDynamicColorListener from '../../../hooks/stickers/useDynamicColorListener';
|
||||||
import useEffectWithPrevDeps from '../../../hooks/useEffectWithPrevDeps';
|
import { useThrottleForHeavyAnimation } from '../../../hooks/useHeavyAnimationCheck';
|
||||||
import useFlag from '../../../hooks/useFlag';
|
|
||||||
import useHeavyAnimationCheck, { isHeavyAnimating } from '../../../hooks/useHeavyAnimationCheck';
|
|
||||||
import useLang from '../../../hooks/useLang';
|
import useLang from '../../../hooks/useLang';
|
||||||
import useLastCallback from '../../../hooks/useLastCallback';
|
import useLastCallback from '../../../hooks/useLastCallback';
|
||||||
import useMedia from '../../../hooks/useMedia';
|
import useMedia from '../../../hooks/useMedia';
|
||||||
@ -79,21 +77,20 @@ const EmojiIconBackground = ({
|
|||||||
|
|
||||||
const lang = useLang();
|
const lang = useLang();
|
||||||
|
|
||||||
// Delay mounting until heavy animation ends
|
|
||||||
const [canUpdate, markCanUpdate, unmarkCanUpdate] = useFlag(!isHeavyAnimating());
|
|
||||||
useHeavyAnimationCheck(unmarkCanUpdate, markCanUpdate);
|
|
||||||
|
|
||||||
const { customEmoji } = useCustomEmoji(emojiDocumentId);
|
const { customEmoji } = useCustomEmoji(emojiDocumentId);
|
||||||
const previewMediaHash = customEmoji ? getStickerPreviewHash(customEmoji.id) : undefined;
|
const previewMediaHash = customEmoji ? getStickerPreviewHash(customEmoji.id) : undefined;
|
||||||
const previewUrl = useMedia(previewMediaHash);
|
const previewUrl = useMedia(previewMediaHash);
|
||||||
|
|
||||||
const customColor = useDynamicColorListener(containerRef);
|
const customColor = useDynamicColorListener(containerRef);
|
||||||
|
|
||||||
useEffect(() => {
|
const preloadAfterHeavyAnimation = useThrottleForHeavyAnimation(() => {
|
||||||
if (!previewUrl || !canUpdate) return;
|
if (!previewUrl) return;
|
||||||
|
|
||||||
preloadImage(previewUrl).then(setEmojiImage);
|
preloadImage(previewUrl).then(setEmojiImage);
|
||||||
}, [previewUrl, canUpdate]);
|
}, [previewUrl]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
preloadAfterHeavyAnimation();
|
||||||
|
}, [preloadAfterHeavyAnimation]);
|
||||||
|
|
||||||
const updateCanvas = useLastCallback(() => {
|
const updateCanvas = useLastCallback(() => {
|
||||||
const canvas = canvasRef.current;
|
const canvas = canvasRef.current;
|
||||||
@ -129,46 +126,43 @@ const EmojiIconBackground = ({
|
|||||||
context.restore();
|
context.restore();
|
||||||
});
|
});
|
||||||
|
|
||||||
useEffectWithPrevDeps(([prevEmojiImage, prevLangRtl, prevCustomColor]) => {
|
const updateCanvasAfterHeavyAnimation = useThrottleForHeavyAnimation(updateCanvas, [updateCanvas]);
|
||||||
// No need to trigger update if only `canUpdate` changed
|
|
||||||
if (emojiImage === prevEmojiImage && lang.isRtl === prevLangRtl && customColor === prevCustomColor) return;
|
|
||||||
updateCanvas();
|
|
||||||
}, [emojiImage, lang.isRtl, customColor, canUpdate]);
|
|
||||||
|
|
||||||
const updateCanvasSize = useLastCallback((parentWidth: number, parentHeight: number) => {
|
useEffect(() => {
|
||||||
const canvas = canvasRef.current;
|
updateCanvasAfterHeavyAnimation();
|
||||||
if (!canvas || isHeavyAnimating()) return;
|
}, [emojiImage, lang.isRtl, customColor, updateCanvasAfterHeavyAnimation]);
|
||||||
|
|
||||||
canvas.width = parentWidth * dpr;
|
const updateCanvasSize = useThrottleForHeavyAnimation((parentWidth: number, parentHeight: number) => {
|
||||||
canvas.height = parentHeight * dpr;
|
requestMutation(() => {
|
||||||
|
const canvas = canvasRef.current;
|
||||||
|
if (!canvas) return;
|
||||||
|
|
||||||
canvas.style.width = `${parentWidth}px`;
|
canvas.width = parentWidth * dpr;
|
||||||
canvas.style.height = `${parentHeight}px`;
|
canvas.height = parentHeight * dpr;
|
||||||
|
|
||||||
updateCanvas();
|
canvas.style.width = `${parentWidth}px`;
|
||||||
});
|
canvas.style.height = `${parentHeight}px`;
|
||||||
|
|
||||||
const handleResize = useLastCallback((entry: ResizeObserverEntry) => {
|
updateCanvas();
|
||||||
|
});
|
||||||
|
}, [dpr]);
|
||||||
|
|
||||||
|
const handleResize = useThrottleForHeavyAnimation((entry: ResizeObserverEntry) => {
|
||||||
const { width, height } = entry.contentRect;
|
const { width, height } = entry.contentRect;
|
||||||
|
|
||||||
requestMutation(() => {
|
updateCanvasSize(width, height);
|
||||||
updateCanvasSize(width, height);
|
}, [updateCanvasSize]);
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
useResizeObserver(containerRef, handleResize, !canUpdate);
|
useResizeObserver(containerRef, handleResize);
|
||||||
|
|
||||||
useEffectWithPrevDeps(([prevDpr]) => {
|
useEffect(() => {
|
||||||
if (dpr === prevDpr) return;
|
|
||||||
const container = containerRef.current;
|
const container = containerRef.current;
|
||||||
if (!container || !canUpdate) return;
|
if (!container) return;
|
||||||
|
|
||||||
const { width, height } = container.getBoundingClientRect();
|
const { width, height } = container.getBoundingClientRect();
|
||||||
|
|
||||||
requestMutation(() => {
|
updateCanvasSize(width, height);
|
||||||
updateCanvasSize(width, height);
|
}, [updateCanvasSize]);
|
||||||
});
|
|
||||||
}, [dpr, canUpdate]);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={buildClassName(styles.root, className)} ref={containerRef}>
|
<div className={buildClassName(styles.root, className)} ref={containerRef}>
|
||||||
|
|||||||
@ -1,4 +1,6 @@
|
|||||||
import { useEffect } from '../lib/teact/teact';
|
import {
|
||||||
|
useCallback, useEffect, useMemo, useRef,
|
||||||
|
} from '../lib/teact/teact';
|
||||||
|
|
||||||
import { createCallbackManager } from '../util/callbacks';
|
import { createCallbackManager } from '../util/callbacks';
|
||||||
import useLastCallback from './useLastCallback';
|
import useLastCallback from './useLastCallback';
|
||||||
@ -39,6 +41,32 @@ const useHeavyAnimationCheck = (
|
|||||||
}, [isDisabled, lastOnEnd, lastOnStart]);
|
}, [isDisabled, lastOnEnd, lastOnStart]);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export function useThrottleForHeavyAnimation<T extends AnyToVoidFunction>(afterHeavyAnimation: T, deps: unknown[]) {
|
||||||
|
// eslint-disable-next-line react-hooks-static-deps/exhaustive-deps
|
||||||
|
const fnMemo = useCallback(afterHeavyAnimation, deps);
|
||||||
|
|
||||||
|
const isScheduledRef = useRef(false);
|
||||||
|
|
||||||
|
return useMemo(() => {
|
||||||
|
return (...args: Parameters<T>) => {
|
||||||
|
if (!isScheduledRef.current) {
|
||||||
|
if (!isAnimating) {
|
||||||
|
fnMemo(...args);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
isScheduledRef.current = true;
|
||||||
|
|
||||||
|
const removeCallback = endCallbacks.addCallback(() => {
|
||||||
|
fnMemo(...args);
|
||||||
|
removeCallback();
|
||||||
|
isScheduledRef.current = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}, [fnMemo]);
|
||||||
|
}
|
||||||
|
|
||||||
export function isHeavyAnimating() {
|
export function isHeavyAnimating() {
|
||||||
return isAnimating;
|
return isAnimating;
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user