当前位置: 首页 > news >正文

Rokid JSAR 技术开发全指南+实战演练

一、JSAR 核心概念与 Rokid 适配基础

1.1 什么是 JSAR

JSAR(JavaScript Augmented Reality)是 Rokid 主导的空间小程序开发技术体系,本质为可嵌入物理空间的 Web 运行时,支持开发者使用 JavaScript、XSML(空间标记语言)等 Web 技术栈,构建能与真实环境融合的沉浸式 AR 应用。其核心价值在于降低 AR 开发门槛,让前端开发者无需掌握底层图形学技术,即可快速实现空间化交互体验。

1.2 JSAR 与 Rokid 设备的深度适配

Rokid 作为 JSAR 技术的核心落地载体,从硬件到软件提供全链路支持:

  • 硬件适配:兼容 Rokid Glasses 系列、Rokid Max 等主流 AR 头显,利用设备的高精度空间定位、手势识别与近眼显示能力,实现虚拟内容与真实空间的精准对齐。
  • 跨设备拓展:依托 WebXR 标准,JSAR 应用可无缝运行于 Rokid、Pico、Apple Vision Pro 等多品牌 AR 设备,且支持 iPhone 等移动终端通过 WebXR Viewer 访问。
  • 工具链集成:提供专属 JSAR DevTools 与真机调试方案,优化 Rokid 设备上的渲染性能与交互响应速度。

1.3 核心技术组件

组件功能说明
XSML空间标记语言,扩展 HTML 语法以描述 3D 空间结构,支持定义平面、模型等空间元素
JSAR-DOM空间文档对象模型,基于 Babylon.js 实现,提供空间元素的交互与渲染能力
JSAR DevToolsVS Code 插件,集成场景预览、代码补全、真机调试等核心开发功能
WebXR 适配层支持沉浸式 AR 模式切换,兼容 Rokid 设备的空间定位与姿态追踪

二、开发环境搭建(Rokid 官方标准流程)

2.1 环境依赖清单

依赖工具版本要求作用说明
Visual Studio Code≥ 1.80.0代码编辑与插件运行载体
Node.js≥ 18.0.0 或最新 LTS 版本依赖管理与项目构建
JSAR DevTools最新稳定版场景预览与调试核心工具

2.2 分步安装指南

步骤 1:安装基础工具
  1. Visual Studio Code:前往 VS Code 官网 下载对应系统版本,建议安装中文语言包提升开发效率。
  2. Node.js:访问 Node.js 官网 下载 LTS 版本,安装后通过终端验证:

node -v # 需显示 v18.0.0 及以上

npm** -v # 配套 npm 版本通常 ≥ 8.0.0**

步骤 2:安装 JSAR DevTools

提供两种官方安装方式,推荐优先使用商店安装:

  • 方式一:VS Code 商店安装

打开 VS Code 拓展面板,搜索 JSAR DevTools(插件 ID:RokidMCreativeLab.vscode-jsar-devtools),点击安装即可,安装链接:市场地址。

  • 方式二:VSIX 包离线安装
  1. 下载最新安装包:vscode-jsar-devtools-latest.vsix
  2. 打开 VS Code,按下 Ctrl + Shift + P,输入 Extensions: Install from VSIX...,选择下载的安装包完成安装。

2.3 环境验证

安装完成后,打开 VS Code 右下角状态栏,若显示 JSAR DevTools: Ready,则说明工具激活成功。

三、JSAR 项目开发核心流程

3.1 项目初始化(两种官方方案)

方案 1:Npm 命令快速创建
  1. 打开终端,执行初始化命令:
npm init @yodaos-jsar/widget
  1. 按照交互提示输入项目名称、描述等信息,工具会自动拉取官方模板 M-CreativeLab/template-for-jsar-widget。
  2. 进入项目目录,安装依赖:
cd 项目名称
npm install  # 安装类型定义文件,提供代码补全
方案 2:GitHub Template 创建
  1. 访问官方模板仓库:template-for-jsar-widget,点击 Use this template。
  2. 填写仓库名称,创建新的 GitHub 项目(推荐添加 jsar-widget 主题标签,便于社区发现)。
  3. 克隆项目到本地并安装依赖,示例项目可参考:
    1. 太阳系模拟器:jsar-gallery-solar-system
    2. 3D 模型展示:jsar-gallery-flatten-lion

