rive-app / rive-wasm

Wasm/JS runtime for Rive
MIT License
735 stars 52 forks source link

Unable to dynamically swap images when using `@rive-app/canvas-advanced` #361

Closed zoephilipps closed 3 months ago

zoephilipps commented 4 months ago

Description

Hi there, I am trying to use Remotion + Rive + Referenced Assets, and I'm running into a weird error message coming from @rive-app/canvas-advanced:

Expected null or instance of RenderImage, got an instance of RenderImage

Please view the details here (including the .riv file): https://github.com/remotion-dev/remotion/issues/4079#issuecomment-2234375571

Browser & Versions (please complete the following information)

HayesGordon commented 4 months ago

Hi @zackphilipps, I had a look into the linked issue and noted that you're including both:

import {FileAsset, ImageAsset} from "@rive-app/canvas-advanced";
import {decodeImage} from "@rive-app/react-canvas";

That might be what you're seeing regarding the complaint about multiple instances. I also see you linked one of our examples and that you're only using the canvas-advanced package to include {FileAsset, ImageAsset}. Instead you can add these through the "@rive-app/react-canvas" dependency. Typically you should only include one, though we should investigate why including both is giving problems.

Here is a modified example of the project that you linked that does the above:

import "./styles.css";
import {
  useRive,
  Layout,
  Fit,
  Alignment,
  decodeImage,
  ImageAsset, // here
  FontAsset, // here
} from "@rive-app/react-canvas";

export const RiveDemo = () => {
  const { RiveComponent } = useRive({
    src: "holo_card.riv",
    stateMachines: "cardInteractivity",
    layout: new Layout({
      fit: Fit.Cover,
      alignment: Alignment.Center,
    }),
    autoplay: true,
    // Callback handler to pass in that dictates what to do with an asset found in
    // the Rive file that's being loaded in
    assetLoader: (asset, bytes) => {
      console.log(
        "Tell our asset importer if we are going to load the asset contents",
        {
          name: asset.name,
          fileExtension: asset.fileExtension,
          cdnUuid: asset.cdnUuid,
          isFont: asset.isFont,
          isImage: asset.isImage,
          bytes,
        }
      );

      // Here, we load a font asset with a random font on load of the Rive file
      // and return true, because this callback handler is responsible for loading
      // the asset, as opposed to the runtime
      if (asset.isImage) {
        randomImageAsset(asset as ImageAsset);
        return true;
      } else {
        return false;
      }
    },
  });

  return <RiveComponent />;
};

// Load a random asset by using decodeImage API to feed to a
// setRenderImage API on the asset provided in assetLoader
const randomImageAsset = (asset: ImageAsset) => {
  fetch("https://picsum.photos/300/500").then(async (res) => {
    console.log("doing this");
    const image = await decodeImage(new Uint8Array(await res.arrayBuffer()));
    asset.setRenderImage(image);
    // You could maintain a reference and update the image dynamically at any time.
    // But be sure to call unref to release any references when no longer needed.
    // This allows the engine to clean it up when it is not used by any more animations.
    image.unref();
  });
};

export const OverviewText = () => {
  return (
    <div className="paragraph-text">
      <p>Click to change drink, refresh to choose random font</p>
    </div>
  );
};

export default function App() {
  return (
    <div>
      <div className="RiveContainer">
        <RiveDemo />
      </div>
      <OverviewText />
    </div>
  );
}

I am interested in your use case and why you opted to also include canvas-advanced.

HayesGordon commented 3 months ago

Closing this issue for now as it seems to be addressed. Please feel free to reopen if needed.