rive-interactive

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Rive Interactive - State Machine-Based Vector Animation

Rive 交互式动画 - 基于状态机的矢量动画

Overview

概述

Rive is a state machine-based animation platform that enables designers to create interactive vector animations with complex logic and runtime interactivity. Unlike timeline-only animation tools (like Lottie), Rive supports state machines, input handling, and two-way data binding between application code and animations.
Key Features:
  • State machine system for complex interactive logic
  • ViewModel API for two-way data binding
  • Input handling (boolean, number, trigger inputs)
  • Custom events for animation-to-code communication
  • Runtime property control (colors, strings, numbers, enums)
  • Cross-platform support (Web, React, React Native, iOS, Android, Flutter)
  • Small file sizes with vector graphics
When to Use This Skill:
  • Creating UI animations with complex state transitions
  • Building interactive animated components (buttons, toggles, loaders)
  • Implementing game-like UI with state-driven animations
  • Binding real-time data to animated visualizations
  • Creating animations that respond to user input
  • Working with designer-created animations requiring runtime control
Alternatives:
  • Lottie (lottie-animations): For simpler timeline-based animations without state machines
  • Framer Motion (motion-framer): For code-first React animations with spring physics
  • GSAP (gsap-scrolltrigger): For timeline-based web animations with precise control
Rive是一个基于状态机的动画平台,设计师可借助它创建带有复杂逻辑和运行时交互的交互式矢量动画。与仅支持时间轴的动画工具(如Lottie)不同,Rive支持状态机、输入处理,以及应用代码与动画之间的双向数据绑定。
核心特性:
  • 用于复杂交互逻辑的状态机系统
  • 支持双向数据绑定的ViewModel API
  • 输入处理(布尔值、数值、触发型输入)
  • 用于动画与代码通信的自定义事件
  • 运行时属性控制(颜色、字符串、数值、枚举)
  • 跨平台支持(Web、React、React Native、iOS、Android、Flutter)
  • 基于矢量图形的小文件体积
适用场景:
  • 创建带有复杂状态过渡的UI动画
  • 构建交互式动画组件(按钮、开关、加载器)
  • 实现带有状态驱动动画的类游戏UI
  • 将实时数据绑定到动画可视化效果
  • 创建响应用户输入的动画
  • 处理需要运行时控制的设计师创作动画
替代方案:
  • Lottie (lottie-animations): 适用于无需状态机的简单时间轴动画
  • Framer Motion (motion-framer): 适用于代码优先的React弹簧物理动画
  • GSAP (gsap-scrolltrigger): 适用于需要精确控制的时间轴Web动画

Core Concepts

核心概念

1. State Machines

1. 状态机

State machines define animation behavior with states and transitions:
  • States: Different animation states (e.g., idle, hover, pressed)
  • Inputs: Variables that control transitions (boolean, number, trigger)
  • Transitions: Rules for moving between states
  • Listeners: React hooks to respond to state changes
状态机通过状态与过渡定义动画行为:
  • 状态: 不同的动画状态(如空闲、悬停、按下)
  • 输入: 控制状态过渡的变量(布尔值、数值、触发型)
  • 过渡: 状态间的切换规则
  • 监听器: 响应状态变化的React钩子

2. Inputs

2. 输入

Three input types control state machine behavior:
  • Boolean: On/off states (e.g., isHovered, isActive)
  • Number: Numeric values (e.g., progress, volume)
  • Trigger: One-time events (e.g., click, submit)
三种输入类型用于控制状态机行为:
  • 布尔值: 开关状态(如isHovered、isActive)
  • 数值: 数字值(如进度、音量)
  • 触发型: 一次性事件(如点击、提交)

3. ViewModels

3. ViewModel

Data binding system for dynamic properties:
  • String Properties: Text content (e.g., username, title)
  • Number Properties: Numeric data (e.g., stock price, score)
  • Color Properties: Dynamic colors (hex values)
  • Enum Properties: Selection from predefined options
  • Trigger Properties: Animation events
用于动态属性的数据绑定系统:
  • 字符串属性: 文本内容(如用户名、标题)
  • 数值属性: 数字数据(如股票价格、分数)
  • 颜色属性: 动态颜色(十六进制值)
  • 枚举属性: 从预定义选项中选择
  • 触发型属性: 动画事件

