21.2 C
New York
Monday, May 12, 2025

Integrating Rive right into a React Mission: Behind the Scenes of Valley Adventures


Bringing new instruments right into a workflow is all the time thrilling—curiosity bumps up in opposition to the consolation of acquainted strategies. However when our longtime consumer, Chumbi Valley, got here to us with their Valley Adventures undertaking, we noticed the proper alternative to experiment with Rive and craft cartoon-style animations that matched the playful spirit of the model.

Rive is a strong real-time interactive design instrument with built-in help for interactivity by State Machines. On this information, we’ll stroll you thru how we built-in a .riv file right into a React atmosphere and added mouse-responsive animations.

We’ll additionally stroll by a modernized integration technique utilizing Rive’s newer Information Binding characteristic—our present most popular strategy for reaching the identical animation with much less complexity and better flexibility.

Animation Idea & File Preparation

Valley Adventures is a gamified Chumbi NFT staking program, the place magical creatures known as Chumbi inhabit an enchanted world. The visible route leans closely into fairytale e book illustrations—vibrant colours, playful characters, and a whimsical, cartoon-like aesthetic.

To right away immerse customers on this world, we went with a full-section hero animation on the touchdown web page. We break up the animation into two elements:

  • an idle animation that brings the scene to life;
  • a cursor-triggered parallax impact, including depth and interactivity.

A number of parts animate concurrently—background layers like rustling leaves and flickering fireflies, together with foreground characters that react to motion. The result’s a dynamic, storybook-like expertise that invitations customers to discover.

Probably the most fascinating—and trickiest—a part of the mixing was tying animations to mouse monitoring. Rive supplies a built-in method to deal with this: by making use of constraints with various strengths to parts inside a bunch that’s linked to Mouse Monitoring, which itself responds to the cursor’s place.

Nevertheless, we encountered a limitation with this strategy: the HTML buttons layered above the Rive asset had been blocking the hover state, stopping it from triggering the animation beneath.

To work round this, we used a extra strong technique that gave us finer management and averted these issues altogether. 

Right here’s how we approached it:

  1. Create 4 separate timelines, every with a single keyframe representing an excessive place of the animation group:
    • Far left
    • Far proper
    • High
    • Backside
  2. Add two animation layers, every answerable for mixing between reverse keyframes:
    • Layer 1 blends the far-left and far-right timelines
    • Layer 2 blends the highest and backside timelines
  3. Tie every layer’s mix quantity to a numeric enter—one for the X axis, one for the Y axis.

By adjusting the values of those inputs primarily based on the cursor’s place, you’ll be able to management how tightly the animation responds on every axis. This strategy provides you a smoother, extra customizable parallax impact—and prevents sudden conduct attributable to overlapping UI.

As soon as the animation is prepared, merely export it as a .riv file—and depart the remainder of the magic to the devs.

How We Did It: Integrating a Rive File right into a React Mission

Earlier than we dive additional, let’s make clear what a .riv file truly is.

A .riv file is the export format from the Rive editor. It will possibly embrace:

  • vector graphics,
  • timeline animations,
  • a State Machine with enter parameters.

In our case, we’re utilizing a State Machine with two numeric inputs: Axis_X and Axis_Y. These inputs are tied to how we management animation in Rive, utilizing values from the X and Y axes of the cursor’s place.

These inputs drive the motion of various parts—just like the swaying leaves, fluttering fireflies, and even delicate character reactions—making a easy, interactive expertise that responds to the consumer’s mouse.

Step-by-Step Integration

Step 1: Set up the Rive React runtime

Set up the official package deal:

npm set up @rive-app/react-canvas

Step 2: Create an Animation Element

Create a part known as RiveBackground.tsx to deal with loading and rendering the animation.

Step 3: Join animation

const { rive, setCanvasRef, setContainerRef } = useRive({
  src: 'https://cdn.rive.app/animations/hero.riv',
  autoplay: true,
  format: new Structure({ match: Match.Cowl, alignment: Alignment.Heart }),
  onLoad: () => setIsLoaded(true),
  enableRiveAssetCDN: true,
});

For a greater understanding, let’s take a more in-depth take a look at every prop you’ll sometimes use when working with Rive in React:

What every choice does:

Property Description
src Path to your .riv file — might be native or hosted through CDN
autoplay Mechanically begins the animation as soon as it’s loaded
format Controls how the animation matches into the canvas (we’re utilizing Cowl and Heart)
onLoad Callback that fires when the animation is prepared — helpful for setting isLoaded
enableRiveAssetCDN Permits loading of exterior property (like fonts or textures) from Rive’s CDN