3.2 项目核心结构解析

project-name/
├── main.xsml        # 入口文件,定义空间结构与逻辑
├── package.json     # 项目配置,需指定 main 为 main.xsml
├── icon.png         # 应用图标
├── model/           # 3D 模型资源(如 glb 格式)
│   └── foobar.glb
└── lib/             # 业务逻辑脚本└── index.ts

package.json 关键配置

{"name": "rokid-jsar-demo","displayName": "JSAR Demo","main": "main.xsml",  // 必须指向 XSML 入口"icon3d": { "base": "./model/foobar.glb" },  // 3D 图标配置"devDependencies": { "@yodaos-jsar/types": "^1.4.0" }  // 类型支持
}

3.3 核心开发语法(XSML + JSAR-DOM)

  1. XSML 空间元素定义

XSML 扩展 HTML 语法,新增 <space> 标签描述 3D 空间,支持嵌套平面、模型等元素:

<xsml version="1.0"><head><title>空间按钮示例</title><script>// 获取空间元素并绑定事件const guiPlane = spatialDocument.getElementById('gui');const openButton = guiPlane.shadowRoot.getElementById('open-btn');openButton.addEventListener('mouseup', () => {console.log('按钮被点击');});</script></head><!-- 空间容器,所有 3D 元素需置于此标签内 --><space><!-- 创建交互平面 --><plane id="gui" width="2" height="1" position="0 1.5 -3"><style>.btn {background: rgba(20,33,33,1);color: white;font-size: 50px;width: 200px;height: 100px;border-radius: 25px;}</style><div id="root"><button id="open-btn" class="btn">点击我</button></div></plane></space>
</xsml>
  1. JSAR-DOM 核心 API
API 方法功能说明
spatialDocument.getElementById(id)获取空间元素,类似 HTML DOM 的 getElementById
element.addEventListener(event, fn)绑定空间交互事件(mouseup、touchstart 等)
spatialDocument.dispatchEvent(e)触发自定义事件,实现跨组件通讯

3.4 场景预览与调试

1. 本地场景预览

  1. 在 VS Code 中打开项目的 main.xsml 文件。
  2. 点击编辑器右上角的「场景视图」按钮(立体图形图标)。
  3. 场景视图支持两种核心操作
    重置位置:将场景恢复到原点坐标
    刷新:代码修改后自动 / 手动重新加载场景
    2. WebXR 浏览器调试
    1.安装 Chrome 插件:Immersive Web Emulator。
    2.上传项目到本地服务,通过以下 URL 访问:
https://m-creativelab.github.io/jsar-dom/?url=http://你的IP:端口/main.xsml

3.点击「Enter AR」按钮,即可在浏览器中模拟 Rokid 设备的 AR 沉浸式体验。
3. Rokid 真机调试

  1. 确保开发机与 Rokid 设备处于同一局域网。
  2. 在 JSAR DevTools 中选择「真机调试」,自动识别设备并部署应用。
  3. 支持通过 Chrome DevTools 协议(CDP)进行断点调试与日志打印。

四 、实战演练-3D模型展示-小黄鸭模型的旋转和视图放大缩小

4.1 项目结构

搭建如下项目结构:

3d-model-showcase/
├── lib/                # TypeScript源码,核心渲染与逻辑
│   └── index.ts        # three.js主入口,场景/模型/控件初始化
├── model/              # 存放glb模型文件
│   └── sample.glb      # 示例鸭子模型
├── node_modules/       # npm依赖
├── types/              # TypeScript类型声明
│   └── three-examples.d.ts # three.js扩展类型声明
├── main.html           # 前端页面,入口UI与脚本加载
├── package.json        # 项目依赖与脚本
├── tsconfig.json       # TypeScript编译配置

1. package.json
{"$schema": "https://json.schemastore.org/package","name": "3d-model-showcase","version": "1.0.0","description": "3D 模型展示最小示例(three.js + TypeScript)","main": "lib/index.ts","type": "module","scripts": {"build": "tsc","start": "npx http-server -c-1 ./ -p 8080"},"author": "","license": "MIT","dependencies": {"three": "^0.154.0"},"devDependencies": {"@types/three": "^0.152.0","http-server": "^14.1.1","typescript": "^5.9.3"}
}

