rive-interactive
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseRive 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
undefinedInstallation
安装
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:
- : Path to .riv file
src - : Which artboard to display
artboard - : Which animation timeline to play
animations - : How animation fits in container
layout
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' }}
/>
);
}关键点:
- : .riv文件路径
src - : 要展示的画板
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:
- - Text properties
useViewModelInstanceString - - Numeric properties
useViewModelInstanceNumber - - Color properties (hex)
useViewModelInstanceColor - - Enum selection
useViewModelInstanceEnum - - Animation triggers
useViewModelInstanceTrigger
使用场景: 将应用数据绑定到动画属性
实现代码:
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 initializationjsx
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: returns null
useStateMachineInputSolution:
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;
}问题: 返回null
useStateMachineInput解决方案:
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 Docs: https://rive.app/docs
- React Rive GitHub: https://github.com/rive-app/rive-react
- Rive Community: https://rive.app/community
- Rive文档: https://rive.app/docs
- React Rive GitHub: https://github.com/rive-app/rive-react
- Rive社区: https://rive.app/community
Rive Editor
Rive编辑器
- Web Editor: https://rive.app/community
- Desktop App: Available for macOS, Windows
- Web编辑器: https://rive.app/community
- 桌面应用: 支持macOS、Windows
Learning Resources
学习资源
- Tutorials: https://rive.app/learn
- Examples: https://rive.app/community/files
- State Machine Guide: https://rive.app/docs/state-machine
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:
- - Generate Rive React component boilerplate
component_generator.py - - Build ViewModel property bindings
viewmodel_builder.py
Run scripts from the skill directory:
bash
./scripts/component_generator.py
./scripts/viewmodel_builder.py该技能包含实用脚本:
- - 生成Rive React组件模板
component_generator.py - - 构建ViewModel属性绑定
viewmodel_builder.py
从技能目录运行脚本:
bash
./scripts/component_generator.py
./scripts/viewmodel_builder.pyAssets
资源文件
Starter templates and examples:
- - Complete React + Rive template
starter_rive/ - - Real-world integration patterns
examples/
入门模板与示例:
- - 完整的React + Rive模板
starter_rive/ - - 真实场景集成模式
examples/