Why do I have frame dropped but no main script is used

I am currently trying to improve animations performances on my project. I have so issues with one animation that I don’t understand. The slowdowns occurs on devices not that old (Honor 9, Pixel 5). I am usually arround 20 fps, I would love to be above 30, or event at 60 (I am on a pixel 6a).

For context :

  • I use a requestAnimationFrame loop to play my animation.
  • I only modify opacity and transform on my elements.

I get a lot of frame drop but the main thread does not seems to be used at this point.

I’m adding the performances report to my message.
Performance report

As you can see, 1 have a lot of frame dropped from 2100ms to 2300ms, but not a lot is going on in the main thread, and I can’t figure why my frames are dropped. I know I have a long task (100ms) before and a long one (210ms) after, but can this be the cause of the drop ? I’ve try reducing it, but I feel like this does not impact my animation that much.

For my animations, I used the following code :

AnimatedValue.ts

export class AnimatedValue {
    startTime = 0;
    steps = 0;

    constructor(private readonly progressCallback: (progress: number) => void, private readonly duration = 1000) {}

    animate() {
        requestAnimationFrame((chrono) => {
            if (!this.startTime) {
                this.startTime = chrono;
                this.steps = 0;
            }
            const elapsedTime = chrono - this.startTime;
            const progress = elapsedTime / this.duration;
            if (progress < 1) {
                this.progressCallback(progress);
                this.animate();
                this.steps++;
            } else {
                this.progressCallback(1);
            }
        });
    }
}

Animate.ts

const animate = async (callback: (progress: number) => void, duration: number, onEnd = () => {}) => {
    return new Promise<void>((resolve) => {
        const animation = new AnimatedValue((progress) => {
            callback(progress);
            if (progress === 1) {
                requestAnimationFrame(() => {
                    onEnd();
                    resolve();
                });
            }
        }, duration);
        animation.animate();
    });
};


export const animateOpen = async (
    feedPlayerElement: HTMLElement,
    backgroundElement: HTMLElement | null,
    fromInformation: { originX?: number; originY?: number },
) => {
    const { originX = 50, originY = 50 } = fromInformation;
    feedPlayerElement.style.transformOrigin = `${originX}% ${originY}%`;

    await animate(
        (progress) => {
            feedPlayerElement.style.transform = `scale(${progress})`;
            if (backgroundElement) backgroundElement.style.opacity = progress.toString();
        },
        CLOSE_ANIMATION_DURATION,
        () => {
            feedPlayerElement.style.transform = '';
            feedPlayerElement.style.transformOrigin = '';
            if (backgroundElement) backgroundElement.style.opacity = '';
        },
    );
};

I am usually arround 20 fps, I would love to be above 30, or event at 60 (I am on a pixel 6a).