[Perf] Animated Emoji: Dispatch heavy animation event, better interaction animation

This commit is contained in:
Alexander Zinchuk 2022-01-24 04:41:57 +01:00
parent 42745ea024
commit b6c80b5236
7 changed files with 40 additions and 55 deletions

View File

@ -1,4 +1,5 @@
.AnimatedEmoji {
cursor: pointer;
margin-bottom: 0.75rem;
img {

View File

@ -93,6 +93,7 @@ const AnimatedEmoji: FC<OwnProps> = ({
size={width}
quality={QUALITY}
play={isIntersecting && playKey}
forceOnHeavyAnimation
noLoop
onLoad={markAnimationLoaded}
/>

View File

@ -19,6 +19,7 @@ type OwnProps = {
quality?: number;
isLowPriority?: boolean;
onLoad?: NoneToVoidFunction;
forceOnHeavyAnimation?: boolean;
color?: [number, number, number];
onEnded?: NoneToVoidFunction;
};
@ -53,8 +54,9 @@ const AnimatedSticker: FC<OwnProps> = ({
size,
quality,
isLowPriority,
onLoad,
color,
forceOnHeavyAnimation,
onLoad,
onEnded,
}) => {
const [animation, setAnimation] = useState<RLottieInstance>();
@ -207,7 +209,7 @@ const AnimatedSticker: FC<OwnProps> = ({
}
}, [playAnimation, animation, animationData]);
useHeavyAnimationCheck(freezeAnimation, unfreezeAnimation);
useHeavyAnimationCheck(freezeAnimation, unfreezeAnimation, forceOnHeavyAnimation);
// Pausing frame may not happen in background
// so we need to make sure it happens right after focusing,
// then we can play again.

View File

@ -74,6 +74,7 @@ const LocalAnimatedEmoji: FC<OwnProps> = ({
size={width}
quality={QUALITY}
play={isIntersecting && playKey}
forceOnHeavyAnimation
noLoop
onLoad={markAnimationLoaded}
/>

View File

@ -2,7 +2,6 @@
--start-x: 0;
--start-y: 0;
--scale: 0;
--end-scale: 0;
position: absolute;
top: 0;
left: 0;
@ -11,42 +10,7 @@
pointer-events: none;
z-index: 1000;
@keyframes hide-reaction-reversed {
from {
transform: translate(100%, -50%) scaleX(-1) scale(1);
}
to {
left: var(--end-x, var(--start-x));
top: var(--end-y, var(--start-y));
transform: translate(50%, 0) scale(var(--end-scale, 0));
}
}
@keyframes show-reaction-reversed {
from {
transform: translate(50%, 0) scaleX(-1) scale(var(--scale, 0));
}
to {
transform: translate(100%, -50%) scaleX(-1) scale(1);
}
}
@keyframes hide-reaction {
from {
transform: translate(-50%, -50%) scale(1);
}
to {
left: var(--end-x, var(--start-x));
top: var(--end-y, var(--start-y));
transform: translate(0, 0) scale(var(--end-scale, 0));
}
}
@keyframes show-reaction {
@keyframes show-interaction {
from {
transform: translate(0, 0) scale(var(--scale, 0));
}
@ -56,13 +20,22 @@
}
}
@keyframes show-interaction-reversed {
from {
transform: translate(50%, 0) scaleX(-1) scale(var(--scale, 0));
}
to {
transform: translate(100%, -50%) scaleX(-1) scale(1);
}
}
.AnimatedSticker {
position: absolute;
top: var(--start-y);
left: var(--start-x);
transform: scale(var(--scale), 0);
transform-origin: left top;
transition: 0.25s cubic-bezier(.29,.81,.27,.99) opacity;
}
@ -71,19 +44,18 @@
}
&.playing .AnimatedSticker {
animation: show-reaction forwards 0.25s cubic-bezier(.29,.81,.27,.99);
animation: show-interaction forwards 0.25s cubic-bezier(.29,.81,.27,.99);
}
&.reversed.playing .AnimatedSticker {
animation: show-reaction-reversed forwards 0.25s cubic-bezier(.29,.81,.27,.99);
animation: show-interaction-reversed forwards 0.25s cubic-bezier(.29,.81,.27,.99);
}
&.hiding .AnimatedSticker {
opacity: 0;
animation: hide-reaction forwards 0.25s cubic-bezier(.29,.81,.27,.99);
}
&.reversed.hiding .AnimatedSticker {
animation: hide-reaction-reversed forwards 0.25s cubic-bezier(.29,.81,.27,.99);
opacity: 0;
}
}

View File

@ -1,19 +1,20 @@
import React, {
FC, memo, useCallback, useEffect, useState,
FC, memo, useCallback, useEffect, useLayoutEffect, useState,
} from '../../lib/teact/teact';
import { getDispatch, withGlobal } from '../../lib/teact/teactn';
import { ActiveEmojiInteraction } from '../../global/types';
import { ApiMediaFormat } from '../../api/types';
import { IS_ANDROID } from '../../util/environment';
import useFlag from '../../hooks/useFlag';
import useMedia from '../../hooks/useMedia';
import buildClassName from '../../util/buildClassName';
import {
selectAnimatedEmojiEffect,
} from '../../modules/selectors';
import { REM } from '../common/helpers/mediaDimensions';
import getAnimationData, { ANIMATED_STICKERS_PATHS } from '../common/helpers/animatedAssets';
import { dispatchHeavyAnimationEvent } from '../../hooks/useHeavyAnimationCheck';
import AnimatedSticker from '../common/AnimatedSticker';
@ -31,7 +32,6 @@ type StateProps = {
const HIDE_ANIMATION_DURATION = 250;
const PLAYING_DURATION = 3000;
const END_SIZE = 1.125 * REM;
const EFFECT_SIZE = 240;
const EmojiInteractionAnimation: FC<OwnProps & StateProps> = ({
@ -66,8 +66,13 @@ const EmojiInteractionAnimation: FC<OwnProps & StateProps> = ({
};
}, [stop]);
useEffect(() => {
setTimeout(stop, PLAYING_DURATION);
useLayoutEffect(() => {
const dispatchHeavyAnimationStop = dispatchHeavyAnimationEvent();
setTimeout(() => {
stop();
dispatchHeavyAnimationStop();
}, PLAYING_DURATION);
}, [stop]);
const mediaDataEffect = useMedia(`sticker${effectAnimationId}`, !effectAnimationId, ApiMediaFormat.Lottie);
@ -82,7 +87,6 @@ const EmojiInteractionAnimation: FC<OwnProps & StateProps> = ({
}, [localEffectAnimation]);
const scale = (emojiInteraction.startSize || 0) / EFFECT_SIZE;
const endScale = END_SIZE / EFFECT_SIZE;
return (
<div
@ -90,16 +94,15 @@ const EmojiInteractionAnimation: FC<OwnProps & StateProps> = ({
'EmojiInteractionAnimation', isHiding && 'hiding', isPlaying && 'playing', isReversed && 'reversed',
)}
// @ts-ignore teact feature
style={`--end-scale: ${endScale}; --scale: ${scale}; --start-x: ${emojiInteraction.x}px;`
+ `--start-y: ${emojiInteraction.y}px;${
emojiInteraction.endX
&& emojiInteraction.endY ? `--end-x: ${emojiInteraction.endX}px; --end-y: ${emojiInteraction.endY}px;` : ''}`}
style={`--scale: ${scale}; --start-x: ${emojiInteraction.x}px; --start-y: ${emojiInteraction.y}px;`}
>
<AnimatedSticker
id={`effect_${effectAnimationId}`}
size={EFFECT_SIZE}
animationData={(localEffectAnimationData || mediaDataEffect) as AnyLiteral}
play={isPlaying}
isLowPriority={IS_ANDROID}
forceOnHeavyAnimation
noLoop
onLoad={startPlaying}
/>

View File

@ -12,8 +12,13 @@ const AUTO_END_TIMEOUT = 1000;
export default (
handleAnimationStart: AnyToVoidFunction,
handleAnimationEnd: AnyToVoidFunction,
isDisabled = false,
) => {
useEffect(() => {
if (isDisabled) {
return undefined;
}
if (isAnimating) {
handleAnimationStart();
}
@ -25,7 +30,7 @@ export default (
document.removeEventListener(ANIMATION_END_EVENT, handleAnimationEnd);
document.removeEventListener(ANIMATION_START_EVENT, handleAnimationStart);
};
}, [handleAnimationEnd, handleAnimationStart]);
}, [isDisabled, handleAnimationEnd, handleAnimationStart]);
};
export function isHeavyAnimating() {