Animated Sticker: Use single instance for same stickers
This commit is contained in:
parent
1fda24ab92
commit
4c97d37439
@ -12,9 +12,11 @@ import buildStyle from '../../util/buildStyle';
|
|||||||
import useHeavyAnimationCheck from '../../hooks/useHeavyAnimationCheck';
|
import useHeavyAnimationCheck from '../../hooks/useHeavyAnimationCheck';
|
||||||
import useBackgroundMode from '../../hooks/useBackgroundMode';
|
import useBackgroundMode from '../../hooks/useBackgroundMode';
|
||||||
import useOnChange from '../../hooks/useOnChange';
|
import useOnChange from '../../hooks/useOnChange';
|
||||||
|
import generateIdFor from '../../util/generateIdFor';
|
||||||
|
|
||||||
export type OwnProps = {
|
export type OwnProps = {
|
||||||
ref?: RefObject<HTMLDivElement>;
|
ref?: RefObject<HTMLDivElement>;
|
||||||
|
id?: string;
|
||||||
className?: string;
|
className?: string;
|
||||||
style?: string;
|
style?: string;
|
||||||
tgsUrl?: string;
|
tgsUrl?: string;
|
||||||
@ -41,6 +43,8 @@ let RLottie: RLottieClass;
|
|||||||
// Time for the main interface to completely load
|
// Time for the main interface to completely load
|
||||||
const LOTTIE_LOAD_DELAY = 3000;
|
const LOTTIE_LOAD_DELAY = 3000;
|
||||||
|
|
||||||
|
const ID_STORE = {};
|
||||||
|
|
||||||
async function ensureLottie() {
|
async function ensureLottie() {
|
||||||
if (!lottiePromise) {
|
if (!lottiePromise) {
|
||||||
lottiePromise = import('../../lib/rlottie/RLottie') as unknown as Promise<RLottieClass>;
|
lottiePromise = import('../../lib/rlottie/RLottie') as unknown as Promise<RLottieClass>;
|
||||||
@ -54,6 +58,7 @@ setTimeout(ensureLottie, LOTTIE_LOAD_DELAY);
|
|||||||
|
|
||||||
const AnimatedSticker: FC<OwnProps> = ({
|
const AnimatedSticker: FC<OwnProps> = ({
|
||||||
ref,
|
ref,
|
||||||
|
id,
|
||||||
className,
|
className,
|
||||||
style,
|
style,
|
||||||
tgsUrl,
|
tgsUrl,
|
||||||
@ -97,8 +102,12 @@ const AnimatedSticker: FC<OwnProps> = ({
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const newAnimation = new RLottie(
|
const fullId = `${id || generateIdFor(ID_STORE, true)}_${size}${color ? `_${color.join(',')}` : ''}`;
|
||||||
|
|
||||||
|
const newAnimation = RLottie.init(
|
||||||
containerRef.current,
|
containerRef.current,
|
||||||
|
onLoad,
|
||||||
|
fullId,
|
||||||
tgsUrl,
|
tgsUrl,
|
||||||
{
|
{
|
||||||
noLoop,
|
noLoop,
|
||||||
@ -107,7 +116,6 @@ const AnimatedSticker: FC<OwnProps> = ({
|
|||||||
isLowPriority,
|
isLowPriority,
|
||||||
},
|
},
|
||||||
color,
|
color,
|
||||||
onLoad,
|
|
||||||
onEnded,
|
onEnded,
|
||||||
onLoop,
|
onLoop,
|
||||||
);
|
);
|
||||||
@ -130,7 +138,7 @@ const AnimatedSticker: FC<OwnProps> = ({
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}, [color, animation, tgsUrl, isLowPriority, noLoop, onLoad, quality, size, speed, onEnded, onLoop]);
|
}, [color, animation, tgsUrl, isLowPriority, noLoop, onLoad, quality, size, speed, onEnded, onLoop, id]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!animation) return;
|
if (!animation) return;
|
||||||
@ -139,9 +147,11 @@ const AnimatedSticker: FC<OwnProps> = ({
|
|||||||
}, [color, animation]);
|
}, [color, animation]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
const container = containerRef.current!;
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
if (animation) {
|
if (animation) {
|
||||||
animation.destroy();
|
animation.removeContainer(container);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}, [animation]);
|
}, [animation]);
|
||||||
@ -151,7 +161,7 @@ const AnimatedSticker: FC<OwnProps> = ({
|
|||||||
if (playSegmentRef.current) {
|
if (playSegmentRef.current) {
|
||||||
animation.playSegment(playSegmentRef.current);
|
animation.playSegment(playSegmentRef.current);
|
||||||
} else {
|
} else {
|
||||||
animation.play(shouldRestart);
|
animation.play(shouldRestart, containerRef.current!);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, [animation]);
|
}, [animation]);
|
||||||
@ -161,7 +171,7 @@ const AnimatedSticker: FC<OwnProps> = ({
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
animation.pause();
|
animation.pause(containerRef.current!);
|
||||||
}, [animation]);
|
}, [animation]);
|
||||||
|
|
||||||
const freezeAnimation = useCallback(() => {
|
const freezeAnimation = useCallback(() => {
|
||||||
@ -229,7 +239,7 @@ const AnimatedSticker: FC<OwnProps> = ({
|
|||||||
}, [playAnimation, animation, tgsUrl]);
|
}, [playAnimation, animation, tgsUrl]);
|
||||||
|
|
||||||
useHeavyAnimationCheck(freezeAnimation, unfreezeAnimation, forceOnHeavyAnimation);
|
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.
|
||||||
useBackgroundMode(freezeAnimation, unfreezeAnimationOnRaf);
|
useBackgroundMode(freezeAnimation, unfreezeAnimationOnRaf);
|
||||||
|
|||||||
@ -93,8 +93,8 @@ const StickerView: FC<OwnProps> = ({
|
|||||||
const [isPlayerReady, markPlayerReady] = useFlag(Boolean(isLottie && fullMediaData));
|
const [isPlayerReady, markPlayerReady] = useFlag(Boolean(isLottie && fullMediaData));
|
||||||
const isFullMediaReady = fullMediaData && (isStatic || isPlayerReady);
|
const isFullMediaReady = fullMediaData && (isStatic || isPlayerReady);
|
||||||
|
|
||||||
|
const thumbClassNames = useMediaTransition(thumbData && !isFullMediaReady);
|
||||||
const fullMediaClassNames = useMediaTransition(isFullMediaReady);
|
const fullMediaClassNames = useMediaTransition(isFullMediaReady);
|
||||||
const thumbClassNames = useMediaTransition(!isFullMediaReady);
|
|
||||||
|
|
||||||
// Preload preview for Message Input and local message
|
// Preload preview for Message Input and local message
|
||||||
useMedia(previewMediaHash, !shouldLoad || !shouldPreloadPreview, undefined, cacheBuster);
|
useMedia(previewMediaHash, !shouldLoad || !shouldPreloadPreview, undefined, cacheBuster);
|
||||||
@ -108,8 +108,9 @@ const StickerView: FC<OwnProps> = ({
|
|||||||
/>
|
/>
|
||||||
{isLottie ? (
|
{isLottie ? (
|
||||||
<AnimatedSticker
|
<AnimatedSticker
|
||||||
|
key={customColor?.join(',')}
|
||||||
|
id={id}
|
||||||
size={size}
|
size={size}
|
||||||
key={fullMediaData}
|
|
||||||
className={buildClassName(styles.media, fullMediaClassName, fullMediaClassNames)}
|
className={buildClassName(styles.media, fullMediaClassName, fullMediaClassNames)}
|
||||||
tgsUrl={fullMediaData}
|
tgsUrl={fullMediaData}
|
||||||
play={shouldPlay}
|
play={shouldPlay}
|
||||||
|
|||||||
@ -7,7 +7,6 @@ import {
|
|||||||
import WorkerConnector from '../../util/WorkerConnector';
|
import WorkerConnector from '../../util/WorkerConnector';
|
||||||
import { animate } from '../../util/animation';
|
import { animate } from '../../util/animation';
|
||||||
import cycleRestrict from '../../util/cycleRestrict';
|
import cycleRestrict from '../../util/cycleRestrict';
|
||||||
import generateIdFor from '../../util/generateIdFor';
|
|
||||||
|
|
||||||
interface Params {
|
interface Params {
|
||||||
noLoop?: boolean;
|
noLoop?: boolean;
|
||||||
@ -26,7 +25,8 @@ const HIGH_PRIORITY_QUALITY = IS_SINGLE_COLUMN_LAYOUT ? 0.75 : 1;
|
|||||||
const LOW_PRIORITY_QUALITY = IS_ANDROID ? 0.5 : 0.75;
|
const LOW_PRIORITY_QUALITY = IS_ANDROID ? 0.5 : 0.75;
|
||||||
const HIGH_PRIORITY_CACHE_MODULO = IS_SAFARI ? 2 : 4;
|
const HIGH_PRIORITY_CACHE_MODULO = IS_SAFARI ? 2 : 4;
|
||||||
const LOW_PRIORITY_CACHE_MODULO = 0;
|
const LOW_PRIORITY_CACHE_MODULO = 0;
|
||||||
const KEY_STORE = {};
|
|
||||||
|
const instancesById = new Map<string, RLottie>();
|
||||||
|
|
||||||
const workers = new Array(MAX_WORKERS).fill(undefined).map(
|
const workers = new Array(MAX_WORKERS).fill(undefined).map(
|
||||||
() => new WorkerConnector(new Worker(new URL('./rlottie.worker.ts', import.meta.url))),
|
() => new WorkerConnector(new Worker(new URL('./rlottie.worker.ts', import.meta.url))),
|
||||||
@ -36,9 +36,15 @@ let lastWorkerIndex = -1;
|
|||||||
class RLottie {
|
class RLottie {
|
||||||
// Config
|
// Config
|
||||||
|
|
||||||
private imgSize!: number;
|
private containers = new Map<HTMLDivElement, {
|
||||||
|
canvas: HTMLCanvasElement;
|
||||||
|
ctx: CanvasRenderingContext2D;
|
||||||
|
onLoad?: NoneToVoidFunction;
|
||||||
|
isOnLoadFired?: boolean;
|
||||||
|
isPaused?: boolean;
|
||||||
|
}>();
|
||||||
|
|
||||||
private key = generateIdFor(KEY_STORE);
|
private imgSize!: number;
|
||||||
|
|
||||||
private msPerFrame = 1000 / 60;
|
private msPerFrame = 1000 / 60;
|
||||||
|
|
||||||
@ -56,12 +62,6 @@ class RLottie {
|
|||||||
|
|
||||||
private chunksCount?: number;
|
private chunksCount?: number;
|
||||||
|
|
||||||
// Container
|
|
||||||
|
|
||||||
private canvas = document.createElement('canvas');
|
|
||||||
|
|
||||||
private ctx = this.canvas.getContext('2d')!;
|
|
||||||
|
|
||||||
// State
|
// State
|
||||||
|
|
||||||
private isAnimating = false;
|
private isAnimating = false;
|
||||||
@ -70,10 +70,10 @@ class RLottie {
|
|||||||
|
|
||||||
private isEnded = false;
|
private isEnded = false;
|
||||||
|
|
||||||
private isOnLoadFired = false;
|
|
||||||
|
|
||||||
private isDestroyed = false;
|
private isDestroyed = false;
|
||||||
|
|
||||||
|
private isRendererInited = false;
|
||||||
|
|
||||||
private approxFrameIndex = 0;
|
private approxFrameIndex = 0;
|
||||||
|
|
||||||
private prevFrameIndex = -1;
|
private prevFrameIndex = -1;
|
||||||
@ -86,25 +86,59 @@ class RLottie {
|
|||||||
|
|
||||||
private lastRenderAt?: number;
|
private lastRenderAt?: number;
|
||||||
|
|
||||||
|
static init(...args: ConstructorParameters<typeof RLottie>) {
|
||||||
|
const [container, onLoad, id] = args;
|
||||||
|
let instance = instancesById.get(id);
|
||||||
|
|
||||||
|
if (!instance) {
|
||||||
|
// eslint-disable-next-line prefer-rest-params
|
||||||
|
instance = new RLottie(...args);
|
||||||
|
instancesById.set(id, instance);
|
||||||
|
} else {
|
||||||
|
instance.addContainer(container, onLoad);
|
||||||
|
}
|
||||||
|
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private container: HTMLDivElement,
|
container: HTMLDivElement,
|
||||||
|
onLoad: NoneToVoidFunction | undefined,
|
||||||
|
private id: string,
|
||||||
private tgsUrl: string,
|
private tgsUrl: string,
|
||||||
private params: Params = {},
|
private params: Params = {},
|
||||||
private customColor?: [number, number, number],
|
private customColor?: [number, number, number],
|
||||||
private onLoad?: () => void,
|
|
||||||
private onEnded?: (isDestroyed?: boolean) => void,
|
private onEnded?: (isDestroyed?: boolean) => void,
|
||||||
private onLoop?: () => void,
|
private onLoop?: () => void,
|
||||||
) {
|
) {
|
||||||
this.initContainer();
|
this.addContainer(container, onLoad);
|
||||||
this.initConfig();
|
this.initConfig();
|
||||||
this.initRenderer();
|
this.initRenderer();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public removeContainer(container: HTMLDivElement) {
|
||||||
|
this.containers.get(container)!.canvas.remove();
|
||||||
|
this.containers.delete(container);
|
||||||
|
|
||||||
|
if (!this.containers.size) {
|
||||||
|
this.destroy();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
isPlaying() {
|
isPlaying() {
|
||||||
return this.isAnimating || this.isWaiting;
|
return this.isAnimating || this.isWaiting;
|
||||||
}
|
}
|
||||||
|
|
||||||
play(forceRestart = false) {
|
play(forceRestart = false, container?: HTMLDivElement) {
|
||||||
|
if (container) {
|
||||||
|
this.containers.get(container)!.isPaused = false;
|
||||||
|
|
||||||
|
const areAllContainersPlaying = Array.from(this.containers.values()).every(({ isPaused }) => !isPaused);
|
||||||
|
if (!areAllContainersPlaying) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (this.isEnded && forceRestart) {
|
if (this.isEnded && forceRestart) {
|
||||||
this.approxFrameIndex = Math.floor(0);
|
this.approxFrameIndex = Math.floor(0);
|
||||||
}
|
}
|
||||||
@ -114,7 +148,16 @@ class RLottie {
|
|||||||
this.doPlay();
|
this.doPlay();
|
||||||
}
|
}
|
||||||
|
|
||||||
pause() {
|
pause(container?: HTMLDivElement) {
|
||||||
|
if (container) {
|
||||||
|
this.containers.get(container)!.isPaused = true;
|
||||||
|
|
||||||
|
const areAllContainersPaused = Array.from(this.containers.values()).every(({ isPaused }) => isPaused);
|
||||||
|
if (!areAllContainersPaused) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (this.isWaiting) {
|
if (this.isWaiting) {
|
||||||
this.stopFrameIndex = this.approxFrameIndex;
|
this.stopFrameIndex = this.approxFrameIndex;
|
||||||
} else {
|
} else {
|
||||||
@ -140,15 +183,8 @@ class RLottie {
|
|||||||
this.params.noLoop = noLoop;
|
this.params.noLoop = noLoop;
|
||||||
}
|
}
|
||||||
|
|
||||||
destroy() {
|
private addContainer(container: HTMLDivElement, onLoad?: NoneToVoidFunction) {
|
||||||
this.isDestroyed = true;
|
if (!(container.parentNode instanceof HTMLElement)) {
|
||||||
this.pause();
|
|
||||||
this.destroyRenderer();
|
|
||||||
this.destroyContainer();
|
|
||||||
}
|
|
||||||
|
|
||||||
private initContainer() {
|
|
||||||
if (!(this.container.parentNode instanceof HTMLElement)) {
|
|
||||||
throw new Error('[RLottie] Container is not mounted');
|
throw new Error('[RLottie] Container is not mounted');
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -156,9 +192,9 @@ class RLottie {
|
|||||||
|
|
||||||
if (!size) {
|
if (!size) {
|
||||||
size = (
|
size = (
|
||||||
this.container.offsetWidth
|
container.offsetWidth
|
||||||
|| parseInt(this.container.style.width, 10)
|
|| parseInt(container.style.width, 10)
|
||||||
|| this.container.parentNode.offsetWidth
|
|| container.parentNode.offsetWidth
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!size) {
|
if (!size) {
|
||||||
@ -166,19 +202,39 @@ class RLottie {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.canvas.style.width = `${size}px`;
|
const canvas = document.createElement('canvas');
|
||||||
this.canvas.style.height = `${size}px`;
|
canvas.dataset.id = this.id;
|
||||||
|
const ctx = canvas.getContext('2d')!;
|
||||||
|
|
||||||
|
canvas.style.width = `${size}px`;
|
||||||
|
canvas.style.height = `${size}px`;
|
||||||
|
|
||||||
const { isLowPriority, quality = isLowPriority ? LOW_PRIORITY_QUALITY : HIGH_PRIORITY_QUALITY } = this.params;
|
const { isLowPriority, quality = isLowPriority ? LOW_PRIORITY_QUALITY : HIGH_PRIORITY_QUALITY } = this.params;
|
||||||
// Reduced quality only looks acceptable on high DPR screens
|
// Reduced quality only looks acceptable on high DPR screens
|
||||||
const imgSize = Math.round(size * Math.max(DPR * quality, 1));
|
const imgSize = Math.round(size * Math.max(DPR * quality, 1));
|
||||||
|
|
||||||
this.canvas.width = imgSize;
|
canvas.width = imgSize;
|
||||||
this.canvas.height = imgSize;
|
canvas.height = imgSize;
|
||||||
|
|
||||||
this.container.appendChild(this.canvas);
|
container.appendChild(canvas);
|
||||||
|
|
||||||
this.imgSize = imgSize;
|
if (!this.imgSize) {
|
||||||
|
this.imgSize = imgSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.containers.set(container, { canvas, ctx, onLoad });
|
||||||
|
|
||||||
|
if (this.isRendererInited) {
|
||||||
|
this.doPlay();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private destroy() {
|
||||||
|
this.isDestroyed = true;
|
||||||
|
this.pause();
|
||||||
|
this.destroyRenderer();
|
||||||
|
|
||||||
|
instancesById.delete(this.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
private initConfig() {
|
private initConfig() {
|
||||||
@ -188,26 +244,8 @@ class RLottie {
|
|||||||
this.chunkSize = CHUNK_SIZE;
|
this.chunkSize = CHUNK_SIZE;
|
||||||
}
|
}
|
||||||
|
|
||||||
private destroyContainer() {
|
|
||||||
this.canvas.remove();
|
|
||||||
}
|
|
||||||
|
|
||||||
setColor(newColor: [number, number, number] | undefined) {
|
setColor(newColor: [number, number, number] | undefined) {
|
||||||
this.customColor = newColor;
|
this.customColor = newColor;
|
||||||
|
|
||||||
// TODO Remove?
|
|
||||||
if (this.customColor) {
|
|
||||||
const imageData = this.ctx.getImageData(0, 0, this.imgSize, this.imgSize);
|
|
||||||
const arr = imageData.data;
|
|
||||||
for (let i = 0; i < arr.length; i += 4) {
|
|
||||||
/* eslint-disable prefer-destructuring */
|
|
||||||
arr[i] = this.customColor[0];
|
|
||||||
arr[i + 1] = this.customColor[1];
|
|
||||||
arr[i + 2] = this.customColor[2];
|
|
||||||
/* eslint-enable prefer-destructuring */
|
|
||||||
}
|
|
||||||
this.ctx.putImageData(imageData, 0, 0);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private initRenderer() {
|
private initRenderer() {
|
||||||
@ -216,7 +254,7 @@ class RLottie {
|
|||||||
workers[this.workerIndex].request({
|
workers[this.workerIndex].request({
|
||||||
name: 'init',
|
name: 'init',
|
||||||
args: [
|
args: [
|
||||||
this.key,
|
this.id,
|
||||||
this.tgsUrl,
|
this.tgsUrl,
|
||||||
this.imgSize,
|
this.imgSize,
|
||||||
this.params.isLowPriority,
|
this.params.isLowPriority,
|
||||||
@ -228,11 +266,12 @@ class RLottie {
|
|||||||
private destroyRenderer() {
|
private destroyRenderer() {
|
||||||
workers[this.workerIndex].request({
|
workers[this.workerIndex].request({
|
||||||
name: 'destroy',
|
name: 'destroy',
|
||||||
args: [this.key],
|
args: [this.id],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private onRendererInit(reduceFactor: number, msPerFrame: number, framesCount: number) {
|
private onRendererInit(reduceFactor: number, msPerFrame: number, framesCount: number) {
|
||||||
|
this.isRendererInited = true;
|
||||||
this.reduceFactor = reduceFactor;
|
this.reduceFactor = reduceFactor;
|
||||||
this.msPerFrame = msPerFrame;
|
this.msPerFrame = msPerFrame;
|
||||||
this.framesCount = framesCount;
|
this.framesCount = framesCount;
|
||||||
@ -251,7 +290,7 @@ class RLottie {
|
|||||||
workers[this.workerIndex].request({
|
workers[this.workerIndex].request({
|
||||||
name: 'changeData',
|
name: 'changeData',
|
||||||
args: [
|
args: [
|
||||||
this.key,
|
this.id,
|
||||||
this.tgsUrl,
|
this.tgsUrl,
|
||||||
this.params.isLowPriority,
|
this.params.isLowPriority,
|
||||||
this.onChangeData.bind(this),
|
this.onChangeData.bind(this),
|
||||||
@ -297,7 +336,7 @@ class RLottie {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Paused from outside
|
// Paused from outside
|
||||||
if (!this.isAnimating && this.isOnLoadFired) {
|
if (!this.isAnimating && Array.from(this.containers.values())[0]!.isOnLoadFired) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -335,12 +374,23 @@ class RLottie {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
const imageData = new ImageData(arr, this.imgSize, this.imgSize);
|
const imageData = new ImageData(arr, this.imgSize, this.imgSize);
|
||||||
this.ctx.putImageData(imageData, 0, 0);
|
|
||||||
|
|
||||||
if (this.onLoad && !this.isOnLoadFired) {
|
this.containers.forEach((containerData) => {
|
||||||
this.isOnLoadFired = true;
|
const {
|
||||||
this.onLoad();
|
ctx, onLoad, isOnLoadFired, isPaused,
|
||||||
}
|
} = containerData;
|
||||||
|
|
||||||
|
if (isPaused) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.putImageData(imageData, 0, 0);
|
||||||
|
|
||||||
|
if (onLoad && !isOnLoadFired) {
|
||||||
|
containerData.isOnLoadFired = true;
|
||||||
|
onLoad();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
this.prevFrameIndex = frameIndex;
|
this.prevFrameIndex = frameIndex;
|
||||||
}
|
}
|
||||||
@ -439,7 +489,7 @@ class RLottie {
|
|||||||
|
|
||||||
workers[this.workerIndex].request({
|
workers[this.workerIndex].request({
|
||||||
name: 'renderFrames',
|
name: 'renderFrames',
|
||||||
args: [this.key, fromIndex, toIndex, this.onFrameLoad.bind(this)],
|
args: [this.id, fromIndex, toIndex, this.onFrameLoad.bind(this)],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,11 +1,13 @@
|
|||||||
const generateIdFor = (store: AnyLiteral) => {
|
export default function generateIdFor(store: AnyLiteral, withAutoUpdate = false) {
|
||||||
let id;
|
let id;
|
||||||
|
|
||||||
do {
|
do {
|
||||||
id = String(Math.random()).replace('0.', 'id');
|
id = String(Math.random()).replace('0.', 'id');
|
||||||
} while (store.hasOwnProperty(id));
|
} while (store[id]);
|
||||||
|
|
||||||
|
if (withAutoUpdate) {
|
||||||
|
store[id] = true;
|
||||||
|
}
|
||||||
|
|
||||||
return id;
|
return id;
|
||||||
};
|
}
|
||||||
|
|
||||||
export default generateIdFor;
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user