Step 4: Join State Machine Inputs

const numX = useStateMachineInput(rive, 'State Machine 1', 'Axis_X', 0);
const numY = useStateMachineInput(rive, 'State Machine 1', 'Axis_Y', 0);

This setup connects on to the enter values outlined contained in the State Machine, permitting us to replace them dynamically in response to consumer interplay.

  • State Machine 1 — the title of your State Machine, precisely as outlined within the Rive editor
  • Axis_X and Axis_Y — numeric inputs that management motion primarily based on cursor place
  • 0 — the preliminary (default) worth for every enter

☝️ Vital: Ensure that your .riv file consists of the precise names: Axis_X, Axis_Y, and State Machine 1. These should match what’s outlined within the Rive editor — in any other case, the animation received’t reply as anticipated.

Step 5: Deal with Mouse Motion

useEffect(() => {
  if (!numX || !numY) return;

  const handleMouseMove = (e: MouseEvent) => {
    const { innerWidth, innerHeight } = window;
    numX.worth = (e.clientX / innerWidth) * 100;
    numY.worth = 100 - (e.clientY / innerHeight) * 100;
  };

  window.addEventListener('mousemove', handleMouseMove);
  return () => window.removeEventListener('mousemove', handleMouseMove);
}, [numX, numY]);

What’s taking place right here:

  • We use clientX and clientY to trace the mouse place inside the browser window.
  • The values are normalized to a 0–100 vary, matching what the animation expects.
  • These normalized values are then handed to the Axis_X and Axis_Y inputs within the Rive State Machine, driving the interactive animation.

⚠️ Vital: All the time bear in mind to take away the occasion listener when the part unmounts to keep away from reminiscence leaks and undesirable conduct. 

Step 6: Cleanup and Render the Element

useEffect(() => {
  return () => rive?.cleanup();
}, [rive]);

And the render:

return (
  <div
    ref={setContainerRef}
    className={`rive-container ${className ?? ''} ${isLoaded ? 'present' : 'conceal'}`}
  >
    <canvas ref={setCanvasRef} />
  </div>
);
  • cleanup() — frees up sources when the part unmounts. All the time name this to forestall reminiscence leaks.
  • setCanvasRef and setContainerRef — these should be linked to the proper DOM parts to ensure that Rive to render the animation correctly.

And right here’s the whole code:

import {
  useRive,
  useStateMachineInput,
  Structure,
  Match,
  Alignment,
} from '@rive-app/react-canvas';
import { useEffect, useState } from 'react';

export operate RiveBackground({ className }: { className?: string }) {
  const [isLoaded, setIsLoaded] = useState(false);

  const { rive, setCanvasRef, setContainerRef } = useRive({
    src: 'https://cdn.rive.app/animations/hero.riv',
    animations: ['State Machine 1','Timeline 1','Timeline 2'
],
    autoplay: true,
    format: new Structure({ match: Match.Cowl, alignment: Alignment.Heart }),
    onLoad: () => setIsLoaded(true),
    enableRiveAssetCDN: true,
  });

  const numX = useStateMachineInput(rive, 'State Machine 1', 'Axis_X', 0);
  const numY = useStateMachineInput(rive, 'State Machine 1', 'Axis_Y', 0);

  useEffect(() => {
    if (!numX || !numY) return;

    const handleMouseMove = (e: MouseEvent) => {
	if (!numX || !numY) {
        return;
      }

      const { innerWidth, innerHeight } = window;
      numX.worth = (e.clientX / innerWidth) * 100;
      numY.worth = 100 - (e.clientY / innerHeight) * 100;
    };

    window.addEventListener('mousemove', handleMouseMove);
    return () => window.removeEventListener('mousemove', handleMouseMove);
  }, [numX, numY]);

  useEffect(() => {
    return () => {
      rive?.cleanup();
    };
  }, [rive]);

  return (
    <div
      ref={setContainerRef}
      className={`rive-container ${className ?? ''} ${isLoaded ? 'present' : 'conceal'}`}
    >
      <canvas ref={setCanvasRef} />
    </div>
  );
}

Step 7: Use the Element

Now you need to use the RiveBackground like some other part:

<RiveBackground className="hero-background" />

Step 8: Preload the WASM File

To keep away from loading the .wasm file at runtime—which might delay the preliminary render—you’ll be able to preload it in App.tsx:

import riveWASMResource from '@rive-app/canvas/rive.wasm';

<hyperlink
  rel="preload"
  href={riveWASMResource}
  as="fetch"
  crossOrigin="nameless"
