Properly handle shared canvas resize
This commit is contained in:
parent
ee73e1ba73
commit
dfe7b80a6b
@ -209,11 +209,17 @@ const AnimatedSticker: FC<OwnProps> = ({
|
||||
}, [unfreezeAnimation]);
|
||||
|
||||
useOnChange(([prevNoLoop]) => {
|
||||
if (noLoop !== undefined && noLoop !== prevNoLoop) {
|
||||
if (prevNoLoop !== undefined && noLoop !== prevNoLoop) {
|
||||
animation?.setNoLoop(noLoop);
|
||||
}
|
||||
}, [noLoop, animation]);
|
||||
|
||||
useOnChange(([prevSharedCanvasCoords]) => {
|
||||
if (prevSharedCanvasCoords !== undefined && sharedCanvasCoords !== prevSharedCanvasCoords) {
|
||||
animation?.setSharedCanvasCoords(containerId, sharedCanvasCoords);
|
||||
}
|
||||
}, [sharedCanvasCoords, containerId, animation]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!animation) {
|
||||
return;
|
||||
|
||||
@ -20,6 +20,7 @@
|
||||
.thumb {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.media {
|
||||
@ -42,7 +43,6 @@
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
z-index: 1;
|
||||
user-select: auto !important;
|
||||
}
|
||||
|
||||
|
||||
@ -1,4 +1,8 @@
|
||||
import { useEffect, useMemo, useState } from '../lib/teact/teact';
|
||||
import {
|
||||
useCallback, useEffect, useMemo, useState,
|
||||
} from '../lib/teact/teact';
|
||||
import { throttle } from '../util/schedulers';
|
||||
import { round } from '../util/math';
|
||||
|
||||
export default function useSharedCanvasCoords(
|
||||
containerRef: React.RefObject<HTMLDivElement>,
|
||||
@ -7,20 +11,42 @@ export default function useSharedCanvasCoords(
|
||||
const [x, setX] = useState<number>();
|
||||
const [y, setY] = useState<number>();
|
||||
|
||||
useEffect(() => {
|
||||
if (!sharedCanvasRef?.current) {
|
||||
const recalculate = useCallback(() => {
|
||||
const container = containerRef.current;
|
||||
const canvas = sharedCanvasRef?.current;
|
||||
if (!container || !canvas) {
|
||||
return;
|
||||
}
|
||||
|
||||
const container = containerRef.current!;
|
||||
const target = container.classList.contains('sticker-set-cover') ? container : container.querySelector('img')!;
|
||||
const targetBounds = target.getBoundingClientRect();
|
||||
const canvasBounds = sharedCanvasRef!.current!.getBoundingClientRect();
|
||||
const canvasBounds = canvas.getBoundingClientRect();
|
||||
|
||||
// Factor coords are used to support rendering while being rescaled (e.g. message appearance animation)
|
||||
setX((targetBounds.left - canvasBounds.left) / canvasBounds.width);
|
||||
setY((targetBounds.top - canvasBounds.top) / canvasBounds.height);
|
||||
setX(round((targetBounds.left - canvasBounds.left) / canvasBounds.width, 4));
|
||||
setY(round((targetBounds.top - canvasBounds.top) / canvasBounds.height, 4));
|
||||
}, [containerRef, sharedCanvasRef]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!('ResizeObserver' in window) || !sharedCanvasRef?.current) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const observer = new ResizeObserver(throttle(([entry]) => {
|
||||
// During animation
|
||||
if (!(entry.target as HTMLCanvasElement).offsetParent) {
|
||||
return;
|
||||
}
|
||||
|
||||
recalculate();
|
||||
}, 300, false));
|
||||
|
||||
observer.observe(sharedCanvasRef.current);
|
||||
|
||||
return () => {
|
||||
observer.disconnect();
|
||||
};
|
||||
}, [recalculate, sharedCanvasRef]);
|
||||
|
||||
return useMemo(() => (x !== undefined && y !== undefined ? { x, y } : undefined), [x, y]);
|
||||
}
|
||||
|
||||
@ -7,6 +7,7 @@ import {
|
||||
import WorkerConnector from '../../util/WorkerConnector';
|
||||
import { animate } from '../../util/animation';
|
||||
import cycleRestrict from '../../util/cycleRestrict';
|
||||
import { fastRaf } from '../../util/schedulers';
|
||||
|
||||
interface Params {
|
||||
noLoop?: boolean;
|
||||
@ -120,9 +121,14 @@ class RLottie {
|
||||
}
|
||||
|
||||
public removeContainer(containerId: string) {
|
||||
const containerData = this.containers.get(containerId)!;
|
||||
if (!containerData.isSharedCanvas) {
|
||||
this.containers.get(containerId)!.canvas.remove();
|
||||
const {
|
||||
canvas, ctx, isSharedCanvas, coords,
|
||||
} = this.containers.get(containerId)!;
|
||||
|
||||
if (isSharedCanvas) {
|
||||
ctx.clearRect(coords!.x, coords!.y, this.imgSize, this.imgSize);
|
||||
} else {
|
||||
canvas.remove();
|
||||
}
|
||||
|
||||
this.containers.delete(containerId);
|
||||
@ -167,9 +173,8 @@ class RLottie {
|
||||
}
|
||||
|
||||
if (!this.params.isLowPriority) {
|
||||
const currentFrameIndex = Math.floor(this.approxFrameIndex);
|
||||
this.frames = this.frames.map((frame, i) => {
|
||||
if (i === currentFrameIndex) {
|
||||
if (i === this.prevFrameIndex) {
|
||||
return frame;
|
||||
} else {
|
||||
if (frame && frame !== WAITING) {
|
||||
@ -193,10 +198,41 @@ class RLottie {
|
||||
this.speed = speed;
|
||||
}
|
||||
|
||||
setNoLoop(noLoop: boolean) {
|
||||
setNoLoop(noLoop?: boolean) {
|
||||
this.params.noLoop = noLoop;
|
||||
}
|
||||
|
||||
setSharedCanvasCoords(containerId: string, newCoords: Params['coords']) {
|
||||
const containerInfo = this.containers.get(containerId)!;
|
||||
const {
|
||||
canvas, ctx, isPaused, coords,
|
||||
} = containerInfo;
|
||||
|
||||
if (!canvas.dataset.isJustCleaned || canvas.dataset.isJustCleaned === 'false') {
|
||||
const { isLowPriority, quality = isLowPriority ? LOW_PRIORITY_QUALITY : HIGH_PRIORITY_QUALITY } = this.params;
|
||||
const sizeFactor = Math.max(DPR * quality, 1);
|
||||
ensureCanvasSize(canvas, sizeFactor);
|
||||
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
||||
canvas.dataset.isJustCleaned = 'true';
|
||||
fastRaf(() => {
|
||||
canvas.dataset.isJustCleaned = 'false';
|
||||
});
|
||||
}
|
||||
|
||||
containerInfo.coords = {
|
||||
x: Math.round((newCoords?.x || 0) * canvas.width),
|
||||
y: Math.round((newCoords?.y || 0) * canvas.height),
|
||||
};
|
||||
|
||||
if (isPaused || !this.isPlaying()) {
|
||||
const frame = this.getFrame(this.prevFrameIndex) || this.getFrame(Math.round(this.approxFrameIndex));
|
||||
|
||||
if (frame && frame !== WAITING) {
|
||||
ctx.drawImage(frame, coords!.x, coords!.y);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private addContainer(
|
||||
containerId: string,
|
||||
container: HTMLDivElement | HTMLCanvasElement,
|
||||
@ -251,14 +287,9 @@ class RLottie {
|
||||
const canvas = container;
|
||||
const ctx = canvas.getContext('2d')!;
|
||||
|
||||
imgSize = Math.round(this.params.size! * sizeFactor);
|
||||
ensureCanvasSize(canvas, sizeFactor);
|
||||
|
||||
const expectedWidth = Math.round(canvas.offsetWidth * sizeFactor);
|
||||
const expectedHeight = Math.round(canvas.offsetHeight * sizeFactor);
|
||||
if (canvas.width !== expectedWidth || canvas.height !== expectedHeight) {
|
||||
canvas.width = expectedWidth;
|
||||
canvas.height = expectedHeight;
|
||||
}
|
||||
imgSize = Math.round(this.params.size! * sizeFactor);
|
||||
|
||||
this.containers.set(containerId, {
|
||||
canvas,
|
||||
@ -537,4 +568,13 @@ class RLottie {
|
||||
}
|
||||
}
|
||||
|
||||
function ensureCanvasSize(canvas: HTMLCanvasElement, sizeFactor: number) {
|
||||
const expectedWidth = Math.round(canvas.offsetWidth * sizeFactor);
|
||||
const expectedHeight = Math.round(canvas.offsetHeight * sizeFactor);
|
||||
if (canvas.width !== expectedWidth || canvas.height !== expectedHeight) {
|
||||
canvas.width = expectedWidth;
|
||||
canvas.height = expectedHeight;
|
||||
}
|
||||
}
|
||||
|
||||
export default RLottie;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user