[Perf] Animated Emoji: Dispatch heavy animation event, better interaction animation
This commit is contained in:
parent
42745ea024
commit
b6c80b5236
@ -1,4 +1,5 @@
|
|||||||
.AnimatedEmoji {
|
.AnimatedEmoji {
|
||||||
|
cursor: pointer;
|
||||||
margin-bottom: 0.75rem;
|
margin-bottom: 0.75rem;
|
||||||
|
|
||||||
img {
|
img {
|
||||||
|
|||||||
@ -93,6 +93,7 @@ const AnimatedEmoji: FC<OwnProps> = ({
|
|||||||
size={width}
|
size={width}
|
||||||
quality={QUALITY}
|
quality={QUALITY}
|
||||||
play={isIntersecting && playKey}
|
play={isIntersecting && playKey}
|
||||||
|
forceOnHeavyAnimation
|
||||||
noLoop
|
noLoop
|
||||||
onLoad={markAnimationLoaded}
|
onLoad={markAnimationLoaded}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@ -19,6 +19,7 @@ type OwnProps = {
|
|||||||
quality?: number;
|
quality?: number;
|
||||||
isLowPriority?: boolean;
|
isLowPriority?: boolean;
|
||||||
onLoad?: NoneToVoidFunction;
|
onLoad?: NoneToVoidFunction;
|
||||||
|
forceOnHeavyAnimation?: boolean;
|
||||||
color?: [number, number, number];
|
color?: [number, number, number];
|
||||||
onEnded?: NoneToVoidFunction;
|
onEnded?: NoneToVoidFunction;
|
||||||
};
|
};
|
||||||
@ -53,8 +54,9 @@ const AnimatedSticker: FC<OwnProps> = ({
|
|||||||
size,
|
size,
|
||||||
quality,
|
quality,
|
||||||
isLowPriority,
|
isLowPriority,
|
||||||
onLoad,
|
|
||||||
color,
|
color,
|
||||||
|
forceOnHeavyAnimation,
|
||||||
|
onLoad,
|
||||||
onEnded,
|
onEnded,
|
||||||
}) => {
|
}) => {
|
||||||
const [animation, setAnimation] = useState<RLottieInstance>();
|
const [animation, setAnimation] = useState<RLottieInstance>();
|
||||||
@ -207,7 +209,7 @@ const AnimatedSticker: FC<OwnProps> = ({
|
|||||||
}
|
}
|
||||||
}, [playAnimation, animation, animationData]);
|
}, [playAnimation, animation, animationData]);
|
||||||
|
|
||||||
useHeavyAnimationCheck(freezeAnimation, unfreezeAnimation);
|
useHeavyAnimationCheck(freezeAnimation, unfreezeAnimation, forceOnHeavyAnimation);
|
||||||
// Pausing frame may not happen in background
|
// Pausing frame may not happen in background
|
||||||
// so we need to make sure it happens right after focusing,
|
// so we need to make sure it happens right after focusing,
|
||||||
// then we can play again.
|
// then we can play again.
|
||||||
|
|||||||
@ -74,6 +74,7 @@ const LocalAnimatedEmoji: FC<OwnProps> = ({
|
|||||||
size={width}
|
size={width}
|
||||||
quality={QUALITY}
|
quality={QUALITY}
|
||||||
play={isIntersecting && playKey}
|
play={isIntersecting && playKey}
|
||||||
|
forceOnHeavyAnimation
|
||||||
noLoop
|
noLoop
|
||||||
onLoad={markAnimationLoaded}
|
onLoad={markAnimationLoaded}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@ -2,7 +2,6 @@
|
|||||||
--start-x: 0;
|
--start-x: 0;
|
||||||
--start-y: 0;
|
--start-y: 0;
|
||||||
--scale: 0;
|
--scale: 0;
|
||||||
--end-scale: 0;
|
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0;
|
top: 0;
|
||||||
left: 0;
|
left: 0;
|
||||||
@ -11,42 +10,7 @@
|
|||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
z-index: 1000;
|
z-index: 1000;
|
||||||
|
|
||||||
@keyframes hide-reaction-reversed {
|
@keyframes show-interaction {
|
||||||
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 {
|
|
||||||
from {
|
from {
|
||||||
transform: translate(0, 0) scale(var(--scale, 0));
|
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 {
|
.AnimatedSticker {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: var(--start-y);
|
top: var(--start-y);
|
||||||
left: var(--start-x);
|
left: var(--start-x);
|
||||||
transform: scale(var(--scale), 0);
|
transform: scale(var(--scale), 0);
|
||||||
transform-origin: left top;
|
transform-origin: left top;
|
||||||
|
|
||||||
transition: 0.25s cubic-bezier(.29,.81,.27,.99) opacity;
|
transition: 0.25s cubic-bezier(.29,.81,.27,.99) opacity;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -71,19 +44,18 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
&.playing .AnimatedSticker {
|
&.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 {
|
&.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 {
|
&.hiding .AnimatedSticker {
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
animation: hide-reaction forwards 0.25s cubic-bezier(.29,.81,.27,.99);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
&.reversed.hiding .AnimatedSticker {
|
&.reversed.hiding .AnimatedSticker {
|
||||||
animation: hide-reaction-reversed forwards 0.25s cubic-bezier(.29,.81,.27,.99);
|
opacity: 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,19 +1,20 @@
|
|||||||
import React, {
|
import React, {
|
||||||
FC, memo, useCallback, useEffect, useState,
|
FC, memo, useCallback, useEffect, useLayoutEffect, useState,
|
||||||
} from '../../lib/teact/teact';
|
} from '../../lib/teact/teact';
|
||||||
import { getDispatch, withGlobal } from '../../lib/teact/teactn';
|
import { getDispatch, withGlobal } from '../../lib/teact/teactn';
|
||||||
|
|
||||||
import { ActiveEmojiInteraction } from '../../global/types';
|
import { ActiveEmojiInteraction } from '../../global/types';
|
||||||
import { ApiMediaFormat } from '../../api/types';
|
import { ApiMediaFormat } from '../../api/types';
|
||||||
|
|
||||||
|
import { IS_ANDROID } from '../../util/environment';
|
||||||
import useFlag from '../../hooks/useFlag';
|
import useFlag from '../../hooks/useFlag';
|
||||||
import useMedia from '../../hooks/useMedia';
|
import useMedia from '../../hooks/useMedia';
|
||||||
import buildClassName from '../../util/buildClassName';
|
import buildClassName from '../../util/buildClassName';
|
||||||
import {
|
import {
|
||||||
selectAnimatedEmojiEffect,
|
selectAnimatedEmojiEffect,
|
||||||
} from '../../modules/selectors';
|
} from '../../modules/selectors';
|
||||||
import { REM } from '../common/helpers/mediaDimensions';
|
|
||||||
import getAnimationData, { ANIMATED_STICKERS_PATHS } from '../common/helpers/animatedAssets';
|
import getAnimationData, { ANIMATED_STICKERS_PATHS } from '../common/helpers/animatedAssets';
|
||||||
|
import { dispatchHeavyAnimationEvent } from '../../hooks/useHeavyAnimationCheck';
|
||||||
|
|
||||||
import AnimatedSticker from '../common/AnimatedSticker';
|
import AnimatedSticker from '../common/AnimatedSticker';
|
||||||
|
|
||||||
@ -31,7 +32,6 @@ type StateProps = {
|
|||||||
|
|
||||||
const HIDE_ANIMATION_DURATION = 250;
|
const HIDE_ANIMATION_DURATION = 250;
|
||||||
const PLAYING_DURATION = 3000;
|
const PLAYING_DURATION = 3000;
|
||||||
const END_SIZE = 1.125 * REM;
|
|
||||||
const EFFECT_SIZE = 240;
|
const EFFECT_SIZE = 240;
|
||||||
|
|
||||||
const EmojiInteractionAnimation: FC<OwnProps & StateProps> = ({
|
const EmojiInteractionAnimation: FC<OwnProps & StateProps> = ({
|
||||||
@ -66,8 +66,13 @@ const EmojiInteractionAnimation: FC<OwnProps & StateProps> = ({
|
|||||||
};
|
};
|
||||||
}, [stop]);
|
}, [stop]);
|
||||||
|
|
||||||
useEffect(() => {
|
useLayoutEffect(() => {
|
||||||
setTimeout(stop, PLAYING_DURATION);
|
const dispatchHeavyAnimationStop = dispatchHeavyAnimationEvent();
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
stop();
|
||||||
|
dispatchHeavyAnimationStop();
|
||||||
|
}, PLAYING_DURATION);
|
||||||
}, [stop]);
|
}, [stop]);
|
||||||
|
|
||||||
const mediaDataEffect = useMedia(`sticker${effectAnimationId}`, !effectAnimationId, ApiMediaFormat.Lottie);
|
const mediaDataEffect = useMedia(`sticker${effectAnimationId}`, !effectAnimationId, ApiMediaFormat.Lottie);
|
||||||
@ -82,7 +87,6 @@ const EmojiInteractionAnimation: FC<OwnProps & StateProps> = ({
|
|||||||
}, [localEffectAnimation]);
|
}, [localEffectAnimation]);
|
||||||
|
|
||||||
const scale = (emojiInteraction.startSize || 0) / EFFECT_SIZE;
|
const scale = (emojiInteraction.startSize || 0) / EFFECT_SIZE;
|
||||||
const endScale = END_SIZE / EFFECT_SIZE;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
@ -90,16 +94,15 @@ const EmojiInteractionAnimation: FC<OwnProps & StateProps> = ({
|
|||||||
'EmojiInteractionAnimation', isHiding && 'hiding', isPlaying && 'playing', isReversed && 'reversed',
|
'EmojiInteractionAnimation', isHiding && 'hiding', isPlaying && 'playing', isReversed && 'reversed',
|
||||||
)}
|
)}
|
||||||
// @ts-ignore teact feature
|
// @ts-ignore teact feature
|
||||||
style={`--end-scale: ${endScale}; --scale: ${scale}; --start-x: ${emojiInteraction.x}px;`
|
style={`--scale: ${scale}; --start-x: ${emojiInteraction.x}px; --start-y: ${emojiInteraction.y}px;`}
|
||||||
+ `--start-y: ${emojiInteraction.y}px;${
|
|
||||||
emojiInteraction.endX
|
|
||||||
&& emojiInteraction.endY ? `--end-x: ${emojiInteraction.endX}px; --end-y: ${emojiInteraction.endY}px;` : ''}`}
|
|
||||||
>
|
>
|
||||||
<AnimatedSticker
|
<AnimatedSticker
|
||||||
id={`effect_${effectAnimationId}`}
|
id={`effect_${effectAnimationId}`}
|
||||||
size={EFFECT_SIZE}
|
size={EFFECT_SIZE}
|
||||||
animationData={(localEffectAnimationData || mediaDataEffect) as AnyLiteral}
|
animationData={(localEffectAnimationData || mediaDataEffect) as AnyLiteral}
|
||||||
play={isPlaying}
|
play={isPlaying}
|
||||||
|
isLowPriority={IS_ANDROID}
|
||||||
|
forceOnHeavyAnimation
|
||||||
noLoop
|
noLoop
|
||||||
onLoad={startPlaying}
|
onLoad={startPlaying}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@ -12,8 +12,13 @@ const AUTO_END_TIMEOUT = 1000;
|
|||||||
export default (
|
export default (
|
||||||
handleAnimationStart: AnyToVoidFunction,
|
handleAnimationStart: AnyToVoidFunction,
|
||||||
handleAnimationEnd: AnyToVoidFunction,
|
handleAnimationEnd: AnyToVoidFunction,
|
||||||
|
isDisabled = false,
|
||||||
) => {
|
) => {
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
if (isDisabled) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
if (isAnimating) {
|
if (isAnimating) {
|
||||||
handleAnimationStart();
|
handleAnimationStart();
|
||||||
}
|
}
|
||||||
@ -25,7 +30,7 @@ export default (
|
|||||||
document.removeEventListener(ANIMATION_END_EVENT, handleAnimationEnd);
|
document.removeEventListener(ANIMATION_END_EVENT, handleAnimationEnd);
|
||||||
document.removeEventListener(ANIMATION_START_EVENT, handleAnimationStart);
|
document.removeEventListener(ANIMATION_START_EVENT, handleAnimationStart);
|
||||||
};
|
};
|
||||||
}, [handleAnimationEnd, handleAnimationStart]);
|
}, [isDisabled, handleAnimationEnd, handleAnimationStart]);
|
||||||
};
|
};
|
||||||
|
|
||||||
export function isHeavyAnimating() {
|
export function isHeavyAnimating() {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user