/>

That is particularly helpful if you happen to’re optimizing for first paint or general efficiency.

Easy Parallax: A New Strategy with Information Binding

Within the first a part of this text, we used a basic strategy with a State Machine to create the parallax animation in Rive. We constructed 4 separate animations (prime, backside, left, proper), managed them utilizing enter variables, and blended their states to create easy movement. This technique made sense on the time, particularly earlier than Information Binding help was launched.

However now that Information Binding is offered in Rive, reaching the identical impact is way easier—only a few steps. Information binding in Rive is a system that connects editor parts to dynamic knowledge and code through view fashions, enabling reactive, runtime-driven updates and interactions between design and improvement.

On this part, we’ll present methods to refactor the unique Rive file and code utilizing the brand new strategy.

Updating the Rive File

  1. Take away the outdated setup:
    • Go to the State Machine.
    • Delete the enter variables: prime, backside, left, proper.
    • Take away the mixing states and their related animations.
  2. Group the parallax layers:
    • Wrap all of the parallax layers into a brand new group—e.g., ParallaxGroup.
  3. Create binding parameters:
    • Choose ParallaxGroup and add:
      • pointerX (Quantity)
      • pointerY (Quantity)
  4. Bind coordinates:
    • Within the properties panel, set:
      • X → pointerX
      • Y → pointerY

Now the group will transfer dynamically primarily based on values handed from JavaScript.

The Up to date JS Code

Earlier than we dive into the up to date JavaScript, let’s shortly outline an essential idea:

When utilizing Information Binding in Rive, viewModelInstance refers back to the runtime object that hyperlinks your Rive file’s bindable properties (like pointerX or pointerY) to your app’s logic. Within the Rive editor, you assign these properties to parts like positions, scales, or rotations. At runtime, your code accesses and updates them by the viewModelInstance—permitting for real-time, declarative management while not having a State Machine.

With that in thoughts, right here’s how the brand new setup replaces the outdated input-driven logic:

import { useRive } from '@rive-app/react-canvas';
import { useEffect, useState } from 'react';

export operate ParallaxEffect({ className }: { className?: string }) {
  const [isLoaded, setIsLoaded] = useState(false);

  const { rive, setCanvasRef, setContainerRef } = useRive({
    src: 'https://cdn.rive.app/animations/hero.riv',
    autoplay: true,
    autoBind: true,
    onLoad: () => setIsLoaded(true),
  });

  useEffect(() => {
    if (!rive) return;

    const vmi = rive.viewModelInstance;
    const pointerX = vmi?.quantity('pointerX');
    const pointerY = vmi?.quantity('pointerY');

    if (!pointerX || !pointerY) return;

    const handleMouseMove = (e: MouseEvent) => {
      const { innerWidth, innerHeight } = window;
      const x = (e.clientX / innerWidth) * 100;
      const y = 100 - (e.clientY / innerHeight) * 100;
      pointerX.worth = x;
      pointerY.worth = y;
    };

    window.addEventListener('mousemove', handleMouseMove);

    return () => {
      window.removeEventListener('mousemove', handleMouseMove);
      rive.cleanup();
    };
  }, [rive]);

  return (
    <div
      ref={setContainerRef}
      className={`rive-container ${className ?? ''} ${isLoaded ? 'present' : 'conceal'}`}
    >
      <canvas ref={setCanvasRef} />
    </div>
  );
}

The Outcome

You get the identical parallax impact, however:

  • with out enter variables or mixing;
  • with out a State Machine;
  • with easy management through the ViewModel.

Official Dwell Instance from Rive

👉 CodeSandbox: Information Binding Parallax

Conclusion

Information Binding is a serious step ahead for interactive Rive animations. Results like parallax can now be arrange quicker, extra reliably, and with cleaner logic. We strongly advocate this strategy for brand new tasks.

Closing Ideas

So why did we select Rive over Lottie for this undertaking?

  • Interactivity: With Lottie, reaching the identical degree of interactivity would’ve required constructing a customized logic layer from scratch. With Rive, we obtained that conduct baked into the file—plug and play.
  • Optimization: Rive provides you extra management over every asset contained in the .riv file, and the output tends to be lighter general.

Our greatest takeaway? Don’t be afraid to experiment with new instruments—particularly once they really feel like the fitting match to your undertaking’s idea. Rive matched the playful, interactive vibe of Valley Adventures completely, and we’re excited to maintain exploring what it could possibly do.



Supply hyperlink

Related Articles

LEAVE A REPLY

Please enter your comment!
Please enter your name here

Latest Articles