4. Events

4. 事件

Custom events emitted from animations:
  • General Events: Custom named events
  • Event Properties: Data attached to events
  • Event Listeners: React hooks to handle events
动画触发的自定义事件:
  • 通用事件: 自定义命名事件
  • 事件属性: 附加到事件的数据
  • 事件监听器: 处理事件的React钩子

Common Patterns

常见模式

Pattern 1: Basic Rive Animation

模式1: 基础Rive动画

Use Case: Display a simple Rive animation in React
Implementation:
bash
undefined
使用场景: 在React中展示简单Rive动画
实现代码:
bash
undefined

Installation

安装

npm install rive-react

```jsx
import Rive from 'rive-react';

export default function SimpleAnimation() {
  return (
    <Rive
      src="animation.riv"
      artboard="Main"
      animations="idle"
      layout={{ fit: "contain", alignment: "center" }}
      style={{ width: '400px', height: '400px' }}
    />
  );
}
Key Points:
  • src
    : Path to .riv file
  • artboard
    : Which artboard to display
  • animations
    : Which animation timeline to play
  • layout
    : How animation fits in container
npm install rive-react

```jsx
import Rive from 'rive-react';

export default function SimpleAnimation() {
  return (
    <Rive
      src="animation.riv"
      artboard="Main"
      animations="idle"
      layout={{ fit: "contain", alignment: "center" }}
      style={{ width: '400px', height: '400px' }}
    />
  );
}
关键点:
  • src
    : .riv文件路径
  • artboard
    : 要展示的画板
  • animations
    : 要播放的动画时间轴
  • layout
    : 动画在容器中的适配方式

Pattern 2: State Machine Control with Inputs

模式2: 通过输入控制状态机

Use Case: Control animation states based on user interaction
Implementation:
jsx
import { useRive, useStateMachineInput } from 'rive-react';

export default function InteractiveButton() {
  const { rive, RiveComponent } = useRive({
    src: 'button.riv',
    stateMachines: 'Button State Machine',
    autoplay: true,
  });

  // Get state machine inputs
  const hoverInput = useStateMachineInput(
    rive,
    'Button State Machine',
    'isHovered',
    false
  );

  const clickInput = useStateMachineInput(
    rive,
    'Button State Machine',
    'isClicked',
    false
  );

  return (
    <div
      onMouseEnter={() => hoverInput && (hoverInput.value = true)}
      onMouseLeave={() => hoverInput && (hoverInput.value = false)}
      onClick={() => clickInput && clickInput.fire()} // Trigger input
      style={{ cursor: 'pointer' }}
    >
      <RiveComponent style={{ width: '200px', height: '100px' }} />
    </div>
  );
}
Input Types:
  • Boolean:
    input.value = true/false
  • Number:
    input.value = 50
  • Trigger:
    input.fire()
使用场景: 根据用户交互控制动画状态
实现代码:
jsx
import { useRive, useStateMachineInput } from 'rive-react';

export default function InteractiveButton() {
  const { rive, RiveComponent } = useRive({
    src: 'button.riv',
    stateMachines: 'Button State Machine',
    autoplay: true,
  });

  // 获取状态机输入
  const hoverInput = useStateMachineInput(
    rive,
    'Button State Machine',
    'isHovered',
    false
  );

  const clickInput = useStateMachineInput(
    rive,
    'Button State Machine',
    'isClicked',
    false
  );

  return (
    <div
      onMouseEnter={() => hoverInput && (hoverInput.value = true)}
      onMouseLeave={() => hoverInput && (hoverInput.value = false)}
      onClick={() => clickInput && clickInput.fire()} // 触发输入
      style={{ cursor: 'pointer' }}
    >
      <RiveComponent style={{ width: '200px', height: '100px' }} />
    </div>
  );
}
输入类型用法:
  • 布尔值:
    input.value = true/false
  • 数值:
    input.value = 50
  • 触发型:
    input.fire()

Pattern 3: ViewModel Data Binding

模式3: ViewModel数据绑定

