数据绑定
在使用运行时数据绑定 API 之前,请先熟悉概述中介绍的核心概念。
视图模型
视图模型描述一组属性,但不能直接用于获取或设置值——这是视图模型实例的职责。
首先,我们需要获取对特定视图模型的引用。可以通过索引、名称或给定画板的默认值从 Rive 文件中获取。默认选项指的是编辑器中下拉菜单为画板分配的视图模型。
从创建的 Rive 对象在 onLoad 回调中访问视图模型:
const r = new rive.Rive({
onLoad: () => {
// Rive 对象现在已加载并可以使用。
}
});
Rive 加载后,你可以使用以下方法访问视图模型:
// 按名称获取引用
const namedVM = r.viewModelByName("My View Model");
// 按索引获取引用
for (let i = 0; i < r.viewModelCount; i++) {
const indexedVM = r.viewModelByIndex(i);
}
// 获取默认视图模型的引用
const defaultVM = r.defaultViewModel();
或者,如果你可以访问底层的 RiveFile 对象,可以在文件上访问上述方法。
const file = new RiveFile(/** ... */);
await file.init();
const vmFromFile = file.viewModelByName("My View Model");
// 或通过其他方式从 RiveFile 底层实例获取 ViewModel
const fileInstance = file.getInstance();
const namedVM = fileInstance.viewModelByName("My View Model");
const indexedVM = fileInstance.viewModelByIndex(0);
const defaultVM = fileInstance.defaultArtboardViewModel(artboard);
视图模型实例
一旦我们有了视图模型的引用,就可以用它来创建实例。创建实例时有四个选项:
-
创建空白实例 —— 使用以下默认值填充创建的实例的属性:
类型 值 Number 0 String 空字符串 Boolean False Color 0xFF000000 Trigger 未触发 Enum 第一个值 Image 无图片 Artboard 无画板 List 空列表 嵌套视图模型 Null -
创建默认实例 —— 使用编辑器中标记为「Default」的实例。通常这是设计师打算在运行时使用的主要实例。
-
按索引创建 —— 使用迭代所有可用实例时返回的顺序。在通过迭代创建多个实例时很有用。
-
按名称创建 —— 使用编辑器中的实例名称。在创建特定实例时很有用。
在某些示例中,由于「view model instance」比较冗长,我们使用缩写「VMI」,以及「VM」代表「view model」。
// 从视图模型创建空白实例 (ViewModel)
const vmiBlank = viewModel.instance();
// 从视图模型创建默认实例 (ViewModel)
const vmiDefault = viewModel.defaultInstance();
// 从视图模型按索引创建实例 (ViewModel)
for (let i = 0; i < viewModel.instanceCount; i++) {
const vmiIndexed = viewModel.instanceByIndex(i);
}
// 从视图模型按名称创建实例 (ViewModel)
const vmiNamed = viewModel.instanceByName("My Instance");
绑定
然后可以将创建的实例分配给状态机或画板。这将建立在编辑时设置的绑定关系。
最好分配给状态机,因为这将自动将实例也应用于画板。仅在不使用状态机的情况下(即文件是静态的或使用线性动画)才分配给画板。
实例的初始值不会应用于其绑定元素,直到状态机或画板推进。
const r = new rive.Rive({
autoBind: false, // 应设 置为 false(默认值)
onLoad: () => {
const vm = r.viewModelByName("My View Model");
const vmi = vm.instanceByName("My Instance");
// 通过将实例应用于状态机和画板来手动绑定
r.bindViewModelInstance(vmi);
}
});
自动绑定
或者,你可能会更倾向于使用自动绑定。这将自动使用默认实例将画板的默认视图模型绑定到状态机和画板。默认视图模型是在编辑器下拉菜单中为画板选择的视图模型。默认实例是编辑器中标记为「Default」的实例。
const r = new rive.Rive({
src: "my_rive_file.riv",
canvas: document.getElementById("canvas"),
autoBind: true,
onLoad: () => {
// 访问已自动绑定的当前实例
let boundInstance = r.viewModelInstance;
}
});
从实例获取视图模型
使用视图模型实例时,你可能希望获取实例来源视图模型的引用,以便动态创建更多相同类型的实例(例如从列表中创建实例)。为此,首先通过 .viewModelName 属性获取实例来源的视图模型名称:
const vmi = r.viewModelInstance;
const vmName = vmi.viewModelName;
然后,一旦有了名称,你可以获取视图模型本身 的引用并按需创建实例。
const vmi = r.viewModelInstance;
const mainListProp = vmi.list("todos") as ViewModelInstanceList;
const vmName = mainListProp.instanceAt(0)?.viewModelName;
const itemVmReference = r.viewModelByName(vmName);
const instanceCopies = [];
for (let i = 0; i < 10; i++) {
instanceCopies.push(itemVmReference.defaultInstance());
}
属性
属性是可以在视图模型实例上读取、设置或观察的值。属性可以是以下类型:
| 类型 | 支持 |
|---|---|
| 浮点数 | ✅ |
| 布尔值 | ✅ |
| 触发 器 | ✅ |
| 字符串 | ✅ |
| 枚举 | ✅ |
| 颜色 | ✅ |
| 嵌套视图模型 | ✅ |
| 列表 | ✅ |
| 图片 | ✅ |
| 画板 | ✅ |
关于版本兼容性,请参见功能支持页面。
列出属性
可以在视图模型上检查属性描述符,以在运行时发现可用的属性。但这些不是可变的属性本身——属性依然在实例上。这些描述符有类型和名称。
// 视图模型上的属性列表 (ViewModel)
const properties = viewModel.properties;
console.log(properties);
读取和写入属性
可以通过名称或路径检索对这些属性的引用。
某些属性是可变的,并对其值有 getter、setter 和观察者操作。获取或观察值将检索自上次状态机或画板推进以来,对该属性绑定设置的最新值。设置值将更新值和其所有绑定元素。
设置属性值后,更改不会应用于其绑定元素,直到状态机或画板推进。
const r = new rive.Rive({
autoBind: true,
onLoad: () => {
// 访问已自动绑定的当前实例
let vmi = r.viewModelInstance;
// 布尔值
const booleanProperty = vmi.boolean("My Boolean Property");
const booleanValue = booleanProperty.value;
booleanProperty.value = true;
// 字符串
const stringProperty = vmi.string("My String Property");
const stringValue = stringProperty.value;
stringProperty.value = "Hello, Rive!";
// 数字
const numberProperty = vmi.number("My Number Property");
const numberValue = numberProperty.value;
numberProperty.value = 10;
// 颜色
const colorProperty = vmi.color("My Color Property");
const colorValue = colorProperty.value;
colorProperty.value = 0xFF000000; // 设置颜色为黑色,100% 不透明度
// 设置颜色的其他方式
colorProperty.rgb(255, 0, 0); // 设置 RGB 为红色
colorProperty.rgba(255, 0, 0, 128); // 设置 RGBA 为红色,50% 不透明度
colorProperty.argb(128, 255, 0, 0); // 设置 ARGB 为红色,50% 不透明度
colorProperty.opacity(0.5); // 设置不透明度为 50%
// 触发器
const triggerProperty = vmi.trigger("My Trigger Property");
triggerProperty.trigger();
// 枚举
const enumProperty = vmi.enum("My Enum Property");
const enumValue = enumProperty.value;
enumProperty.value = "Option1";
}
});
嵌套属性路径
视图模型可以有视图模型类型的属性,允许任意嵌套。你可以在每个实例上从根开始链式调用属性,直到获取到感兴趣的属性。或者,你可以通过路径参数来做到这一点,类似于 URI,以正斜杠分隔的属性名称列表,以感兴趣的属性名称结尾。
const r = new rive.Rive({
autoBind: true,
onLoad: () => {
// 访问已自动绑定的当前实例
let vmi = r.viewModelInstance;
const nestedNumberByChain = vmi
.viewModel("My Nested View Model")
.viewModel("My Second Nested VM")
.number("My Nested Number");
const nestedNumberByPath = vmi.number("My Nested View Model/My Second Nested VM/My Nested Number");
}
});
可观察性
你可以通过使用监听器或平台等效方法观察属性值随时间的变化。一旦开始观察,当状态机推进应用属性更改时,你将收到通知,无论是显式设置的新值还是绑定更新后的值。
为属性添加观察者通过调用属性上的 on 方法完成。
public on(callback: EventCallback)
观察者可以通过调用属性上的 off 方法并传入回调函数来移除。或者, 你可以不带参数调用 off() 来移除所有观察者。
public off(callback?: EventCallback)
示例:
const r = new rive.Rive({
autoBind: true,
onLoad: () => {
// 访问已自动绑定的当前实例
let vmi = r.viewModelInstance;
const numberProperty = vmi.number("My Number Property");
// 观察
numberProperty.on((event) => {
console.log(event.data);
});
// 完成后移除所有监听器
numberProperty.off();
}
});
图片
图片属性允许你在运行时设置和替换位图图像,每个图像实例独立管理。例如,你可以构建一个头像创建器,并通过设置视图模型的图像属性动态更新特征——比如换一顶帽子。
const randomImageAsset = (imageProperty) => {
fetch("https://picsum.photos/300/500").then(async (res) => {
// 从响应中解码图像。此对象用于设置图像属性。
const image = await rive.decodeImage(
new Uint8Array(await res.arrayBuffer())
);
imageProperty.value = image;
// Rive 会自动清理此项。但最好在设置解码图像后手动释放。
// 如果你打算再次使用解码的资源,请不要调用 `unref`。
image.unref();
});
};
const r = new rive.Rive({
autoBind: true,
onLoad: () => {
// 访问已自动绑定的当前实例
let vmi = r.viewModelInstance;
// 按名称获取图像属性
var imageProperty = vmi.image("bound_image");
// 加载随机图像
randomImageAsset(imageProperty);
// 清除图像以渲染空白
imageProperty.value = null;
}
});
列表
列表属性允许你在运行时管理一组动态的视图模型实例。例如,你可以构建一个 TODO 应用,用户可以在可滚动的布局中添加和删除任务。
请参阅编辑器部分关于创建数据绑定列表的内容。
单个列表属性可以包含不同的视图模型类型,每个视图模型与其自己的组件绑定,从而轻松用各种组件实例填充列表。
使用列表属性,你可以:
- 添加新的视图模型实例(可选按索引)
- 删除现有的视图模型实例(可选按索引)
- 按索引交换两个视图模型实例
- 获取列表的大小
关于列表属性的更多信息,请参阅数据绑定列表属性编辑器文档。
const r = new rive.Rive({
autoBind: true,
onLoad: () => {
// 访问已自动绑定的当前实例
let vmi = r.viewModelInstance;
// 按名称获取列表属性
var list = vmi.list("todos");
console.log("length: ", list.length);
// 获取视图模型
var todoItemVM = r.viewModelByName("TodoItem");
// 从视图模型创建空白实例。
// 为要添加的每个新项目执行此操作。
var myTodo = todoItemVM.instance();
myTodo.string("description").value = "Buy groceries";
// 将新创建的实例添加到列表中
list.addInstance(myTodo);
// 从列表中删除特定实例
list.removeInstance(myTodo);
// 交换列表中索引 0 和 1 的两个实例
list.swap(0, 1);
// 删除索引 0 处的实例
list.removeInstanceAt(0);
}
});
画板
画板属性允许你在运行时替换整个组件。这对于创建可以在不同设计或应用间重用的模块化组件非常有用,例如:
- 创建支持大量变体的皮肤系统,如角色创建器,可以替换不同的身体部位、服装和配饰。
- 创建一个复杂场景,由从各种不同 Rive 文件加载的多个画板组成(绘制到单个 canvas/纹理/控件上)。
- 通过将单个 Rive 文件分解为更小的组件来减小其大小(复杂度),这些组件可以按需加载并按需替换。
let artboardProperty = null;
let characterArtboard = null;
function attachCharacter() {
// 如果画板属性和角色画板都存在,则设置画板
if (characterArtboard && artboardProperty) {
artboardProperty.value = characterArtboard;
}
}
const r = new Rive({
src: "swap_character_main.riv",
autoplay: true,
canvas: el,
autoBind: true,
layout: new Layout({
fit: Fit.Layout,
layoutScaleFactor: 0.5,
}),
stateMachines: "State Machine 1",
onLoad: () => {
r.resizeDrawingSurfaceToCanvas();
const vmi = r.viewModelInstance;
artboardProperty = vmi.artboard("Artboard property");
attachCharacter();
},
onLoadError: () => {
console.log("error");
},
});
// 加载外部画板
const assetsFile = new RiveFile({
src: "swap_character_assets.riv",
onLoad: () => {
characterArtboard = assetsFile.getBindableArtboard("Character 1");
attachCharacter();
},
onLoadError: () => {
console.log("error");
},
});
assetsFile.init();
在上面的示例中,我们从单独的 swap_character_assets.riv 文件加载以获取 Character 1 可绑定画板。然后我们可以通过将 artboardProperty 的值设置为加载的 characterArtboard 来在主 swap_character_main.riv 文件中使用此值。这允许我们在运行时替换主文件中使用的画板。此外,你可以通过 .viewModel setter 为该可绑定画板设置外部的 ViewModelInstance。
枚举
枚举属性分为两种:系统枚举和用户自定义枚举。实际上,你不需要担心这种区别,只需知道系统枚举在任何绑定到编辑器定义枚举集的 Rive 文件中都可用,代表编辑器下拉菜单中的选项,而用户自定义枚举是由设计师在编辑器中定义的。
枚举是字符串类型。Rive 文件包含枚举列表。每个枚举又有一个名称和一个字符串列表。
const r = new rive.Rive({
onLoad: () => {
const enums = r.enums();
console.log(enums);
}
});
示例
视频教程: