rive-app/rive-react

Load animation once

Soarc opened this issue · 3 comments

Soarc commented

I wondering, is there any way to load rive animation once and use in multiple places?

I had button component with loader animation on it. I used that button on multiple times on page and it results to unnessary http request for each button.

image

This is simplified version of component I use

import RiveComponent, { Alignment, Fit, Layout } from '@rive-app/react-canvas';

export const Button =() => {    
    return <button type="button"  className="flex">
        ClickMe
        <RiveComponent src="/loading-wave.riv" layout={new Layout({ fit: Fit.Contain, alignment: Alignment.Center })} />
    </button>
});

Probably related to #135

Soarc commented

Since I'm using nextjs I was able to workaround animations loading via webpack url-loader.

You will need to install url-loader npm package and modify next.config.js.

const nextConfig = {
  reactStrictMode: true,
  webpack: (
    config,
    { buildId, dev, isServer, defaultLoaders, nextRuntime, webpack }
  ) => {
    config.module.rules.push({
      test: /\.riv$/,
      use: {
        loader: "url-loader",
        options: {
          limit: 100000,
        },
      },
    });
    return config;
  },
};

module.exports = nextConfig;

Now you will be able to use animations as modules

import LoadingWave from "assets/loading-wave.riv";
export const Button =() => {    
    return <button type="button"  className="flex">
        ClickMe
        <RiveComponent src={LoadingWave} layout={new Layout({ fit: Fit.Contain, alignment: Alignment.Center })} />
    </button>
});

I also needed to add rive.d.ts typescript definition definition to get rid off VSCode "cannot find module" errors.

declare module "*.riv" {
    const value: string;
    export default value;
}

@Soarc Thank you so much, been struggling with this.

We just released a new useRiveFile hook that makes it easier to reuse a RiveFile instance.

Here's a simplified example of how you can use it:

import React, { useState } from 'react';
import { useRiveFile } from '@rive-app/react-canvas';

// Custom Wrapper component to display the Rive animation
const RiveAnimation = ({ riveFile }) => {
  const { RiveComponent } = useRive({
    riveFile,
    autoplay: true
  });

  return <RiveComponent/>;
};

function App() {
  const { riveFile, status } = useRiveFile({
    src: 'https://cdn.rive.app/animations/myrivefile.riv',
  });

  const [instanceCount] = useState(5); // Number of RiveAnimation components to render

  if (status === 'loading') {
    return <div>Loading...</div>;
  }

  if (status === 'failed') {
    return <div>Failed to load Rive file.</div>;
  }

// Each RiveAnimation component uses the RiveFile we loaded earlier, so it is only fetched and initialized once
 return (
      <div className="App">
        <header className="App-header">Rive Instances</header>
        <div className="rive-list">
          {Array.from({ length: instanceCount }, (_, index) => (
            <RiveAnimation key={`rive-instance-${index}`} riveFile={riveFile} />
          ))}
        </div>
      </div>
    );
  
}

export default App;