[Refactoring] RLottie: Make size required

This commit is contained in:
Alexander Zinchuk 2023-04-23 18:33:21 +04:00
parent cfc71da0c1
commit 649fb46777
7 changed files with 27 additions and 49 deletions

View File

@ -28,7 +28,7 @@ export type OwnProps = {
playSegment?: [number, number]; playSegment?: [number, number];
speed?: number; speed?: number;
noLoop?: boolean; noLoop?: boolean;
size?: number; size: number;
quality?: number; quality?: number;
color?: [number, number, number]; color?: [number, number, number];
isLowPriority?: boolean; isLowPriority?: boolean;
@ -126,14 +126,14 @@ const AnimatedSticker: FC<OwnProps> = ({
tgsUrl, tgsUrl,
container, container,
renderId || generateIdFor(ID_STORE, true), renderId || generateIdFor(ID_STORE, true),
viewId,
{ {
noLoop,
size, size,
noLoop,
quality, quality,
isLowPriority, isLowPriority,
coords: sharedCanvasCoords, coords: sharedCanvasCoords,
}, },
viewId,
color, color,
onLoad, onLoad,
onEnded, onEnded,
@ -152,18 +152,12 @@ const AnimatedSticker: FC<OwnProps> = ({
]); ]);
useEffect(() => { useEffect(() => {
if (animation || !tgsUrl || (sharedCanvas && !sharedCanvasCoords)) {
return;
}
if (RLottie) { if (RLottie) {
init(); init();
} else { } else {
ensureLottie().then(() => { ensureLottie().then(init);
requestMeasure(init);
});
} }
}, [animation, init, sharedCanvas, sharedCanvasCoords, tgsUrl]); }, [init]);
const throttledInit = useThrottledCallback(init, [init], THROTTLE_MS); const throttledInit = useThrottledCallback(init, [init], THROTTLE_MS);
useSharedIntersectionObserver(sharedCanvas, throttledInit); useSharedIntersectionObserver(sharedCanvas, throttledInit);

View File

@ -10,7 +10,7 @@ import { EMOJI_SIZE_PICKER } from '../../config';
import buildClassName from '../../util/buildClassName'; import buildClassName from '../../util/buildClassName';
import { getDocumentMediaHash, isSameReaction } from '../../global/helpers'; import { getDocumentMediaHash, isSameReaction } from '../../global/helpers';
import useBoundsInSharedCanvas from '../../hooks/useBoundsInSharedCanvas'; import useCoordsInSharedCanvas from '../../hooks/useCoordsInSharedCanvas';
import useMediaTransition from '../../hooks/useMediaTransition'; import useMediaTransition from '../../hooks/useMediaTransition';
import useMedia from '../../hooks/useMedia'; import useMedia from '../../hooks/useMedia';
@ -49,7 +49,7 @@ const ReactionEmoji: FC<OwnProps> = ({
), [availableReactions, reaction]); ), [availableReactions, reaction]);
const thumbDataUri = availableReaction?.staticIcon?.thumbnail?.dataUri; const thumbDataUri = availableReaction?.staticIcon?.thumbnail?.dataUri;
const animationId = availableReaction?.selectAnimation?.id; const animationId = availableReaction?.selectAnimation?.id;
const bounds = useBoundsInSharedCanvas(ref, sharedCanvasHqRef); const coords = useCoordsInSharedCanvas(ref, sharedCanvasHqRef);
const mediaData = useMedia( const mediaData = useMedia(
availableReaction?.selectAnimation ? getDocumentMediaHash(availableReaction.selectAnimation) : undefined, availableReaction?.selectAnimation ? getDocumentMediaHash(availableReaction.selectAnimation) : undefined,
!animationId, !animationId,
@ -91,7 +91,7 @@ const ReactionEmoji: FC<OwnProps> = ({
size={EMOJI_SIZE_PICKER} size={EMOJI_SIZE_PICKER}
className={transitionClassNames} className={transitionClassNames}
sharedCanvas={sharedCanvasHqRef!.current || undefined} sharedCanvas={sharedCanvasHqRef!.current || undefined}
sharedCanvasCoords={bounds.coords} sharedCanvasCoords={coords}
/> />
)} )}
</div> </div>