Use Case: Bind application data to animation properties
Implementation:
jsx
import { useRive, useViewModel, useViewModelInstance,
         useViewModelInstanceString, useViewModelInstanceNumber } from 'rive-react';
import { useEffect, useState } from 'react';

export default function Dashboard() {
  const [stockPrice, setStockPrice] = useState(150.0);

  const { rive, RiveComponent } = useRive({
    src: 'dashboard.riv',
    autoplay: true,
    autoBind: false, // Manual binding for ViewModels
  });

  // Get ViewModel and instance
  const viewModel = useViewModel(rive, { name: 'Dashboard' });
  const viewModelInstance = useViewModelInstance(viewModel, { rive });

  // Bind properties
  const { setValue: setTitle } = useViewModelInstanceString(
    'title',
    viewModelInstance
  );

  const { setValue: setPrice } = useViewModelInstanceNumber(
    'stockPrice',
    viewModelInstance
  );

  useEffect(() => {
    if (setTitle) setTitle('Stock Dashboard');
  }, [setTitle]);

  useEffect(() => {
    if (setPrice) setPrice(stockPrice);
  }, [setPrice, stockPrice]);

  // Simulate real-time updates
  useEffect(() => {
    const interval = setInterval(() => {
      setStockPrice((prev) => prev + (Math.random() - 0.5) * 10);
    }, 1000);

    return () => clearInterval(interval);
  }, []);

  return <RiveComponent style={{ width: '800px', height: '600px' }} />;
}
ViewModel Property Hooks:
  • useViewModelInstanceString
    - Text properties
  • useViewModelInstanceNumber
    - Numeric properties
  • useViewModelInstanceColor
    - Color properties (hex)
  • useViewModelInstanceEnum
    - Enum selection
  • useViewModelInstanceTrigger
    - Animation triggers
使用场景: 将应用数据绑定到动画属性
实现代码:
jsx
import { useRive, useViewModel, useViewModelInstance,
         useViewModelInstanceString, useViewModelInstanceNumber } from 'rive-react';
import { useEffect, useState } from 'react';

export default function Dashboard() {
  const [stockPrice, setStockPrice] = useState(150.0);

  const { rive, RiveComponent } = useRive({
    src: 'dashboard.riv',
    autoplay: true,
    autoBind: false, // 手动绑定ViewModel
  });

  // 获取ViewModel及实例
  const viewModel = useViewModel(rive, { name: 'Dashboard' });
  const viewModelInstance = useViewModelInstance(viewModel, { rive });

  // 绑定属性
  const { setValue: setTitle } = useViewModelInstanceString(
    'title',
    viewModelInstance
  );

  const { setValue: setPrice } = useViewModelInstanceNumber(
    'stockPrice',
    viewModelInstance
  );

  useEffect(() => {
    if (setTitle) setTitle('Stock Dashboard');
  }, [setTitle]);

  useEffect(() => {
    if (setPrice) setPrice(stockPrice);
  }, [setPrice, stockPrice]);

  // 模拟实时更新
  useEffect(() => {
    const interval = setInterval(() => {
      setStockPrice((prev) => prev + (Math.random() - 0.5) * 10);
    }, 1000);

    return () => clearInterval(interval);
  }, []);

  return <RiveComponent style={{ width: '800px', height: '600px' }} />;
}
ViewModel属性钩子:
  • useViewModelInstanceString
    - 文本属性
  • useViewModelInstanceNumber
    - 数值属性
  • useViewModelInstanceColor
    - 颜色属性(十六进制)
  • useViewModelInstanceEnum
    - 枚举选择
  • useViewModelInstanceTrigger
    - 动画触发

Pattern 4: Handling Rive Events

模式4: 处理Rive事件

Use Case: React to events emitted from Rive animation
Implementation:
jsx
import { useRive, EventType, RiveEventType } from 'rive-react';
import { useEffect } from 'react';

