More improvements for shared canvas emoji

This commit is contained in:
Alexander Zinchuk 2022-11-13 17:06:25 +04:00
parent a2a0161cc9
commit 7cc64ed6bb
16 changed files with 42 additions and 53 deletions

View File

@ -7,7 +7,6 @@ import type { FC, TeactNode } from '../../lib/teact/teact';
import type { ObserveFn } from '../../hooks/useIntersectionObserver';
import { ApiMessageEntityTypes } from '../../api/types';
import { REM } from './helpers/mediaDimensions';
import { getPropertyHexColor } from '../../util/themeStyle';
import { hexToRgb } from '../../util/switchTheme';
import buildClassName from '../../util/buildClassName';
@ -79,8 +78,6 @@ const CustomEmoji: FC<OwnProps> = ({
const [customColor, setCustomColor] = useState<[number, number, number] | undefined>();
const hasCustomColor = customEmoji && selectIsDefaultEmojiStatusPack(getGlobal(), customEmoji.stickerSetInfo);
const [realSize, setRealSize] = useState<number>(size);
useEffect(() => {
if (!hasCustomColor) {
setCustomColor(undefined);
@ -95,13 +92,6 @@ const CustomEmoji: FC<OwnProps> = ({
setCustomColor([customColorRgb.r, customColorRgb.g, customColorRgb.b]);
}, [hasCustomColor]);
useEffect(() => {
const computedSize = getComputedStyle(containerRef.current!).getPropertyValue('--custom-emoji-size');
if (computedSize) {
setRealSize(Math.round(Number(computedSize.replace(/[^\d.]/g, '')) * REM));
}
}, []);
const handleVideoEnded = useCallback((e) => {
if (!loopLimit) return;
@ -154,7 +144,7 @@ const CustomEmoji: FC<OwnProps> = ({
containerRef={containerRef}
sticker={customEmoji}
isSmall
size={realSize}
size={size}
customColor={customColor}
thumbClassName={styles.thumb}
fullMediaClassName={styles.media}

View File

@ -36,7 +36,7 @@
bottom: 0.625rem;
}
img:not(.emoji) {
.pictogram {
margin-inline-start: 0.5rem;
}
@ -120,7 +120,7 @@
opacity: 0.75;
}
img:not(.emoji) {
.pictogram {
width: 2rem;
height: 2rem;
object-fit: cover;
@ -144,7 +144,7 @@
bottom: 0.3125rem;
}
img:not(.emoji) {
.pictogram {
margin-left: 0.125rem;
}

View File

@ -120,7 +120,7 @@ function renderPictogram(
width={width}
height={height}
alt=""
className={isRoundVideo ? 'round' : ''}
className={buildClassName('pictogram', isRoundVideo && 'round')}
draggable={!isProtected}
/>
{isProtected && <span className="protector" />}

View File

@ -2,7 +2,6 @@
display: flex;
align-items: center;
gap: 0.25rem;
--custom-emoji-size: 1.25rem;
> h3 {
margin-bottom: 0;

View File

@ -17,7 +17,7 @@ import { useIsIntersecting } from '../../hooks/useIntersectionObserver';
import useThumbnail from '../../hooks/useThumbnail';
import useMediaTransition from '../../hooks/useMediaTransition';
import useFlag from '../../hooks/useFlag';
import useSharedCanvasCoords from '../../hooks/useSharedCanvasCoords';
import useBoundsInSharedCanvas from '../../hooks/useBoundsInSharedCanvas';
import AnimatedSticker from './AnimatedSticker';
import OptimizedVideo from '../ui/OptimizedVideo';
@ -114,14 +114,15 @@ const StickerView: FC<OwnProps> = ({
const fullMediaClassNames = useMediaTransition(isFullMediaReady);
const noTransition = isLottie && preloadedPreviewData;
const sharedCanvasCoords = useSharedCanvasCoords(containerRef, sharedCanvasRef);
const bounds = useBoundsInSharedCanvas(containerRef, sharedCanvasRef);
const realSize = bounds.size || size;
// Preload preview for Message Input and local message
useMedia(previewMediaHash, !shouldLoad || !shouldPreloadPreview, undefined, cacheBuster);
const randomIdPrefix = useMemo(() => generateIdFor(ID_STORE, true), []);
const idKey = [
(withSharedAnimation ? SHARED_PREFIX : randomIdPrefix), id, size, customColor?.join(','),
(withSharedAnimation ? SHARED_PREFIX : randomIdPrefix), id, realSize, customColor?.join(','),
].filter(Boolean).join('_');
return (
@ -141,7 +142,7 @@ const StickerView: FC<OwnProps> = ({
<AnimatedSticker
key={idKey}
animationId={idKey}
size={size}
size={realSize}
className={buildClassName(
styles.media,
(noTransition || isThumbOpaque) && styles.noTransition,
@ -155,7 +156,7 @@ const StickerView: FC<OwnProps> = ({
forceOnHeavyAnimation={forceOnHeavyAnimation}
isLowPriority={isSmall && !selectIsAlwaysHighPriorityEmoji(getGlobal(), stickerSetInfo)}
sharedCanvas={sharedCanvasRef?.current || undefined}
sharedCanvasCoords={sharedCanvasCoords}
sharedCanvasCoords={bounds.coords}
onLoad={markPlayerReady}
onLoop={onAnimatedStickerLoop}
onEnded={onAnimatedStickerLoop}

View File

@ -175,10 +175,6 @@
vertical-align: -0.125rem;
}
.custom-emoji {
--custom-emoji-size: 1.25rem;
}
.icon-play {
position: relative;
display: inline-block;

View File

@ -263,7 +263,7 @@ const Chat: FC<OwnProps & StateProps> = ({
const isChat = chat && (isChatChannel(chat) || lastMessage.senderId === lastMessage.chatId);
return (
<p className="last-message" dir={lang.isRtl ? 'auto' : 'ltr'}>
<p className="last-message shared-canvas-container" dir={lang.isRtl ? 'auto' : 'ltr'}>
{renderActionMessageText(
lang,
lastMessage,
@ -281,7 +281,7 @@ const Chat: FC<OwnProps & StateProps> = ({
const senderName = getMessageSenderName(lang, chatId, lastMessageSender);
return (
<p className="last-message" dir={lang.isRtl ? 'auto' : 'ltr'}>
<p className="last-message shared-canvas-container" dir={lang.isRtl ? 'auto' : 'ltr'}>
{senderName && (
<>
<span className="sender-name">{renderText(senderName)}</span>

View File

@ -73,10 +73,6 @@
vertical-align: -2px;
}
.custom-emoji {
--custom-emoji-size: 1.25rem;
}
&.multiline {
&::before {
content: "";

View File

@ -519,10 +519,6 @@
body.is-ios & {
font-size: 0.9375rem;
}
.custom-emoji {
--custom-emoji-size: 1.25rem;
}
}
}

View File

@ -572,7 +572,6 @@
}
.custom-emoji {
--custom-emoji-size: 1.25rem;
vertical-align: text-top;
}
}

View File

@ -13,7 +13,7 @@ import buildClassName from '../../../util/buildClassName';
import { useIsIntersecting } from '../../../hooks/useIntersectionObserver';
import useMedia from '../../../hooks/useMedia';
import useMediaTransition from '../../../hooks/useMediaTransition';
import useSharedCanvasCoords from '../../../hooks/useSharedCanvasCoords';
import useBoundsInSharedCanvas from '../../../hooks/useBoundsInSharedCanvas';
import AnimatedSticker from '../../common/AnimatedSticker';
import OptimizedVideo from '../../ui/OptimizedVideo';
@ -46,7 +46,7 @@ const StickerSetCover: FC<OwnProps> = ({
const isReady = mediaData && (!isVideo || IS_WEBM_SUPPORTED);
const transitionClassNames = useMediaTransition(isReady);
const sharedCanvasCoords = useSharedCanvasCoords(containerRef, sharedCanvasRef);
const bounds = useBoundsInSharedCanvas(containerRef, sharedCanvasRef);
return (
<div ref={containerRef} className="sticker-set-cover">
@ -55,11 +55,11 @@ const StickerSetCover: FC<OwnProps> = ({
<AnimatedSticker
className={transitionClassNames}
tgsUrl={mediaData}
size={size}
size={size || bounds.size}
play={isIntersecting && !noAnimate}
isLowPriority={!selectIsAlwaysHighPriorityEmoji(getGlobal(), stickerSet)}
sharedCanvas={sharedCanvasRef?.current || undefined}
sharedCanvasCoords={sharedCanvasCoords}
sharedCanvasCoords={bounds.coords}
/>
) : isVideo ? (
<OptimizedVideo

View File

@ -95,6 +95,7 @@ import useOuterHandlers from './hooks/useOuterHandlers';
import useInnerHandlers from './hooks/useInnerHandlers';
import { getServerTime } from '../../../util/serverTime';
import { isElementInViewport } from '../../../util/isElementInViewport';
import { getCustomEmojiSize } from '../composer/helpers/customEmoji';
import Button from '../../ui/Button';
import Avatar from '../../common/Avatar';
@ -126,10 +127,9 @@ import DotAnimation from '../../common/DotAnimation';
import CustomEmoji from '../../common/CustomEmoji';
import PremiumIcon from '../../common/PremiumIcon';
import FakeIcon from '../../common/FakeIcon';
import MessageText from '../../common/MessageText';
import './Message.scss';
import MessageText from '../../common/MessageText';
import { getCustomEmojiSize } from '../composer/helpers/customEmoji';
type MessagePositionProperties = {
isFirstInGroup: boolean;

View File

@ -272,7 +272,6 @@
}
.custom-emoji {
--custom-emoji-size: 1.25rem;
margin-left: 0.25rem;
&.custom-color {

View File

@ -1,15 +1,17 @@
import {
useCallback, useEffect, useMemo, useState,
useCallback, useEffect, useLayoutEffect, useMemo, useState,
} from '../lib/teact/teact';
import { throttle } from '../util/schedulers';
import { round } from '../util/math';
export default function useSharedCanvasCoords(
export default function useBoundsInSharedCanvas(
containerRef: React.RefObject<HTMLDivElement>,
sharedCanvasRef?: React.RefObject<HTMLCanvasElement>,
) {
const [x, setX] = useState<number>();
const [y, setY] = useState<number>();
const [size, setSize] = useState<number>();
const recalculate = useCallback(() => {
const container = containerRef.current;
@ -25,8 +27,11 @@ export default function useSharedCanvasCoords(
// Factor coords are used to support rendering while being rescaled (e.g. message appearance animation)
setX(round((targetBounds.left - canvasBounds.left) / canvasBounds.width, 4));
setY(round((targetBounds.top - canvasBounds.top) / canvasBounds.height, 4));
setSize(Math.round(targetBounds.width));
}, [containerRef, sharedCanvasRef]);
useLayoutEffect(recalculate, [recalculate]);
useEffect(() => {
if (!('ResizeObserver' in window) || !sharedCanvasRef?.current) {
return undefined;
@ -48,5 +53,7 @@ export default function useSharedCanvasCoords(
};
}, [recalculate, sharedCanvasRef]);
return useMemo(() => (x !== undefined && y !== undefined ? { x, y } : undefined), [x, y]);
const coords = useMemo(() => (x !== undefined && y !== undefined ? { x, y } : undefined), [x, y]);
return { coords, size };
}

View File

@ -28,6 +28,7 @@ const rLottieApiPromise = new Promise<void>((resolve) => {
const HIGH_PRIORITY_MAX_FPS = 60;
const LOW_PRIORITY_MAX_FPS = 30;
const DESTROY_REPEAT_DELAY = 1000;
const renderers = new Map<string, {
imgSize: number;
@ -150,12 +151,17 @@ function applyColor(arr: Uint8ClampedArray, color: [number, number, number]) {
}
}
function destroy(key: string) {
const renderer = renderers.get(key)!;
rLottieApi.destroy(renderer.handle);
renderers.delete(key);
function destroy(key: string, isRepeated = false) {
try {
const renderer = renderers.get(key)!;
rLottieApi.destroy(renderer.handle);
renderers.delete(key);
} catch (err) {
// `destroy` sometimes can be called before the initialization is finished
if (!isRepeated) {
setTimeout(() => destroy(key, true), DESTROY_REPEAT_DELAY);
}
}
}
createWorkerInterface({

View File

@ -184,7 +184,7 @@ $color-message-reaction-own-hover: #b5e0a4;
--messages-container-width: 45.5rem;
--right-column-width: 26.5rem;
--header-height: 3.5rem;
--custom-emoji-size: 1.5rem;
--custom-emoji-size: 1.25rem;
--symbol-menu-width: 26.25rem;
--symbol-menu-height: 23.25rem;