dotlottie-web icon indicating copy to clipboard operation
dotlottie-web copied to clipboard

InvalidStateError: Failed to execute 'transferControlToOffscreen' on 'HTMLCanvasElement': Cannot transfer control from a canvas more than once.

Open theashraf opened this issue 1 year ago • 9 comments

DotLottieWorkerReact on Next.js is throwing the following error:

InvalidStateError: Failed to execute 'transferControlToOffscreen' on 'HTMLCanvasElement': Cannot transfer control from a canvas more than once.
image

theashraf avatar Aug 19 '24 06:08 theashraf

Hey @theashraf ! did you find anything useful?

flow3d avatar Oct 08 '24 17:10 flow3d

We have the same issue. We use @lottiefiles/dotlottie-web@^0.34.0 with next@^14.2.13. (We preferred to use @lottiefiles/dotlottie-web over @lottiefiles/dotlottie-react due to an issue the react package has with config.layout)

Way to reproduce:

"use client";

import { DotLottieWorker, type Config } from "@lottiefiles/dotlottie-web";
import {
  type ComponentPropsWithoutRef,
  useEffect,
  useLayoutEffect,
  useRef,
} from "react";

type DotLottieAnimationProps = ComponentPropsWithoutRef<"canvas"> & {
  readonly src: NonNullable<Config["src"]>;
  readonly height: string;
  readonly width: string;
};

const useIsomorphicLayoutEffect =
  typeof window === "undefined" ? useEffect : useLayoutEffect;

function createLottieWorker(canvas: HTMLCanvasElement, src: string): DotLottieWorker {
  return new DotLottieWorker({
    workerId: "dotlottie-worker",
    canvas,
    backgroundColor: "transparent",
    layout: { fit: "fit-width", align: [0.5, 0.5] },
    renderConfig: {
      autoResize: true,
      freezeOnOffscreen: false,
      devicePixelRatio: 5,
    },
    src,
    loop: true,
    autoplay: true,
  });
}

export function DotLottieAnimation({
  src,
  height,
  width,
  style,
  ...props
}: DotLottieAnimationProps): React.ReactNode {
  const ref = useRef<HTMLCanvasElement>(null);

  useIsomorphicLayoutEffect(() => {
    if (ref.current === null) {
      return;
    }

    const worker = createLottieWorker(ref.current, src);

    return () => {
      void worker.destroy();
    };
  }, [ref, src]);

  return <canvas ref={ref} style={{ height, width, ...style }} {...props} />;
}

alumag avatar Oct 09 '24 08:10 alumag

We get the same error just by enabling StrictMode in our nextjs app.

lazybean avatar Oct 11 '24 07:10 lazybean

@flow3d I haven't had time to look into this issue yet, but I'm happy to hear that you accept PRs if someone is interested in helping to fix it

theashraf avatar Oct 11 '24 09:10 theashraf

Thanks to @lazybean comment I figured out it happens because createLottieWorker is called twice when StrictMode is enabled, causing a side-effect to run twice. I made this patch to make sure the effect runs only once. This will prevent re-rendering on any state changes, so be aware that this solution might not be suitable for you.

export function DotLottieAnimation({
  src,
  height,
  width,
  style,
  ...props
}: DotLottieAnimationProps): React.ReactNode {
  const ref = useRef<HTMLCanvasElement>(null);

  useIsomorphicLayoutEffect(() => {
    if (ref.current === null) {
      return;
    }

    const { current: canvas } = ref;

    // `createLottieWorker` has a side effect, so we have to force a single worker per canvas.
    // This is a workaround to prevent multiple workers from being created, which could happen
    // when react is on strict mode (development) or when the `src` prop changes.
    // It means that changing the props `src` will not cause re-render.
    if (canvas.hasAttribute("animation")) {
      return;
    }

    canvas.setAttribute("animation", "true");
    const worker = createLottieWorker(canvas, src);

    return () => {
      void worker.destroy();
    };
  }, [ref.current, src]);

  return <canvas ref={ref} style={{ height, width, ...style }} {...props} />;
}

alumag avatar Oct 27 '24 09:10 alumag

resolved in @lottiefiles/[email protected].

theashraf avatar Dec 17 '24 08:12 theashraf

@theashraf I get the same exception and use recent version of next, react, @lottiefiles/dotlottie-react packages.