export default function InteractiveRating() {
  const { rive, RiveComponent } = useRive({
    src: 'rating.riv',
    stateMachines: 'State Machine 1',
    autoplay: true,
    automaticallyHandleEvents: true,
  });

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

    const onRiveEvent = (event) => {
      const eventData = event.data;

      if (eventData.type === RiveEventType.General) {
        console.log('Event:', eventData.name);

        // Access event properties
        const rating = eventData.properties.rating;
        const message = eventData.properties.message;

        if (rating >= 4) {
          alert(`Thanks for ${rating} stars: ${message}`);
        }
      }
    };

    rive.on(EventType.RiveEvent, onRiveEvent);

    return () => {
      rive.off(EventType.RiveEvent, onRiveEvent);
    };
  }, [rive]);

  return <RiveComponent style={{ width: '400px', height: '300px' }} />;
}
使用场景: 响应Rive动画触发的事件
实现代码:
jsx
import { useRive, EventType, RiveEventType } from 'rive-react';
import { useEffect } from 'react';

export default function InteractiveRating() {
  const { rive, RiveComponent } = useRive({
    src: 'rating.riv',
    stateMachines: 'State Machine 1',
    autoplay: true,
    automaticallyHandleEvents: true,
  });

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

    const onRiveEvent = (event) => {
      const eventData = event.data;

      if (eventData.type === RiveEventType.General) {
        console.log('Event:', eventData.name);

        // 访问事件属性
        const rating = eventData.properties.rating;
        const message = eventData.properties.message;

        if (rating >= 4) {
          alert(`Thanks for ${rating} stars: ${message}`);
        }
      }
    };

    rive.on(EventType.RiveEvent, onRiveEvent);

    return () => {
      rive.off(EventType.RiveEvent, onRiveEvent);
    };
  }, [rive]);

  return <RiveComponent style={{ width: '400px', height: '300px' }} />;
}

Pattern 5: Preloading Rive Files

模式5: 预加载Rive文件

Use Case: Optimize load times by preloading animations
Implementation:
jsx
import { useRiveFile, useRive } from 'rive-react';

export default function PreloadedAnimation() {
  const { riveFile, status } = useRiveFile({
    src: 'large-animation.riv',
  });

  const { RiveComponent } = useRive({
    riveFile: riveFile,
    artboard: 'Main',
    autoplay: true,
  });

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

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

  return <RiveComponent style={{ width: '600px', height: '400px' }} />;
}
使用场景: 通过预加载动画优化加载时间
实现代码:
jsx
import { useRiveFile, useRive } from 'rive-react';

export default function PreloadedAnimation() {
  const { riveFile, status } = useRiveFile({
    src: 'large-animation.riv',
  });

  const { RiveComponent } = useRive({
    riveFile: riveFile,
    artboard: 'Main',
    autoplay: true,
  });

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

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

  return <RiveComponent style={{ width: '600px', height: '400px' }} />;
}

Pattern 6: Controlled Animation with Refs

模式6: 用Ref控制动画

Use Case: Control animation from parent component
Implementation:
jsx
import { useRive, useViewModel, useViewModelInstance,
         useViewModelInstanceTrigger } from 'rive-react';
import { useImperativeHandle, forwardRef } from 'react';

const AnimatedComponent = forwardRef((props, ref) => {
  const { rive, RiveComponent } = useRive({
    src: 'logo.riv',
    autoplay: true,
    autoBind: false,
  });

  const viewModel = useViewModel(rive, { useDefault: true });
  const viewModelInstance = useViewModelInstance(viewModel, { rive });

  const { trigger: spinTrigger } = useViewModelInstanceTrigger(
    'triggerSpin',
    viewModelInstance
  );

  // Expose methods to parent
  useImperativeHandle(ref, () => ({
    spin: () => spinTrigger && spinTrigger(),
    pause: () => rive && rive.pause(),
    play: () => rive && rive.play(),
  }));

  return <RiveComponent style={{ width: '200px', height: '200px' }} />;
});

export default function App() {
  const animationRef = useRef();

  return (
    <div>
      <AnimatedComponent ref={animationRef} />
      <button onClick={() => animationRef.current?.spin()}>Spin</button>
      <button onClick={() => animationRef.current?.pause()}>Pause</button>
    </div>
  );
}
使用场景: 从父组件控制动画
实现代码:
jsx
import { useRive, useViewModel, useViewModelInstance,
         useViewModelInstanceTrigger } from 'rive-react';
import { useImperativeHandle, forwardRef } from 'react';