记录依赖(如three、typescript、http-server)。

提供npm start脚本,启动本地静态服务器。

2. tsconfig.json
{"$schema": "https://json.schemastore.org/tsconfig","compilerOptions": {"target": "ES2020","module": "ES2020","moduleResolution": "node","strict": false,"esModuleInterop": true,"forceConsistentCasingInFileNames": true,"skipLibCheck": true,"outDir": "./lib","declaration": true,"allowJs": true,"checkJs": false,"lib": ["ES2020", "DOM", "DOM.Iterable"]},"include": ["src/**/*", "lib/**/*"],"exclude": ["node_modules"]
}

3. main.html
<!-- main.xsml - 最小页面示例,包含一个全屏 canvas 用于 three.js 渲染 -->
<!DOCTYPE html>
<html lang="zh-CN"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>3D 模型展示</title><!-- Import map: 映射裸模块名到本地模块,便于直接在浏览器中使用 node_modules 的 ES module --><script type="importmap">{"imports": {"three": "/node_modules/three/build/three.module.js","three/": "/node_modules/three/"}}</script><style>html,body { height:100%; margin:0; }#app { width:100%; height:100%; display:flex; flex-direction:column; }#viewer { flex:1; position:relative; }canvas { width:100%; height:100%; display:block; }#controls { padding:8px; background:#111; color:#fff; font-family: Arial, sans-serif }button { margin-right:8px }</style></head><body><div id="app"><div id="controls"><button id="loadBtn">加载示例模型</button><input id="modelUrl" placeholder="输入 glb URL 或留空使用 ./model/sample.glb" style="width:50%" /><span id="status" style="margin-left:12px;color:#9cc;">就绪</span></div><div id="progressBar" style="height:6px; background:#333; width:100%"><div id="progressFill" style="height:100%; width:0%; background:#3af;"></div></div><div id="errorMsg" style="color:#f66; padding:6px 8px; display:none; background:#2b0000"></div><div id="viewer"><canvas id="glCanvas"></canvas></div></div><script type="module">import { init, loadModelFromUrl } from './lib/index.js';// 初始化渲染器并挂载到 canvasinit({ canvas: '#glCanvas', background: '#0b1220' });const statusEl = document.getElementById('status');const progressFill = document.getElementById('progressFill');const errorEl = document.getElementById('errorMsg');document.getElementById('loadBtn').addEventListener('click', async () => {const input = document.getElementById('modelUrl');const url = input.value && input.value.trim() !== '' ? input.value.trim() : './model/sample.glb';// reset UIif (errorEl) { errorEl.style.display = 'none'; errorEl.textContent = ''; }if (statusEl) statusEl.textContent = '开始加载...';if (progressFill instanceof HTMLElement) progressFill.style.width = '0%';try {await loadModelFromUrl(url, (percent) => {if (statusEl) statusEl.textContent = `加载中 ${percent}%`;if (progressFill instanceof HTMLElement) progressFill.style.width = percent + '%';}, (err) => {if (errorEl) { errorEl.style.display = 'block'; errorEl.textContent = '加载出错: ' + (err && err.message ? err.message : String(err)); }if (statusEl) statusEl.textContent = '加载错误';});if (statusEl) statusEl.textContent = '加载完成';if (progressFill instanceof HTMLElement) progressFill.style.width = '100%';} catch (e) {console.error(e);if (errorEl) { errorEl.style.display = 'block'; errorEl.textContent = '加载失败: ' + (e && e.message ? e.message : String(e)); }if (statusEl) statusEl.textContent = '加载失败';}});</script></body>
</html>

页面入口,包含UI(加载按钮、输入框、进度条)。

加载lib/index.js,初始化three.js渲染。

使用import map映射three.js及其扩展模块,支持浏览器原生ESM加载。

绑定按钮事件,调用loadModelFromUrl加载模型。