InvalidStateError: Failed to execute 'transferControlToOffscreen' on 'HTMLCanvasElement': Cannot transfer control from a canvas for more than one time. at c1.eval (webpack-internal:///(app-pages-browser)/./node_modules/@lottiefiles/dotlottie-react/dist/browser/index.js:14:381227) at Generator.next () at eval (webpack-internal:///(app-pages-browser)/./node_modules/@lottiefiles/dotlottie-react/dist/browser/index.js:9:833) at new Promise () at g (webpack-internal:///(app-pages-browser)/./node_modules/@lottiefiles/dotlottie-react/dist/browser/index.js:9:649) at c1._create (webpack-internal:///(app-pages-browser)/./node_modules/@lottiefiles/dotlottie-react/dist/browser/index.js:14:381134) at new c1 (webpack-internal:///(app-pages-browser)/./node_modules/@lottiefiles/dotlottie-react/dist/browser/index.js:14:378396) at u0 (webpack-internal:///(app-pages-browser)/./node_modules/@lottiefiles/dotlottie-react/dist/browser/index.js:14:395347) at eval (webpack-internal:///(app-pages-browser)/./node_modules/@lottiefiles/dotlottie-react/dist/browser/index.js:14:392918) at commitAttachRef (webpack-internal:///(app-pages-browser)/./node_modules/next/dist/compiled/react-dom/cjs/react-dom-client.development.js:10780:44) at runWithFiberInDEV (webpack-internal:///(app-pages-browser)/./node_modules/next/dist/compiled/react-dom/cjs/react-dom-client.development.js:1511:30) at safelyAttachRef (webpack-internal:///(app-pages-browser)/./node_modules/next/dist/compiled/react-dom/cjs/react-dom-client.development.js:10798:9) at reappearLayoutEffects (webpack-internal:///(app-pages-browser)/./node_modules/next/dist/compiled/react-dom/cjs/react-dom-client.development.js:12311:11) at recursivelyTraverseReappearLayoutEffects (webpack-internal:///(app-pages-browser)/./node_modules/next/dist/compiled/react-dom/cjs/react-dom-client.development.js:12378:9) at reappearLayoutEffects (webpack-internal:///(app-pages-browser)/./node_modules/next/dist/compiled/react-dom/cjs/react-dom-client.development.js:12302:11) at recursivelyTraverseReappearLayoutEffects (webpack-internal:///(app-pages-browser)/./node_modules/next/dist/compiled/react-dom/cjs/react-dom-client.development.js:12378:9) at reappearLayoutEffects (webpack-internal:///(app-pages-browser)/./node_modules/next/dist/compiled/react-dom/cjs/react-dom-client.development.js:12258:11) at recursivelyTraverseReappearLayoutEffects (webpack-internal:///(app-pages-browser)/./node_modules/next/dist/compiled/react-dom/cjs/react-dom-client.development.js:12378:9) at reappearLayoutEffects (webpack-internal:///(app-pages-browser)/./node_modules/next/dist/compiled/react-dom/cjs/react-dom-client.development.js:12258:11) at recursivelyTraverseReappearLayoutEffects (webpack-internal:///(app-pages-browser)/./node_modules/next/dist/compiled/react-dom/cjs/react-dom-client.development.js:12378:9) at reappearLayoutEffects (webpack-internal:///(app-pages-browser)/./node_modules/next/dist/compiled/react-dom/cjs/react-dom-client.development.js:12302:11) at recursivelyTraverseReappearLayoutEffects (webpack-internal:///(app-pages-browser)/./node_modules/next/dist/compiled/react-dom/cjs/react-dom-client.development.js:12378:9) at reappearLayoutEffects (webpack-internal:///(app-pages-browser)/./node_modules/next/dist/compiled/react-dom/cjs/react-dom-client.development.js:12302:11) at recursivelyTraverseReappearLayoutEffects (webpack-internal:///(app-pages-browser)/./node_modules/next/dist/compiled/react-dom/cjs/react-dom-client.development.js:12378:9) at reappearLayoutEffects (webpack-internal:///(app-pages-browser)/./node_modules/next/dist/compiled/react-dom/cjs/react-dom-client.development.js:12302:11) at recursivelyTraverseReappearLayoutEffects (webpack-internal:///(app-pages-browser)/./node_modules/next/dist/compiled/react-dom/cjs/react-dom-client.development.js:12378:9) at reappearLayoutEffects (webpack-internal:///(app-pages-browser)/./node_modules/next/dist/compiled/react-dom/cjs/react-dom-client.development.js:12302:11) at recursivelyTraverseReappearLayoutEffects (webpack-internal:///(app-pages-browser)/./node_modules/next/dist/compiled/react-dom/cjs/react-dom-client.development.js:12378:9) at reappearLayoutEffects (webpack-internal:///(app-pages-browser)/./node_modules/next/dist/compiled/react-dom/cjs/react-dom-client.development.js:12258:11) at recursivelyTraverseReappearLayoutEffects (webpack-internal:///(app-pages-browser)/./node_modules/next/dist/compiled/react-dom/cjs/react-dom-client.development.js:12378:9) at reappearLayoutEffects (webpack-internal:///(app-pages-browser)/./node_modules/next/dist/compiled/react-dom/cjs/react-dom-client.development.js:12258:11) at doubleInvokeEffectsOnFiber (webpack-internal:///(app-pages-browser)/./node_modules/next/dist/compiled/react-dom/cjs/react-dom-client.development.js:16025:11) at runWithFiberInDEV (webpack-internal:///(app-pages-browser)/./node_modules/next/dist/compiled/react-dom/cjs/react-dom-client.development.js:1511:30) at recursivelyTraverseAndDoubleInvokeEffectsInDEV (webpack-internal:///(app-pages-browser)/./node_modules/next/dist/compiled/react-dom/cjs/react-dom-client.development.js:15987:17) at recursivelyTraverseAndDoubleInvokeEffectsInDEV (webpack-internal:///(app-pages-browser)/./node_modules/next/dist/compiled/react-dom/cjs/react-dom-client.development.js:15994:17) at recursivelyTraverseAndDoubleInvokeEffectsInDEV (webpack-internal:///(app-pages-browser)/./node_modules/next/dist/compiled/react-dom/cjs/react-dom-client.development.js:15994:17) at recursivelyTraverseAndDoubleInvokeEffectsInDEV (webpack-internal:///(app-pages-browser)/./node_modules/next/dist/compiled/react-dom/cjs/react-dom-client.development.js:15994:17) at recursivelyTraverseAndDoubleInvokeEffectsInDEV (webpack-internal:///(app-pages-browser)/./node_modules/next/dist/compiled/react-dom/cjs/react-dom-client.development.js:15994:17) at recursivelyTraverseAndDoubleInvokeEffectsInDEV (webpack-internal:///(app-pages-browser)/./node_modules/next/dist/compiled/react-dom/cjs/react-dom-client.development.js:15994:17) at recursivelyTraverseAndDoubleInvokeEffectsInDEV (webpack-internal:///(app-pages-browser)/./node_modules/next/dist/compiled/react-dom/cjs/react-dom-client.development.js:15994:17) at recursivelyTraverseAndDoubleInvokeEffectsInDEV (webpack-internal:///(app-pages-browser)/./node_modules/next/dist/compiled/react-dom/cjs/react-dom-client.development.js:15994:17) at recursivelyTraverseAndDoubleInvokeEffectsInDEV (webpack-internal:///(app-pages-browser)/./node_modules/next/dist/compiled/react-dom/cjs/react-dom-client.development.js:15994:17) at recursivelyTraverseAndDoubleInvokeEffectsInDEV (webpack-internal:///(app-pages-browser)/./node_modules/next/dist/compiled/react-dom/cjs/react-dom-client.development.js:15994:17) at recursivelyTraverseAndDoubleInvokeEffectsInDEV (webpack-internal:///(app-pages-browser)/./node_modules/next/dist/compiled/react-dom/cjs/react-dom-client.development.js:15994:17) at recursivelyTraverseAndDoubleInvokeEffectsInDEV (webpack-internal:///(app-pages-browser)/./node_modules/next/dist/compiled/react-dom/cjs/react-dom-client.development.js:15994:17) at recursivelyTraverseAndDoubleInvokeEffectsInDEV (webpack-internal:///(app-pages-browser)/./node_modules/next/dist/compiled/react-dom/cjs/react-dom-client.development.js:15994:17) at recursivelyTraverseAndDoubleInvokeEffectsInDEV (webpack-internal:///(app-pages-browser)/./node_modules/next/dist/compiled/react-dom/cjs/react-dom-client.development.js:15994:17) at recursivelyTraverseAndDoubleInvokeEffectsInDEV (webpack-internal:///(app-pages-browser)/./node_modules/next/dist/compiled/react-dom/cjs/react-dom-client.development.js:15994:17) at recursivelyTraverseAndDoubleInvokeEffectsInDEV (webpack-internal:///(app-pages-browser)/./node_modules/next/dist/compiled/react-dom/cjs/react-dom-client.development.js:15994:17) at recursivelyTraverseAndDoubleInvokeEffectsInDEV (webpack-internal:///(app-pages-browser)/./node_modules/next/dist/compiled/react-dom/cjs/react-dom-client.development.js:15994:17)

meness avatar Mar 24 '25 06:03 meness

Hi @theashraf can I work on this...

Sukuna0007Abhi avatar Oct 09 '25 01:10 Sukuna0007Abhi

sure @Sukuna0007Abhi

theashraf avatar Oct 09 '25 03:10 theashraf