快速开始
本页的目标是让你在一个 ArkUI 页面里完成一次真正可用的体素预览:能加载 JSON/VFP、可响应触控,且页面退出时不会留下场景或异步任务。若你还不了解这个组件负责什么,先读 欢迎与介绍。
开始前检查
| 条件 | 为什么需要 |
|---|---|
| HarmonyOS API 23 / 6.1.0 或兼容环境 | VoxelViewer 使用 ArkUI/ArkGraphics 3D 能力。 |
设备具备 SystemCapability.ArkUi.Graphics3D | 没有该能力无法在页面上呈现 Component3D。 |
| 在真机或支持 ArkGraphics 的模拟器测试 | DevEco 的设计预览不能证明 3D 场景、手势或材质工作正常。 |
| 一份 JSON 或 VFP 样本 | JSON 需符合本页最小合同;VFP 支持 1.2/1.3。 |
- 已经有 JSON 字符串:
loadText()。 - 已经拿到 rawfile/网络缓存的完整字节:
loadBytes()。 - 来自系统文件选择器:
loadUri(),这是大 VFP 的优先方式。
1. 安装
推荐通过字符科技 OHPM 仓安装。打开 DevEco Studio 的 Terminal,进入宿主工程根目录后执行:
ohpm config set registry http://ohpm.charactech.cn/repos/ohpm/
ohpm install voxel-kit
第一条命令将 OHPM 下载源设为字符科技仓;第二条命令会安装当前仓中的 voxel-kit,并更新工程的 oh-package.json5 与 lock 文件。可在 OHPM 的 voxel-kit 页面 查看包信息与版本。
若团队只希望对单个工程使用该源,可在工程根目录新增或修改 .ohpmrc:
registry=http://ohpm.charactech.cn/repos/ohpm/
随后仍在工程根目录执行:
ohpm install voxel-kit
不要手动把本地 HAR 路径作为常规安装方式。以下写法只用于仓库开发或未发布 HAR 的本地验证:
{
"dependencies": {
"voxel-kit": "file:./libs/voxelKit.har"
}
}
业务代码始终从包根导入:
import { VoxelViewer, VoxelViewerController, VoxelViewerOptions } from 'voxel-kit';
不要从 voxel-kit/src/... 或任何 internal 路径导入。它们不是版本兼容契约。
仓库内开发也可使用:
{
"dependencies": {
"voxel-kit": "file:../voxel-preview"
}
}
2. 第一个 JSON 预览
import {
VoxelLoadAnimationMode,
VoxelViewer,
VoxelViewerController,
VoxelViewerOptions
} from 'voxel-kit';
@Entry
@Component
struct HousePreviewPage {
private readonly options: VoxelViewerOptions = new VoxelViewerOptions();
private readonly controller: VoxelViewerController = new VoxelViewerController(this.options);
aboutToAppear(): void {
// 在首次 load* 前设置。该图片由 HAR 依赖携带,宿主负责解析 Resource。
this.options.unitGridTexture = $rawfile('voxel/voxel_grid.png');
this.options.animationMode = VoxelLoadAnimationMode.STACK;
this.options.loadAnimationDurationMs = 1500;
this.options.showGroundGrid = true;
this.controller.loadText(this.readModelText(), 'house.json')
.then((result) => {
console.info(`loaded=${result.voxelCount}, faces=${result.surfaceCount}`);
})
.catch((error: Error) => {
console.error('Voxel import failed: ' + error.message);
});
}
build() {
VoxelViewer({ controller: this.controller })
.width('100%')
.height(520)
}
aboutToDisappear(): void {
this.controller.dispose();
}
private readModelText(): string {
return '';
}
}
不要在 build() 中 new VoxelViewerController(),也不要每次状态更新时创建新的 Options/Controller 对。Controller 持有可变场景引用和内置刷新回调,必须保持稳定。
这段示例中最容易遗漏的是 unitGridTexture:它并不是要求业务自己实现描边,而是让静态 HAR 在 ArkGraphics 中取得由宿主解析好的 PBR 网格纹理。未设置时模型仍能载入,但大模型可能先显示主体,随后走兼容描边路径。
2.1 读取成功与失败
三个加载方法都返回同一种 Promise<VoxelLoadResult>。把成功与失败放在同一个业务入口处理,避免在回调里又开始一次导入:
private async openJson(text: string): Promise<void> {
try {
const result = await this.controller.loadText(text, 'house.json');
console.info(`已载入 ${result.voxelCount} 体素,合并表面 ${result.surfaceCount}`);
// result.sourceFormat 为 JSON;VFP 成功时为 VFP。
} catch (error) {
// error 是 Error,message 与 getLastError() 一致。
console.error('导入失败:' + error.message);
}
}
getStatus() 返回给用户看的中文说明,不应被业务当作稳定枚举;若你要展示自己的 Loading,使用 VoxelLoadOverlayMode.EXTERNAL 与 onLoadOverlayStateChanged,详见 接入与生命周期。
3. 从 rawfile 载入 VFP
const content: Uint8Array = await this.getUIContext().getHostContext()
.resourceManager.getRawFileContent('voxel/house.vfp');
const copy = new Uint8Array(content.length);
copy.set(content);
await this.controller.loadBytes(copy.buffer, 'house.vfp');
loadBytes() 的前 4 个字节为 VFPK 时自动走 VFP;否则按 UTF-8 JSON 处理。VFP 数据没有 JSON 15 MiB 上限,但完整 ArrayBuffer 会占用宿主内存;大型文件优先使用 Picker URI。
如果该 rawfile 是 JSON,也可以用相同的 loadBytes();由组件自动识别即可。只有你已拥有文本并且希望更明确地约束 JSON 时,才使用 loadText()。
4. 从系统 Picker 导入
import { picker } from '@kit.CoreFileKit';
private async importModel(): Promise<void> {
const documentPicker = new picker.DocumentViewPicker(this.getUIContext().getHostContext());
const options = new picker.DocumentSelectOptions();
options.fileSuffixFilters = [
'Voxel JSON|.json',
'Voxel Format Package|.vfp'
];
options.maxSelectNumber = 1;
const uris = await documentPicker.select(options);
if (uris.length > 0) {
await this.controller.loadUri(uris[0]);
}
}
不要将 Picker URI 视为普通 POSIX 文件路径,也不要在 loadUri() 之外再通过 fileIo 打开它。HAR 会在 URI 授权有效期内完成打开、读取、解析和关闭;VFP 1.3 会选择性读取预览所需区段。
4.1 一个完整的导入按钮模式
Picker 取消并不属于模型加载错误,建议把它与真实 I/O/格式错误分开:
private async importModel(): Promise<void> {
const documentPicker = new picker.DocumentViewPicker(this.getUIContext().getHostContext());
const options = new picker.DocumentSelectOptions();
options.fileSuffixFilters = ['Voxel JSON|.json', 'Voxel Format Package|.vfp'];
options.maxSelectNumber = 1;
const uris = await documentPicker.select(options);
if (uris.length === 0) {
return; // 用户取消
}
try {
await this.controller.loadUri(uris[0]);
} catch (error) {
// 显示 error.message 或让内置 Overlay 显示失败状态。
console.error(error.message);
}
}
同一 Controller 上要等这次 await 结束后,才允许用户开始下一次导入。若 UI 需要禁用按钮,可用 controller.isLoading() 或自己的导入任务状态。
5. 最小 JSON 约定
| 字段 | 要求 |
|---|---|
grid_size | 1..128 的整数。 |
colors | 非空体素字符到颜色的映射。 |
data | 按 data[z][y][x] 读取,展开后必须恰好有 grid_size³ 项。 |
| 空体素 | .。 |
| 坐标系 | Z-up,x 最快、y 次之、z 最慢。 |
JSON 解析失败、网格大小错误、颜色符号未定义或超过 maxJsonBytes 都会使 Promise reject,同时可通过 getLastError() 取到可展示文本。
下一步:把最小示例变成产品界面
| 你想做的事 | 阅读下一页 |
|---|---|
| 改背景、图片、加载遮罩或手势体验 | 接入与生命周期 |
| 查询每个 Options、回调与光照参数 | VoxelViewer API 参考 |
| 读取 VFP 目录/META、提取内嵌 GLB 或 PNG 缩略图 | Reader API 概览 |
| 在真机测试大模型与 Picker 失败场景 | 测试与排障 |