4. index.ts
// lib/index.ts
// 简单的 three.js 初始化与 glTF (GLB) 加载器。
// 目标:作为项目的最小可用实现 — 若要实际运行,请先 `npm install` threeexport type InitOptions = {canvas?: HTMLCanvasElement | string;modelUrl?: string;background?: string;
};let THREE: any = null;
let renderer: any = null;
let scene: any = null;
let camera: any = null;
let controls: any = null;
let model: any = null;  // 保存当前加载的模型引用export async function init(opts: InitOptions = {}) {try {// 通过包名动态导入 three,兼容 TypeScript 和打包器THREE = await import('three');} catch (e) {// three.js 未安装或不能加载// 在开发时请运行: npm install three// 这里仅做无侵入的降级处理// eslint-disable-next-line no-consoleconsole.warn('three.js not available. Please run `npm install three` to enable 3D preview.');return;}const { canvas, background = '#222' } = opts;const canvasEl = typeof canvas === 'string' ? (document.querySelector(canvas) as HTMLCanvasElement) : canvas;renderer = new THREE.WebGLRenderer({ canvas: canvasEl ?? undefined, antialias: true });renderer.setSize(window.innerWidth, window.innerHeight);renderer.setPixelRatio(window.devicePixelRatio || 1);renderer.setClearColor(background);scene = new THREE.Scene();camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 1000);camera.position.set(0, 1.5, 3);const hemi = new THREE.HemisphereLight(0xffffff, 0x444444, 1.0);scene.add(hemi);const dir = new THREE.DirectionalLight(0xffffff, 1);dir.position.set(5, 10, 7.5);scene.add(dir);// 小网格辅助const grid = new THREE.GridHelper(10, 10);scene.add(grid);window.addEventListener('resize', onResize);// 动态加载 OrbitControls(浏览器环境直接从 node_modules)try {const controlsModule = await import('three/examples/jsm/controls/OrbitControls.js');const { OrbitControls } = controlsModule;controls = new OrbitControls(camera, renderer.domElement);controls.enableDamping = true;controls.dampingFactor = 0.05;controls.screenSpacePanning = false;controls.minDistance = 0.1;controls.maxDistance = 500;} catch (e) {// ignore if controls cannot be loaded}animate();if (opts.modelUrl) {// 忽略加载错误,让调用者处理异常try {await loadModelFromUrl(opts.modelUrl);} catch (err) {// eslint-disable-next-line no-consoleconsole.error('模型加载失败', err);}}
}export async function loadModelFromUrl(url: string, onProgress?: (percent: number) => void, onError?: (err: any) => void) {if (!THREE) throw new Error('three.js 未初始化');// GLTFLoader 在 examples 模块中,直接从 node_modules 引入浏览器模块// 动态导入 GLTFLoader,兼容 Vite/Webpack/Node 环境const loaderModule = await import('three/examples/jsm/loaders/GLTFLoader.js');const { GLTFLoader } = loaderModule;const loader = new GLTFLoader();return new Promise((resolve, reject) => {loader.load(url,(gltf: any) => {// 把模型加入场景并居中scene.add(gltf.scene);model = gltf.scene;  // 保存模型引用const box = new THREE.Box3().setFromObject(gltf.scene);const center = box.getCenter(new THREE.Vector3());const size = box.getSize(new THREE.Vector3());const radius = size.length() * 0.5;// 将模型居中到原点,便于统一处理gltf.scene.position.sub(center);// 重新计算包围盒并把模型底部抬到 y=0(贴地)const bbox = new THREE.Box3().setFromObject(gltf.scene);const minY = bbox.min.y;if (minY < 0) {const baseY = -minY;gltf.scene.position.y = baseY; // 将最低点移动到 y=0gltf.scene.userData.baseY = baseY; // 保存基础高度供动画使用}// 计算一个合适的相机距离以完整看到模型if (camera) {const fov = camera.fov * (Math.PI / 180); // 垂直视场(弧度)// 根据包围球半径和视场角估算距离const distance = Math.abs(radius / Math.sin(fov / 2)) || radius * 2;// 把相机放到模型前上方,稍微偏上以便看到顶部细节camera.position.set(0, radius * 0.6, distance * 1.2);camera.lookAt(new THREE.Vector3(0, 0, 0));camera.updateProjectionMatrix();}resolve(gltf);},(progressEvent: ProgressEvent) => {if (onProgress && progressEvent && progressEvent.lengthComputable) {const percent = Math.round((progressEvent.loaded / progressEvent.total) * 100);onProgress(percent);}},(err: any) => {if (onError) onError(err);reject(err);});});
}function animate() {requestAnimationFrame(animate);if (controls) controls.update();// 添加简单的上下浮动动画if (model) {const bobbingHeight = 0.1;const baseY = model.userData.baseY || 0;model.position.y = baseY + Math.sin(Date.now() * 0.005) * bobbingHeight;}if (renderer && scene && camera) renderer.render(scene, camera);
}function onResize() {if (!camera || !renderer) return;camera.aspect = window.innerWidth / window.innerHeight;camera.updateProjectionMatrix();renderer.setSize(window.innerWidth, window.innerHeight);
}export function dispose() {window.removeEventListener('resize', onResize);if (renderer) {try {renderer.dispose();// 强制丢弃 GL context(如果可用)// @ts-ignorerenderer.forceContextLoss && renderer.forceContextLoss();} catch (e) {// ignore}}renderer = scene = camera = null;
}
// end of file

three.js主逻辑,导出init和loadModelFromUrl两个核心方法。

init:初始化渲染器、场景、相机、光源、网格辅助线、OrbitControls(鼠标旋转缩放)。

loadModelFromUrl:加载glb模型,自动居中、贴地,调整相机视角。

animate:渲染循环,实时刷新场景。

仅保留基础交互,支持鼠标操作。

5. 准备其他文件
  • <font style="background-color:rgb(187,191,196);">icon.png</font>:准备一张方形图片(例如 128x128 像素),放在项目根目录,作为小程序图标。
  • <font style="background-color:rgb(187,191,196);">model/sample.glb</font>:找一个 glb 格式的 3D 模型文件,放在 <font style="background-color:rgb(187,191,196);">model</font> 目录下(可从网上下载免费的 3D 模型,或使用自己的模型)。
  • three-examples.d.ts:声明three.js扩展模块类型,解决TypeScript编译时的类型报错。

4.2 运行方式

  1. 安装依赖

npm install

  1. 编译TypeScript

npx tsc --project tsconfig.json

  1. 启动本地服务器

npm start

默认会用 http-server 启动 8080 端口。

访问页面 在浏览器打开

http://127.0.0.1:8080/main.html

  1. 加载模型

点击“加载示例模型”按钮,或输入自定义glb模型URL加载。

支持鼠标旋转、缩放、平移视角。

4.3 核心依赖

three.js:WebGL 3D渲染引擎

http-server:本地静态服务器

TypeScript:类型安全开发

4.4 效果展示

五、资源与支持

官方资源

  • 开发者平台:Rokid 开发者中心
  • 模板仓库:template-for-jsar-widget
  • 技术文档:JSAR 官方手册
http://www.dtcms.com/a/442777.html

相关文章:

  • 昆明做网站哪家便宜计算机网络课程设计
  • 《网页设计与网站建设》A卷答案成都o2o网站建设
  • 建筑工程网官网入口商丘seo教程
  • 求解子网掩码
  • 网站 转成 微信小程序西城上海网站建设
  • 【AI论文】SLA:通过精细可调的稀疏线性注意力机制突破扩散变换器中的稀疏性局限
  • 博客自定义网站服饰 视频 网站建设
  • SSM创新实践学分管理系统08a30(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面。
  • 太原建设网站制作WordPress手机号验证登录
  • SSM大学教务管理系统61dy9(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面。
  • 连云港市建设工程安全监督站网站青岛平台网站建设
  • 缓存锁(Cache Lock)是什么?
  • linux建设网站php打开提示404申请一个域名可以建设一个网站吗
  • 人工智能开发工具全景指南:从编码辅助到模型部署的全链路实践
  • 做一个宣传网站要多少钱wordpress 要加上
  • mysql学习
  • 爬坑 10 年!爱回收询价接口实战:从型号匹配、分页续传到数据完整性校验
  • 人工智能领域、图欧科技、IMYAI智能助手2025年9月更新月报
  • 怎么利用网站开发app中海园林建设有限公司网站
  • Python Access:删除数据库中指定的表和查询
  • 苏州设计网页网站珠宝行业做网站的好处
  • 设备管理系统网站模板什么网站比较容易做
  • 动漫人物做羞羞事的网站镇江专业网站制作
  • Docker 网络模式与通信机制
  • 预约优化方案全链路优化实践
  • Linux指令(1)
  • TS学习笔记
  • 上海建设银行官方网站有关网站排名的论文
  • Zabbix对决Prometheus:监控系统终极对比
  • 【ROS2学习笔记】 TF 坐标系