const AnimatedComponent = forwardRef((props, ref) => {
  const { rive, RiveComponent } = useRive({
    src: 'logo.riv',
    autoplay: true,
    autoBind: false,
  });

  const viewModel = useViewModel(rive, { useDefault: true });
  const viewModelInstance = useViewModelInstance(viewModel, { rive });

  const { trigger: spinTrigger } = useViewModelInstanceTrigger(
    'triggerSpin',
    viewModelInstance
  );

  // 向父组件暴露方法
  useImperativeHandle(ref, () => ({
    spin: () => spinTrigger && spinTrigger(),
    pause: () => rive && rive.pause(),
    play: () => rive && rive.play(),
  }));

  return <RiveComponent style={{ width: '200px', height: '200px' }} />;
});

export default function App() {
  const animationRef = useRef();

  return (
    <div>
      <AnimatedComponent ref={animationRef} />
      <button onClick={() => animationRef.current?.spin()}>Spin</button>
      <button onClick={() => animationRef.current?.pause()}>Pause</button>
    </div>
  );
}

Pattern 7: Multi-Property ViewModel Updates

模式7: 多属性ViewModel更新

Use Case: Update multiple animation properties from complex data
Implementation:
jsx
import { useRive, useViewModel, useViewModelInstance,
         useViewModelInstanceString, useViewModelInstanceNumber,
         useViewModelInstanceColor } from 'rive-react';
import { useEffect } from 'react';

export default function UserProfile({ user }) {
  const { rive, RiveComponent } = useRive({
    src: 'profile.riv',
    autoplay: true,
    autoBind: false,
  });

  const viewModel = useViewModel(rive, { useDefault: true });
  const viewModelInstance = useViewModelInstance(viewModel, { rive });

  // Bind all properties
  const { setValue: setName } = useViewModelInstanceString('name', viewModelInstance);
  const { setValue: setScore } = useViewModelInstanceNumber('score', viewModelInstance);
  const { setValue: setColor } = useViewModelInstanceColor('avatarColor', viewModelInstance);

  useEffect(() => {
    if (user && setName && setScore && setColor) {
      setName(user.name);
      setScore(user.score);
      setColor(parseInt(user.color.substring(1), 16)); // Convert hex to number
    }
  }, [user, setName, setScore, setColor]);

  return <RiveComponent style={{ width: '300px', height: '300px' }} />;
}
使用场景: 从复杂数据更新多个动画属性
实现代码:
jsx
import { useRive, useViewModel, useViewModelInstance,
         useViewModelInstanceString, useViewModelInstanceNumber,
         useViewModelInstanceColor } from 'rive-react';
import { useEffect } from 'react';

export default function UserProfile({ user }) {
  const { rive, RiveComponent } = useRive({
    src: 'profile.riv',
    autoplay: true,
    autoBind: false,
  });

  const viewModel = useViewModel(rive, { useDefault: true });
  const viewModelInstance = useViewModelInstance(viewModel, { rive });

  // 绑定所有属性
  const { setValue: setName } = useViewModelInstanceString('name', viewModelInstance);
  const { setValue: setScore } = useViewModelInstanceNumber('score', viewModelInstance);
  const { setValue: setColor } = useViewModelInstanceColor('avatarColor', viewModelInstance);

  useEffect(() => {
    if (user && setName && setScore && setColor) {
      setName(user.name);
      setScore(user.score);
      setColor(parseInt(user.color.substring(1), 16)); // 将十六进制转换为数字
    }
  }, [user, setName, setScore, setColor]);

  return <RiveComponent style={{ width: '300px', height: '300px' }} />;
}

Integration Patterns

集成模式

With Framer Motion (motion-framer)

与Framer Motion (motion-framer)集成

Animate container while Rive handles interactive content:
jsx
import { motion } from 'framer-motion';
import Rive from 'rive-react';

export default function AnimatedCard() {
  return (
    <motion.div
      initial={{ opacity: 0, y: 20 }}
      animate={{ opacity: 1, y: 0 }}
      whileHover={{ scale: 1.05 }}
    >
      <Rive
        src="card.riv"
        stateMachines="Card State Machine"
        style={{ width: '300px', height: '400px' }}
      />
    </motion.div>
  );
}
用Framer Motion动画容器,Rive处理交互式内容:
jsx
import { motion } from 'framer-motion';
import Rive from 'rive-react';

export default function AnimatedCard() {
  return (
    <motion.div
      initial={{ opacity: 0, y: 20 }}
      animate={{ opacity: 1, y: 0 }}
      whileHover={{ scale: 1.05 }}
    >
      <Rive
        src="card.riv"
        stateMachines="Card State Machine"
        style={{ width: '300px', height: '400px' }}
      />
    </motion.div>
  );
}

With GSAP ScrollTrigger (gsap-scrolltrigger)

与GSAP ScrollTrigger (gsap-scrolltrigger)集成

Trigger Rive animations on scroll:
jsx
import { useRive, useStateMachineInput } from 'rive-react';
import { useEffect, useRef } from 'react';
import gsap from 'gsap';
import ScrollTrigger from 'gsap/ScrollTrigger';

gsap.registerPlugin(ScrollTrigger);

export default function ScrollRive() {
  const containerRef = useRef();
  const { rive, RiveComponent } = useRive({
    src: 'scroll-animation.riv',
    stateMachines: 'State Machine 1',
    autoplay: true,
  });

  const trigger = useStateMachineInput(rive, 'State Machine 1', 'trigger');

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

    ScrollTrigger.create({
      trigger: containerRef.current,
      start: 'top center',
      onEnter: () => trigger.fire(),
    });
  }, [trigger]);

  return (
    <div ref={containerRef}>
      <RiveComponent style={{ width: '100%', height: '600px' }} />
    </div>
  );
}
滚动时触发Rive动画:
jsx
import { useRive, useStateMachineInput } from 'rive-react';
import { useEffect, useRef } from 'react';
import gsap from 'gsap';
import ScrollTrigger from 'gsap/ScrollTrigger';

gsap.registerPlugin(ScrollTrigger);

export default function ScrollRive() {
  const containerRef = useRef();
  const { rive, RiveComponent } = useRive({
    src: 'scroll-animation.riv',
    stateMachines: 'State Machine 1',
    autoplay: true,
  });

  const trigger = useStateMachineInput(rive, 'State Machine 1', 'trigger');

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

    ScrollTrigger.create({
      trigger: containerRef.current,
      start: 'top center',
      onEnter: () => trigger.fire(),
    });
  }, [trigger]);

  return (
    <div ref={containerRef}>
      <RiveComponent style={{ width: '100%', height: '600px' }} />
    </div>
  );
}

Performance Optimization

性能优化

1. Use Off-Screen Renderer

1. 使用离屏渲染器

jsx
<Rive
  src="animation.riv"
  useOffscreenRenderer={true} // Better performance
/>
jsx
<Rive
  src="animation.riv"
  useOffscreenRenderer={true} // 提升性能
/>

2. Optimize Rive Files

2. 优化Rive文件

In Rive Editor:
  • Keep artboards under 2MB
  • Use vector graphics (avoid raster images when possible)
  • Minimize number of bones in skeletal animations
  • Reduce complexity of state machines
在Rive编辑器中:
  • 保持画板文件小于2MB
  • 使用矢量图形(尽可能避免光栅图像)
  • 减少骨骼动画中的骨骼数量
  • 降低状态机复杂度

3. Preload Critical Animations

3. 预加载关键动画

jsx
const { riveFile } = useRiveFile({ src: 'critical.riv' });
// Preload during app initialization
jsx
const { riveFile } = useRiveFile({ src: 'critical.riv' });
// 在应用初始化时预加载

4. Disable Automatic Event Handling

4. 禁用自动事件处理

jsx
<Rive
  src="animation.riv"
  automaticallyHandleEvents={false} // Manual control
/>
jsx
<Rive
  src="animation.riv"
  automaticallyHandleEvents={false} // 手动控制
/>

Common Pitfalls and Solutions

常见问题与解决方案

Pitfall 1: State Machine Input Not Found

问题1: 找不到状态机输入

Problem:
useStateMachineInput
returns null
Solution:
jsx
// ❌ Wrong: Incorrect input name
const input = useStateMachineInput(rive, 'State Machine', 'wrongName');

// ✅ Correct: Match exact name from Rive editor
const input = useStateMachineInput(rive, 'State Machine', 'isHovered');

// Always check if input exists before using
if (input) {
  input.value = true;
}
问题:
useStateMachineInput
返回null
解决方案:
jsx
// ❌ 错误: 输入名称不正确
const input = useStateMachineInput(rive, 'State Machine', 'wrongName');

// ✅ 正确: 与Rive编辑器中的名称完全匹配
const input = useStateMachineInput(rive, 'State Machine', 'isHovered');

// 使用前务必检查输入是否存在
if (input) {
  input.value = true;
}

Pitfall 2: ViewModel Property Not Updating

问题2: ViewModel属性未更新

Problem: ViewModel property doesn't update animation
Solution:
jsx
// ❌ Wrong: autoBind enabled
const { rive } = useRive({
  src: 'dashboard.riv',
  autoplay: true,
  // autoBind: true (default)
});

// ✅ Correct: Disable autoBind for ViewModels
const { rive } = useRive({
  src: 'dashboard.riv',
  autoplay: true,
  autoBind: false, // Required for manual ViewModel control
});
问题: ViewModel属性未同步到动画
解决方案:
jsx
// ❌ 错误: 启用了autoBind
const { rive } = useRive({
  src: 'dashboard.riv',
  autoplay: true,
  // autoBind: true (默认值)
});

// ✅ 正确: 禁用autoBind以手动控制ViewModel
const { rive } = useRive({
  src: 'dashboard.riv',
  autoplay: true,
  autoBind: false, // 手动ViewModel控制必填
});

Pitfall 3: Event Listener Not Firing

问题3: 事件监听器未触发

Problem: Rive events not triggering callback
Solution:
jsx
// ❌ Wrong: Missing automaticallyHandleEvents
const { rive } = useRive({
  src: 'rating.riv',
  stateMachines: 'State Machine 1',
  autoplay: true,
});

// ✅ Correct: Enable event handling
const { rive } = useRive({
  src: 'rating.riv',
  stateMachines: 'State Machine 1',
  autoplay: true,
  automaticallyHandleEvents: true, // Required for events
});
问题: Rive事件未回调
解决方案:
jsx
// ❌ 错误: 缺少automaticallyHandleEvents配置
const { rive } = useRive({
  src: 'rating.riv',
  stateMachines: 'State Machine 1',
  autoplay: true,
});

// ✅ 正确: 启用事件处理
const { rive } = useRive({
  src: 'rating.riv',
  stateMachines: 'State Machine 1',
  autoplay: true,
  automaticallyHandleEvents: true, // 事件处理必填
});

Resources

资源

Official Documentation

官方文档

Rive Editor

Rive编辑器

Learning Resources

学习资源

Related Skills

相关技能

  • lottie-animations: For simpler timeline-based animations without state machines
  • motion-framer: For code-first React animations with gestures
  • gsap-scrolltrigger: For scroll-driven animations
  • spline-interactive: For 3D interactive animations
  • lottie-animations: 适用于无需状态机的简单时间轴动画
  • motion-framer: 适用于带手势的代码优先React动画
  • gsap-scrolltrigger: 适用于滚动驱动动画
  • spline-interactive: 适用于3D交互式动画

Scripts

脚本

This skill includes utility scripts:
  • component_generator.py
    - Generate Rive React component boilerplate
  • viewmodel_builder.py
    - Build ViewModel property bindings
Run scripts from the skill directory:
bash
./scripts/component_generator.py
./scripts/viewmodel_builder.py
该技能包含实用脚本:
  • component_generator.py
    - 生成Rive React组件模板
  • viewmodel_builder.py
    - 构建ViewModel属性绑定
从技能目录运行脚本:
bash
./scripts/component_generator.py
./scripts/viewmodel_builder.py

Assets

资源文件

Starter templates and examples:
  • starter_rive/
    - Complete React + Rive template
  • examples/
    - Real-world integration patterns
入门模板与示例:
  • starter_rive/
    - 完整的React + Rive模板
  • examples/
    - 真实场景集成模式