View File

@ -17,7 +17,7 @@ import { useIsIntersecting } from '../../hooks/useIntersectionObserver';
import useThumbnail from '../../hooks/useThumbnail'; import useThumbnail from '../../hooks/useThumbnail';
import useMediaTransition from '../../hooks/useMediaTransition'; import useMediaTransition from '../../hooks/useMediaTransition';
import useFlag from '../../hooks/useFlag'; import useFlag from '../../hooks/useFlag';
import useBoundsInSharedCanvas from '../../hooks/useBoundsInSharedCanvas'; import useCoordsInSharedCanvas from '../../hooks/useCoordsInSharedCanvas';
import useHeavyAnimationCheck, { isHeavyAnimating } from '../../hooks/useHeavyAnimationCheck'; import useHeavyAnimationCheck, { isHeavyAnimating } from '../../hooks/useHeavyAnimationCheck';
import AnimatedSticker from './AnimatedSticker'; import AnimatedSticker from './AnimatedSticker';
@ -118,7 +118,7 @@ const StickerView: FC<OwnProps> = ({
const fullMediaClassNames = useMediaTransition(isFullMediaReady); const fullMediaClassNames = useMediaTransition(isFullMediaReady);
const noTransition = isLottie && preloadedPreviewData; const noTransition = isLottie && preloadedPreviewData;
const bounds = useBoundsInSharedCanvas(containerRef, sharedCanvasRef); const coords = useCoordsInSharedCanvas(containerRef, sharedCanvasRef);
// Preload preview for Message Input and local message // Preload preview for Message Input and local message
useMedia(previewMediaHash, !shouldLoad || !shouldPreloadPreview, undefined, cacheBuster); useMedia(previewMediaHash, !shouldLoad || !shouldPreloadPreview, undefined, cacheBuster);
@ -160,7 +160,7 @@ const StickerView: FC<OwnProps> = ({
forceOnHeavyAnimation={forceOnHeavyAnimation} forceOnHeavyAnimation={forceOnHeavyAnimation}
isLowPriority={isSmall && !selectIsAlwaysHighPriorityEmoji(getGlobal(), stickerSetInfo)} isLowPriority={isSmall && !selectIsAlwaysHighPriorityEmoji(getGlobal(), stickerSetInfo)}
sharedCanvas={sharedCanvasRef?.current || undefined} sharedCanvas={sharedCanvasRef?.current || undefined}
sharedCanvasCoords={bounds.coords} sharedCanvasCoords={coords}
onLoad={markPlayerReady} onLoad={markPlayerReady}
onLoop={onAnimatedStickerLoop} onLoop={onAnimatedStickerLoop}
onEnded={onAnimatedStickerLoop} onEnded={onAnimatedStickerLoop}

View File

@ -15,7 +15,7 @@ import { getStickerPreviewHash } from '../../../global/helpers';
import { useIsIntersecting } from '../../../hooks/useIntersectionObserver'; import { useIsIntersecting } from '../../../hooks/useIntersectionObserver';
import useMedia from '../../../hooks/useMedia'; import useMedia from '../../../hooks/useMedia';
import useMediaTransition from '../../../hooks/useMediaTransition'; import useMediaTransition from '../../../hooks/useMediaTransition';
import useBoundsInSharedCanvas from '../../../hooks/useBoundsInSharedCanvas'; import useCoordsInSharedCanvas from '../../../hooks/useCoordsInSharedCanvas';
import AnimatedSticker from '../../common/AnimatedSticker'; import AnimatedSticker from '../../common/AnimatedSticker';
import OptimizedVideo from '../../ui/OptimizedVideo'; import OptimizedVideo from '../../ui/OptimizedVideo';
@ -54,7 +54,7 @@ const StickerSetCover: FC<OwnProps> = ({
const isReady = mediaData || staticMediaData; const isReady = mediaData || staticMediaData;
const transitionClassNames = useMediaTransition(isReady); const transitionClassNames = useMediaTransition(isReady);
const bounds = useBoundsInSharedCanvas(containerRef, sharedCanvasRef); const coords = useCoordsInSharedCanvas(containerRef, sharedCanvasRef);
useEffect(() => { useEffect(() => {
if (isIntersecting && !stickerSet.stickers?.length) { if (isIntersecting && !stickerSet.stickers?.length) {
@ -74,11 +74,11 @@ const StickerSetCover: FC<OwnProps> = ({
<AnimatedSticker <AnimatedSticker
className={transitionClassNames} className={transitionClassNames}
tgsUrl={mediaData} tgsUrl={mediaData}
size={size || bounds.size} size={size}
play={isIntersecting && !noAnimate} play={isIntersecting && !noAnimate}
isLowPriority={!selectIsAlwaysHighPriorityEmoji(getGlobal(), stickerSet)} isLowPriority={!selectIsAlwaysHighPriorityEmoji(getGlobal(), stickerSet)}
sharedCanvas={sharedCanvasRef?.current || undefined} sharedCanvas={sharedCanvasRef?.current || undefined}
sharedCanvasCoords={bounds.coords} sharedCanvasCoords={coords}
/> />
) : (isVideo && !shouldFallbackToStatic) ? ( ) : (isVideo && !shouldFallbackToStatic) ? (
<OptimizedVideo <OptimizedVideo

View File

@ -200,12 +200,12 @@ function createPlayer({
mediaUrl, mediaUrl,
isHq ? sharedCanvasHqRef.current! : sharedCanvasRef.current!, isHq ? sharedCanvasHqRef.current! : sharedCanvasRef.current!,
renderId, renderId,
viewId,
{ {
size: SIZE, size: SIZE,
coords: position, coords: position,
isLowPriority: !isHq, isLowPriority: !isHq,
}, },
viewId,
customEmoji.shouldUseTextColor ? textColor : undefined, customEmoji.shouldUseTextColor ? textColor : undefined,
); );

View File

@ -10,13 +10,12 @@ import useSharedIntersectionObserver from './useSharedIntersectionObserver';
const THROTTLE_MS = 150; const THROTTLE_MS = 150;
export default function useBoundsInSharedCanvas( export default function useCoordsInSharedCanvas(
containerRef: React.RefObject<HTMLDivElement>, containerRef: React.RefObject<HTMLDivElement>,
sharedCanvasRef?: React.RefObject<HTMLCanvasElement>, sharedCanvasRef?: React.RefObject<HTMLCanvasElement>,
) { ) {
const [x, setX] = useState<number>(); const [x, setX] = useState<number>();
const [y, setY] = useState<number>(); const [y, setY] = useState<number>();
const [size, setSize] = useState<number>();
const recalculate = useCallback(() => { const recalculate = useCallback(() => {
const container = containerRef.current; const container = containerRef.current;
@ -43,7 +42,6 @@ export default function useBoundsInSharedCanvas(
// Factor coords are used to support rendering while being rescaled (e.g. message appearance animation) // Factor coords are used to support rendering while being rescaled (e.g. message appearance animation)
setX(round((targetBounds.left - canvasBounds.left) / canvasBounds.width, 4) || 0); setX(round((targetBounds.left - canvasBounds.left) / canvasBounds.width, 4) || 0);
setY(round((targetBounds.top - canvasBounds.top) / canvasBounds.height, 4) || 0); setY(round((targetBounds.top - canvasBounds.top) / canvasBounds.height, 4) || 0);
setSize(Math.round(targetBounds.width));
}, [containerRef, sharedCanvasRef]); }, [containerRef, sharedCanvasRef]);
useEffect(recalculate, [recalculate]); useEffect(recalculate, [recalculate]);
@ -52,7 +50,5 @@ export default function useBoundsInSharedCanvas(
useResizeObserver(sharedCanvasRef, throttledRecalculate); useResizeObserver(sharedCanvasRef, throttledRecalculate);
useSharedIntersectionObserver(sharedCanvasRef, throttledRecalculate); useSharedIntersectionObserver(sharedCanvasRef, throttledRecalculate);
const coords = useMemo(() => (x !== undefined && y !== undefined ? { x, y } : undefined), [x, y]); return useMemo(() => (x !== undefined && y !== undefined ? { x, y } : undefined), [x, y]);
return { coords, size };
} }

View File

@ -11,8 +11,8 @@ import cycleRestrict from '../../util/cycleRestrict';
import generateIdFor from '../../util/generateIdFor'; import generateIdFor from '../../util/generateIdFor';
interface Params { interface Params {
size: number;
noLoop?: boolean; noLoop?: boolean;
size?: number;
quality?: number; quality?: number;
isLowPriority?: boolean; isLowPriority?: boolean;
coords?: { x: number; y: number }; coords?: { x: number; y: number };
@ -96,8 +96,8 @@ class RLottie {
const [ const [
, canvas, , canvas,
renderId, renderId,
viewId = generateIdFor(ID_STORE, true), params,
params, , viewId = generateIdFor(ID_STORE, true), ,
onLoad, onLoad,
] = args; ] = args;
let instance = instancesByRenderId.get(renderId); let instance = instancesByRenderId.get(renderId);
@ -117,8 +117,8 @@ class RLottie {
private tgsUrl: string, private tgsUrl: string,
private container: HTMLDivElement | HTMLCanvasElement, private container: HTMLDivElement | HTMLCanvasElement,
private renderId: string, private renderId: string,
private params: Params,
viewId: string = generateIdFor(ID_STORE, true), viewId: string = generateIdFor(ID_STORE, true),
private params: Params = {},
private customColor?: [number, number, number], private customColor?: [number, number, number],
private onLoad?: NoneToVoidFunction | undefined, private onLoad?: NoneToVoidFunction | undefined,
private onEnded?: (isDestroyed?: boolean) => void, private onEnded?: (isDestroyed?: boolean) => void,
@ -264,19 +264,7 @@ class RLottie {
throw new Error('[RLottie] Container is not mounted'); throw new Error('[RLottie] Container is not mounted');
} }
let { size } = this.params; const { size } = this.params;
if (!size) {
size = (
container.offsetWidth
|| parseInt(container.style.width, 10)
|| container.parentNode.offsetWidth
);
if (!size) {
throw new Error('[RLottie] Failed to detect width from container');
}
}
imgSize = Math.round(size * sizeFactor); imgSize = Math.round(size * sizeFactor);
@ -309,7 +297,7 @@ class RLottie {
const canvas = container; const canvas = container;
const ctx = canvas.getContext('2d')!; const ctx = canvas.getContext('2d')!;
imgSize = Math.round(this.params.size! * sizeFactor); imgSize = Math.round(this.params.size * sizeFactor);
if (!this.imgSize) { if (!this.imgSize) {
this.imgSize = imgSize; this.imgSize = imgSize;
@ -323,8 +311,8 @@ class RLottie {
ctx, ctx,
isSharedCanvas: true, isSharedCanvas: true,
coords: { coords: {
x: Math.round((coords?.x || 0) * canvasWidth), x: Math.round(coords!.x * canvasWidth),
y: Math.round((coords?.y || 0) * canvasHeight), y: Math.round(coords!.y * canvasHeight),
}, },
onLoad, onLoad,
}); });
@ -337,8 +325,8 @@ class RLottie {
private calcSizeFactor() { private calcSizeFactor() {
const { const {
isLowPriority,
size, size,
isLowPriority,
// Reduced quality only looks acceptable on big enough images // Reduced quality only looks acceptable on big enough images
quality = isLowPriority && (!size || size > LOW_PRIORITY_QUALITY_SIZE_THRESHOLD) quality = isLowPriority && (!size || size > LOW_PRIORITY_QUALITY_SIZE_THRESHOLD)
? LOW_PRIORITY_QUALITY : HIGH_PRIORITY_QUALITY, ? LOW_PRIORITY_QUALITY : HIGH_PRIORITY_QUALITY,