Rive — 一个用于创建和播放交互式矢量动画的工具,它结合了动画编辑器和移动平台的运行时库。与被动格式不同,Rive 支持状态机 — 根据用户操作在动画之间切换的逻辑。根据 Rive 博客(2026 年),该工具已用于超过 10,000 个项目,包括 Duolingo 和 Bumble。有关其他动画类型的更多信息,请阅读通用动画指南。
要点
Rive(前身为 Rive.app,成立于 2016 年)— 是一个用于交互式矢量动画的图形编辑器和跨平台运行时。与 After Effects + Bodymovin(Lottie)不同,Rive 提供统一的环境:设计师直接在编辑器中绘制、动画和配置切换逻辑。结果导出为 .riv 二进制文件,大小根据复杂性为 2 到 50 KB。
Rive 与其他解决方案的主要区别 — 状态机。这是一个可视化图形,其中节点是动画(空闲、悬停、按下、激活),它们之间的切换由触发器(触摸、拖动、布尔值)激活。开发者在代码中只需发送触发器,RiveRuntime 自行决定播放哪个动画以及如何平滑切换。根据 Rive Engineering(2026 年),带有状态机的平均 .riv 动画占用 15 KB — 比等效视频文件小 5–10 倍。
Rive 运行时适用于 Flutter、iOS(Swift)、Android(Kotlin/Java)、Web(WASM)和 React。渲染通过 Skia(Flutter/Web)、Core Graphics(iOS)或 Canvas(Android)完成。Rive 支持形状动画(旋钮、滑块、具有自定义几何形状的按钮),从而可以创建完全动画化的 UI 组件,而不仅仅是装饰性动画。
Rive 和 Lottie 解决不同的任务。Lottie 是 After Effects 成品动画的播放器,最适合被动装饰效果。Rive 是创建具有自己逻辑的交互式动画的工具,其中动画对用户操作做出反应。选择取决于所需的交互程度。
| 标准 | Rive | Lottie |
|---|---|---|
| 编辑器 | 自有(Rive 编辑器) | Adobe After Effects + Bodymovin |
| 交互性 | 状态机、触发器、布尔值、数字 | 仅播放/暂停/搜索 |
| 格式 | .riv(二进制) | .json 或 .lottie(protobuf) |
| 渲染 | Skia / Core Graphics / Canvas | Canvas / Core Animation / Skia |
| 动画 UI | 旋钮、滑块、按钮、输入(内置) | 无(仅装饰层) |
何时选择 Rive:具有点击响应的交互式按钮、游戏元素(具有状态的角色)、带动画的开关、响应状态的动态图标。何时选择 Lottie:预加载器、点赞、欢迎屏幕、无逻辑的重音动画。对于 Flutter 项目,由于与 Skia 和 Flutter 渲染管线的原生集成,Rive 是首选。
在 Flutter 中,RiveAnimation 是嵌入 .riv 文件的主要组件。它通过 rive 包(官方)提供。RiveAnimation 支持参数:artboard(选择画板)、animation(动画名称)、fit、alignment、controllers(用于状态机)。对于交互式场景,使用 StateMachineController。
import 'package:rive/rive.dart';
// 简单动画播放
RiveAnimation.asset(
'assets/animations/button.riv',
artboard: 'Button',
animations: const ['idle'],
fit: BoxFit.contain,
)
// 带触发器的 StateMachineController
class InteractiveButton extends StatefulWidget {
@override
State<InteractiveButton> createState() => _InteractiveButtonState();
}
class _InteractiveButtonState extends State<InteractiveButton> {
StateMachineController? _controller;
SMIInput<bool>? _isPressed;
@override
Widget build(BuildContext context) {
return GestureDetector(
onTapDown: (_) => _isPressed?.value = true,
onTapUp: (_) => _isPressed?.value = false,
child: RiveAnimation.asset(
'assets/animations/button.riv',
fit: BoxFit.contain,
onInit: (artboard) {
_controller = StateMachineController.fromArtboard(
artboard, 'State Machine 1',
);
if (_controller != null) {
artboard.addController(_controller!);
_isPressed = _controller!.findInput<bool>('isPressed');
}
},
),
);
}
}
Flutter 最佳实践:通过 RiveAnimation.asset(内置 AssetBundle)异步加载 .riv 文件。对于列表视图,使用在元素之间共享的一个 StateMachineController。Rive 不再需要单独的导入 — rive v0.12+ 包含所有功能。对于自定义动画,直接控制 RiveAnimationController — 它提供对动画、混合和速度的访问。
在 iOS 中,RiveRuntime 由 RiveView(UIKit)和 RiveViewModel(SwiftUI)类表示。库通过 Swift Package Manager 连接。RiveView 通过 Core Graphics 和 Metal 渲染 .riv 文件,在 ProMotion 显示屏上支持 120 FPS。对于状态机,使用 RiveStateMachineLayer 绑定到触发器。
import RiveRuntime
// SwiftUI:带动画的 RiveViewModel
struct RiveAnimationView: View {
@State private var isPressed = false
var body: some View {
RiveViewModel(fileName: "button").view()
.frame(width: 200, height: 60)
.gesture(
DragGesture(minimumDistance: 0)
.onChanged { _ in
if !isPressed {
isPressed = true
// 通过状态机的触发器
}
}
.onEnded { _ in
isPressed = false
}
)
}
}
// UIKit:手动控制的 RiveView
let riveView = RiveView()
riveView.configure(fileName: "loading", extension: "riv")
riveView.fit = .contain
riveView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
view.addSubview(riveView)
// 状态机管理
riveView.triggerInput(named: "startAnimation")
iOS 最佳实践:对于 SwiftUI 使用 RiveViewModel — 它自动管理生命周期。对于 UIKit,订阅 RiveViewDelegate 以跟踪事件。由于通过 Core Graphics 渲染,Rive 在 iOS 上比 Lottie 消耗更少的 CPU。对于 NavigationView/UICollectionView 中的动画,通过 RiveFile.preload() 使用预取。
在 Android 中,RiveAnimationView 是嵌入 .riv 动画的主要视图。库 app.rive:rive-android 通过 Gradle 连接。RiveAnimationView 继承自 View 并通过 Canvas 渲染,支持硬件加速。状态机通过 setTriggers 和 setInputs 管理。
// build.gradle.kts
implementation("app.rive:rive-android:0.9.0")
// XML layout
<app.rive.RiveAnimationView
android:id="@+id/riveView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:rive_file="@raw/mascot"
app:rive_artboard="Mascot"
app:rive_animation="idle" />
// Kotlin:状态机管理
import app.rive.riveandroid.RiveAnimationView
import app.rive.riveandroid.StateMachineInput
val riveView = findViewById<RiveAnimationView>(R.id.riveView)
riveView.post {
riveView.setTriggers("hover") "> 向 State Machine 发送触发器
riveView.setInput("speed", 1.5f)
riveView.setInput("isActive", true)
riveView.play()
}
// 动画完成处理
riveView.addListener(object : RiveAnimationView.RiveListener() {
override fun onAnimationCompleted(animationName: String) {
Log.d("Rive", "Animation $animationName completed")
}
})
Android 最佳实践:通过 res/raw(内置资源)或 assets(网络加载)加载 .riv 文件。RiveAnimationView 自动处理生命周期 — 不要在 onResume/onPause 中手动调用 play/pause。对于 RecyclerView 中的动画,使用 setOnScrollListener 在滚动时暂停播放。根据 Rive 文档(2026 年),高达 100 KB 的 .riv 文件在中端设备上可在 5–10 毫秒内解析。
状态机 — Rive 的关键功能,使其区别于被动动画格式。这是一个可视化图形,其中顶点是状态(动画),边是由触发器激活的切换。状态机在 Rive 编辑器中创建:设计师添加动画节点,用线连接它们,并分配触发器(触摸、布尔值、数字)。开发者只需从代码中发送这些触发器。
// Flutter:完全控制状态机
import 'package:rive/rive.dart';
class RiveStateMachineWidget extends StatefulWidget {
@override
State<RiveStateMachineWidget> createState() => _RiveStateMachineWidgetState();
}
class _RiveStateMachineWidgetState extends State<RiveStateMachineWidget> {
Artboard? _artboard;
StateMachineController? _controller;
SMIInput<bool>? _isHovered;
SMIInput<double>? _progress;
@override
Widget build(BuildContext context) {
return MouseRegion(
onEnter: (_) => _isHovered?.value = true,
onExit: (_) => _isHovered?.value = false,
child: RiveAnimation.asset(
'assets/interactive_icon.riv',
onInit: (artboard) {
_controller = StateMachineController.fromArtboard(
artboard, 'State Machine 1',
);
artboard.addController(_controller!);
_isHovered = _controller!.findInput<bool>('isHovered');
_progress = _controller!.findInput<double>('progress');
},
),
);
}
}
// 发送数值
SMIInput<double> speedInput = controller.findInput('speed');
speedInput?.value = scrollPosition;
触发器类型:布尔值 — 开关(true/false),数字 — 数值(0.0–1.0 或任意范围),触发器 — 一次性脉冲(按钮按下)。在 Rive 编辑器中可以配置切换平滑度(过渡持续时间) — 建议值为 100–300 毫秒以获得平滑效果。对于复杂的 UI 状态,使用嵌套状态机 — 动画的每个部分都独立管理。Rive 状态机在所有平台上无需更改 .riv 文件即可工作 — 切换逻辑嵌入在文件中,而不是应用程序代码中。
常见问题
Rive — 具有状态机、触发器和自有编辑器的交互式动画。Lottie — 来自 After Effects 的 JSON 动画的被动播放。Rive 适用于按钮、游戏和具有用户响应的 UI 元素。Lottie — 用于装饰效果(预加载器、点赞)。Rive 使用 .riv 格式,Lottie 使用 JSON 或 .lottie 格式。
可以。Rive 支持没有状态机的简单动画播放 — 在 RiveAnimation.asset 或 RiveView 中指定 animationName。在这种情况下,Rive 像 Lottie 一样工作:循环或一次性动画。状态机是用于交互性的可选层。对于只需要装饰性动画的项目,状态机是多余的。
是的,通过 AndroidView 将 RiveAnimationView 嵌入到 Compose 中。从 rive-android 0.9.0 开始,提供了支持修饰符的 Compose 封装 RiveAnimation compose()。对于状态机管理,使用 remember { StateMachineHolder() }。Rive 还通过共享的 Compose 运行时在 iOS 上支持 Compose Multiplatform。
是的,Rive 通过 Skia Wasm(CanvasKit)或 HTML 渲染器支持 Flutter Web。建议使用 CanvasKit 以获得最大性能。Rive 文件通过 HTTP 请求(RiveAnimation.network)加载或打包在 assets 中。带有 Rive 的 Flutter Web 在所有现代浏览器(Chrome 90+、Safari 15+、Firefox 90+)中都能正常工作。
将同时活动的图层数量限制在 20–30 个。对于简单形状,使用平面着色而不是渐变。对于带文字的动画,使用系统文字(而不是 Rive 编辑器中的矢量文字)。尽量减少角色的骨骼动画数量 — 对于 UI 元素来说不需要。在旧设备上,在 Android 上使用 riveView.enableDoubleBuffering = false 禁用双缓冲。
总结
我们将开发一款交钥匙移动应用程序
IT Sectr自2017年以来为初创企业和企业打造iOS和Android应用程序。我们将为您提供咨询并提出最佳解决方案。