快速开始
本页的目标是让你在一个 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 install voxel-kit --registry http://ohpm.charactech.cn/repos/ohpm/
该命令只为本次安装指定字符科技 OHPM 仓,不会改写用户的全局 registry;它会安装当前仓中的 voxel-kit,并更新工程的 oh-package.json5 与 lock 文件。可在 OHPM 的 voxel-kit 页面 查看包信息与版本。
若团队只希望对单个工程使用该源,可在工程根目录新增或修改 .ohpmrc:
registry=http://ohpm.charactech.cn/repos/ohpm/
随后仍在工程根目录执行:
ohpm install voxel-kit --registry http://ohpm.charactech.cn/repos/ohpm/
不要手动把本地 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 {
this.options.animationMode = VoxelLoadAnimationMode.STACK;
this.options.loadAnimationDurationMs = 1500;
this.options.showGroundGrid = true;
this.options.unitGridTexture = $rawfile('voxel/voxel_grid.png');
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 持有可变场景引用和内置刷新回调,必须保持稳定。
单位描边:在宿主 HAP 提供一次资源
下载 voxel_grid.png(PBR 单位网格纹理),然后将其放入实际承载页面的 HAP 模块,例如:
entry/src/main/resources/rawfile/voxel/voxel_grid.png
建议将下载文件重命名为 voxel_grid.png,保持以上目录和 $rawfile() 路径一致。
然后必须在第一次 loadText()、loadBytes() 或 loadUri() 之前赋值:
options.unitGridTexture = $rawfile('voxel/voxel_grid.png');
这张 16×16 的 PBR AO 单位网格纹理属于主体材质:它不会改写调色板颜色,近处保持边缘锐利,远处降低闪烁,并会随明暗面、光照和阴影变化。若不设置或资源无法创建,Viewer 仍能显示模型,但会回退到逐批创建的兼容 BORD Geometry 描边;大模型的描边出现会更晚,也更占用主线程和原生 Geometry 预算。
当前静态 HAR 无法被 ArkGraphics 稳定地解析为自身的图像资源:已验证 HAR 内部 $rawfile() 会解析为空 file://,Data URI 与缓存 file:// 也不能稳定创建图像。因此当前公开 HAR 的可靠合同是“宿主 HAP 在首次加载前传入 Resource”。这不是 VFP 文件内容,也不需要随每个模型复制。
若未来需要完全零配置,应将资源随独立资源命名空间的 HSP/应用模块交付;这属于包形态升级,不是当前静态 HAR 中再加一个资源文件即可解决的问题。
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() 取到可展示文本。
6. 在预览页按需开启编辑
编辑器不影响普通预览的加载路径;只有用户点“编辑”后才读取 VFP 的权威体素数据。创建时复用同一个 Viewer Controller:
import {
VoxelEditMode,
VoxelEditorController,
VoxelEditorOptions
} from 'voxel-kit';
private readonly editorOptions = new VoxelEditorOptions();
private readonly editor = new VoxelEditorController(this.controller, this.editorOptions);
// build()
VoxelViewer({ controller: this.controller, editor: this.editor })
private async enableEditing(): Promise<void> {
await this.editor.enterEdit();
this.editor.setMode(VoxelEditMode.REPLACE);
this.editor.setSelectedColor('#3A86FF');
}
之后用户单击模型即可按当前模式编辑;双指仍用于平移和缩放。进入编辑、选区、多选/框选、保存和 VFP 区段控制见 编辑器概览与接入。
下一步:把最小示例变成产品界面
| 你想做的事 | 阅读下一页 |
|---|---|
| 改背景、图片、加载遮罩或手势体验 | 接入与生命周期 |
| 查询每个 Options、回调与光照参数 | VoxelViewer API 参考 |
| 读取 VFP 目录/META、提取内嵌 GLB 或 PNG 缩略图 | Reader API 概览 |
| 在已预览模型上选取、编辑、保存或导出 | 编辑器概览与接入 |
| 在真机测试大模型与 Picker 失败场景 | 测试与排障 |