From b6c80b5236c20e9871dc6d6efc11b13f7c09ff73 Mon Sep 17 00:00:00 2001 From: Alexander Zinchuk Date: Mon, 24 Jan 2022 04:41:57 +0100 Subject: [PATCH] [Perf] Animated Emoji: Dispatch heavy animation event, better interaction animation --- src/components/common/AnimatedEmoji.scss | 1 + src/components/common/AnimatedEmoji.tsx | 1 + src/components/common/AnimatedSticker.tsx | 6 +- src/components/common/LocalAnimatedEmoji.tsx | 1 + .../middle/EmojiInteractionAnimation.scss | 56 +++++-------------- .../middle/EmojiInteractionAnimation.tsx | 23 ++++---- src/hooks/useHeavyAnimationCheck.ts | 7 ++- 7 files changed, 40 insertions(+), 55 deletions(-) diff --git a/src/components/common/AnimatedEmoji.scss b/src/components/common/AnimatedEmoji.scss index fcfef9fc..21801d89 100644 --- a/src/components/common/AnimatedEmoji.scss +++ b/src/components/common/AnimatedEmoji.scss @@ -1,4 +1,5 @@ .AnimatedEmoji { + cursor: pointer; margin-bottom: 0.75rem; img { diff --git a/src/components/common/AnimatedEmoji.tsx b/src/components/common/AnimatedEmoji.tsx index 8c5647ca..fb1dbb3d 100644 --- a/src/components/common/AnimatedEmoji.tsx +++ b/src/components/common/AnimatedEmoji.tsx @@ -93,6 +93,7 @@ const AnimatedEmoji: FC = ({ size={width} quality={QUALITY} play={isIntersecting && playKey} + forceOnHeavyAnimation noLoop onLoad={markAnimationLoaded} /> diff --git a/src/components/common/AnimatedSticker.tsx b/src/components/common/AnimatedSticker.tsx index f042c299..2319b6e1 100644 --- a/src/components/common/AnimatedSticker.tsx +++ b/src/components/common/AnimatedSticker.tsx @@ -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 = ({ size, quality, isLowPriority, - onLoad, color, + forceOnHeavyAnimation, + onLoad, onEnded, }) => { const [animation, setAnimation] = useState(); @@ -207,7 +209,7 @@ const AnimatedSticker: FC = ({ } }, [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. diff --git a/src/components/common/LocalAnimatedEmoji.tsx b/src/components/common/LocalAnimatedEmoji.tsx index 3ae7747b..38926615 100644 --- a/src/components/common/LocalAnimatedEmoji.tsx +++ b/src/components/common/LocalAnimatedEmoji.tsx @@ -74,6 +74,7 @@ const LocalAnimatedEmoji: FC = ({ size={width} quality={QUALITY} play={isIntersecting && playKey} + forceOnHeavyAnimation noLoop onLoad={markAnimationLoaded} /> diff --git a/src/components/middle/EmojiInteractionAnimation.scss b/src/components/middle/EmojiInteractionAnimation.scss index 955801fe..8195789c 100644 --- a/src/components/middle/EmojiInteractionAnimation.scss +++ b/src/components/middle/EmojiInteractionAnimation.scss @@ -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; } } diff --git a/src/components/middle/EmojiInteractionAnimation.tsx b/src/components/middle/EmojiInteractionAnimation.tsx index eb0782e2..65c1639e 100644 --- a/src/components/middle/EmojiInteractionAnimation.tsx +++ b/src/components/middle/EmojiInteractionAnimation.tsx @@ -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 = ({ @@ -66,8 +66,13 @@ const EmojiInteractionAnimation: FC = ({ }; }, [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 = ({ }, [localEffectAnimation]); const scale = (emojiInteraction.startSize || 0) / EFFECT_SIZE; - const endScale = END_SIZE / EFFECT_SIZE; return (
= ({ '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;`} > diff --git a/src/hooks/useHeavyAnimationCheck.ts b/src/hooks/useHeavyAnimationCheck.ts index d98adb42..9da6e6fe 100644 --- a/src/hooks/useHeavyAnimationCheck.ts +++ b/src/hooks/useHeavyAnimationCheck.ts @@ -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() {