# VR 初始化展示指南

本文档面向**接入众趣 zqsdk 的开发者**，说明如何在自己的项目中**一步步完成 VR 展示**：从准备容器、引入依赖、获取模型 ID，到调用初始化接口、启动渲染并处理加载状态与错误。按顺序完成以下步骤即可在页面中看到 VR 场景。

---

## 一、功能简介与前置条件

### 1.1 什么是 VR 展示

VR 展示即使用众趣 zqsdk 在网页中加载并渲染三维空间模型，用户可进行漫游、切换楼层、切换模式（全景/平面/3D）等操作。完成「初始化并启动」后，SDK 会将 3D 内容渲染到你提供的 DOM 容器中，并挂载 `housePlay`、`conf`、`company` 等对象供后续功能（导览、讲房、户型图等）使用。

### 1.2 前置条件

- **模型 ID**：众趣后台创建并发布的空间模型对应的 ID，通常通过 URL 参数（如 `?m=xxx`）或业务接口传入。
- **运行环境**：支持现代浏览器的前端项目；若使用构建工具，需保证 zqsdk 及其依赖的加载顺序正确。
- **依赖包**：jQuery、TWEEN（tween.js）、以及众趣提供的 zqsdk.js；依赖必须在 zqsdk 之前加载。

---

## 二、集成步骤概览

| 步骤 | 内容 |
|------|------|
| 1 | 在页面中准备一个用于渲染的 DOM 容器，并保证其 id 与 SDK 约定一致（通常为 `player`） |
| 2 | 按顺序引入依赖（jQuery → TWEEN → zqsdk），并将 zqsdk 挂载到 `window.zqsdk` |
| 3 | 获取模型 ID（如从 URL 参数 `m` 或业务数据中读取） |
| 4 | 调用 `window.zqsdk.init({ modelId, complete })`，在 `complete` 回调中拿到 `housePlay` |
| 5 | 在 `complete` 中先注册需要的事件监听，再调用 `housePlay.start()` 启动 VR 展示 |
| 6 | （可选）监听 `initLoading`、`load_progress`、错误情况，用于展示加载进度与错误提示 |

以下按步骤展开。

---

## 三、第一步：准备渲染容器

SDK 需要在一个**已存在于 DOM 中的节点**内挂载 WebGL 与 UI，该节点的 **id 需与 SDK 内部约定一致**。众趣 SDK 默认使用 **`id="player"`** 的容器。

**示例（全屏或占满父容器）：**

```html
<!-- 核心 3D 舞台，SDK 会在此节点内渲染 -->
<div id="player" class="player-container"></div>
```

```css
.player-container {
  width: 100%;
  height: 100%;
  position: absolute;
  left: 0;
  top: 0;
}
```

注意：

- 容器必须在调用 `zqsdk.init` **之前**就已在页面上渲染完成；若使用 Vue/React，请在组件挂载后再执行 init（例如在 `onMounted` / `useEffect` 中调用）。
- 容器尺寸建议占满可视区域或你希望展示 VR 的区域，否则 3D 画布可能被裁剪或比例异常。

---

## 四、第二步：引入依赖与 zqsdk

zqsdk **依赖** jQuery 和 TWEEN（tween.js），且必须在 zqsdk 之前加载，否则会报错。推荐两种方式之一。

### 4.1 方式一：通过 script 标签（适合传统页面或 index.html）

**顺序不可颠倒：**

```html
<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.4.1/jquery.js"></script>
<script src="./js/tween.umd.js"></script>
<script src="./js/zqsdk.js"></script>
```

若使用本地文件：

```html
<script src="/js/jquery.min.js"></script>
<script src="/js/tween.umd.js"></script>
<script src="/js/zqsdk.js"></script>
```

zqsdk 初始化完成后会向 `window` 挂载 `zqsdk`、`housePlay`、`conf`、`company`，因此需保证 zqsdk 以**同步**方式加载（不用 async/defer 影响 init 调用时机），或在你自己的脚本中在依赖就绪后再调用 `zqsdk.init`。

### 4.2 方式二：通过模块打包（Webpack / Vite 等）

先安装依赖：

```bash
npm install jquery @tweenjs/tween.js
```

在入口或 VR 初始化前执行（必须在 zqsdk 引入之前）：

```javascript
import $ from 'jquery'
import * as TWEEN from '@tweenjs/tween.js'

window.jQuery = $
window.$ = $
window.TWEEN = TWEEN.default || TWEEN
```

再引入 zqsdk（若 SDK 提供 npm 包或你复制到项目中）：

```javascript
import zqsdk from './js/zqsdk.js'  // 或众趣提供的路径
window.zqsdk = zqsdk
```

**注意**：zqsdk 支持 ES Modules 和 CommonJS，但**依赖必须在 zqsdk 之前加载**，否则会报错。

---

## 五、第三步：获取模型 ID

模型 ID 由众趣后台分配，一般为字符串。常见获取方式：

- **URL 参数**：例如 `https://your-domain.com/vr?m=模型ID`，在页面中解析 `?m=xxx`；
- **业务接口**：从你的后端或配置中获取当前房源的模型 ID，再传给初始化逻辑。

示例（从 URL 读取参数 `m`）：

```javascript
function getModelId() {
  const params = new URLSearchParams(window.location.search)
  
  return params.get('m')  // 无则返回 null
}

const modelId = getModelId()

if (!modelId) {
  console.error('缺少模型 ID，请通过 URL 参数 m 传入')
  // 可在此展示错误提示，不调用 init
  return
}
```

---

## 六、第四步：调用 zqsdk.init

在**容器已渲染、依赖已加载、模型 ID 已获取**的前提下，调用初始化接口：

**模型静态资源的存储规则必须按照众趣要求的路径存储**

```javascript
window.zqsdk.init({
  modelId: modelId,   // 上一步获取的模型 ID 字符串
  // (可选) 资源请求配置
  // 当众趣模型的静态资源需要在客户自己的服务器维护时使用
  // 需要设置资源请求的域名
  // 当前云存储仅支持阿里oss静态资源存储
  resourceRequestConf: {
    resourceType: 'custom_host',
    customDomain: 'https://your_domain.com/',
  },
  complete: function() {
    // 连接成功：此时 window.housePlay、window.conf、window.company 已挂载
    let housePlay = window.housePlay
    if (!housePlay) {
      console.error('housePlay 实例未创建')
      return
    }
    // 下一步：在此注册事件并启动（见第七步）
  }
})
```

说明：
- **modelId**：必填，字符串。
- **resourceRequestConf**：选填，对象格式。 如果您需要将众趣生产的模型静态资源文件（全景切图等）存储到您自己的服务器上进行管理， 则在初始化sdk的时候，需要传递`resourceRequestConf`参数，来设置模型静态资源的域名。resourceRequestConf.resourceType: 资源类型， 取值为`zq_cloud`或`custom_host`。 `zq_cloud`代表资源从众趣云平台获取。 `custom_host`代表从自定义域名服务上获取。 

resourceRequestConf.customDomain: 自定义域名。 您自己服务的域名地址（当`resourceRequestConf.resourceType`设置为`custom_host`时才生效）

**<font color="red">切图资源必须使用阿里oss进行存储，zqsdk目前仅支持阿里oss的切图参数</font>**。

- **complete**：初始化成功后的回调；**仅在此回调执行后**，`housePlay` 才可用；若 init 失败，可能不会调用 complete，需结合你项目的错误处理（如超时、网络错误）做兜底。
- 初始化过程中 SDK 会拉取模型配置、资源等；真正的 3D 渲染在调用 `housePlay.start()` 之后开始。

---

## 七、第五步：在 complete 中注册事件并启动

在 `complete` 回调里建议按以下顺序执行：

1. **保存 housePlay 引用**（若你封装成单例或 store，可在此赋值）。
2. **注册你关心的事件**（如加载进度、UI 就绪、楼层变化等），便于后续做加载条、楼层切换、导览、讲房等。事件注册示例见下。
3. **调用 `housePlay.start()`**，启动 VR 渲染与交互。

**最小可用示例：**

```javascript
complete: function() {
  let housePlay = window.housePlay
  if (!housePlay) return

  // 可选：注册加载进度，用于显示 0–100% 进度条
  housePlay.on('initLoading', function() {
    yourApp.setStatus('loading')
  })

  housePlay.on('load_progress', function(progress) {
    yourApp.setLoadProgress(progress)
    if (progress >= 99) yourApp.setLoadProgress(100)
  })

  // 可选：UI 就绪后初始化楼层、导览、讲房等列表
  housePlay.on('init_UI', function(model) {
    yourApp.setStatus('ready')
    // 可在此根据 model.heroLocations、model.settings.vr_speak_data 等初始化业务 UI
  })

  // 必须：启动 VR 展示
  housePlay.start()
}
```

**推荐**：先注册事件再 `start()`，这样从「开始加载」到「UI 就绪」的整个过程中的事件都不会遗漏。

---

## 八、第六步（可选）：加载进度与错误处理

为提升体验，建议在页面上展示**加载进度**与**错误提示**。

### 8.1 加载状态与进度

- **initLoading**：SDK 开始加载时触发，可将状态设为「加载中」并显示遮罩或进度条。
- **load_progress**：回调参数为 0–100 的数值，可更新进度条；当进度达到 99 或 100 时，可认为即将完成或已完成，再结合 **init_UI** 将状态设为「就绪」并隐藏加载遮罩。

```javascript
housePlay.on('initLoading', function() {
  yourApp.setVrStatus('loading')
})

housePlay.on('load_progress', function(progress) {
  yourApp.setLoadProgress(progress)
  if (progress >= 99) yourApp.setLoadProgress(100)
})

housePlay.on('init_UI', function() {
  yourApp.setVrStatus('ready')  // 可在此隐藏加载层
})
```

### 8.2 错误处理

若 `modelId` 无效、网络异常或 SDK 内部错误，可能不会调用 `complete`，或页面白屏。建议：

- 在调用 `init` 前校验 `modelId` 是否存在；
- 为 `init` 或 `complete` 设置超时，超时后提示「加载失败，请重试」并可提供重新加载按钮；
- 若有全局错误捕获，可对相关错误做友好提示。

本仓库中在初始化失败时会设置 `status === 'error'` 和 `errorMessage`，并展示错误组件，用户可点击「重试」再次调用 `initSdk()`（即重新执行 init）。

---

## 九、完整流程串联示例

下面将上述步骤合并为一段**伪代码**，便于按顺序实现：

```javascript
// 1. 页面中已有 <div id="player" class="player-container"></div>
// 2. 已按顺序加载 jQuery、TWEEN、zqsdk，并挂载 window.zqsdk

function initVr() {
  const modelId = getModelId()  // 例如从 URL ?m= 读取

  if (!modelId) {
    showError('缺少模型 ID（请通过 URL 参数 m 传入）')

    return
  }

  if (!window.zqsdk) {
    showError('zqsdk 未加载，请检查依赖引入')

    return
  }

  window.zqsdk.init({
    modelId: modelId,
    // (可选) 资源请求配置
    // 当众趣模型的静态资源需要在客户自己的服务器维护时使用
    // 需要设置资源请求的域名
    // resourceRequestConf: {
    //     resourceType: 'custom_host',
    //     customDomain: 'https://your_domain.com/',
    // },
    complete: function() {
      const housePlay = window.housePlay

      if (!housePlay) {
        showError('housePlay 实例未创建')
        return
      }

      housePlay.on('initLoading', function() {
        setStatus('loading')
      })
      housePlay.on('load_progress', function(progress) {
        setLoadProgress(progress >= 99 ? 100 : progress)
      })
      housePlay.on('init_UI', function(model) {
        setStatus('ready')
        initBusinessFeatures(model)  // 楼层、导览、讲房等
      })

      housePlay.start()
    }
  })
}

// 页面或组件挂载后调用
initVr()
```

---

## 十、API 速查表

### 10.1 初始化

| 接口 | 说明 |
|------|------|
| `window.zqsdk.init({ modelId, complete })` | 初始化 SDK；`modelId` 为字符串；`complete` 为成功回调，内可拿到 `housePlay` 并执行 `start()` |

### 10.2 启动与全局对象

| 接口 | 说明 |
|------|------|
| `housePlay.start()` | 启动 VR 展示（必须在 complete 内调用） |
| `window.housePlay` | 模型控制对象，用于事件注册与后续所有 VR 能力 |
| `window.conf` | 配置信息 |
| `window.company` | 公司信息（若后台未配置则为 null） |

### 10.3 常用事件（用于加载与就绪）

| 事件 | 说明 |
|------|------|
| `initLoading` | 开始加载 |
| `load_progress` | 加载进度，参数为 0–100 |
| `init_UI` | UI 初始化完成，可在此根据 model 初始化楼层、导览、讲房等 |

### 10.4 相关文档
<font color=red>更为详细的housePlay使用文档，请参考[housePlay使用说明文档（全）](../众趣zqsdk使用指南/housePlay使用说明文档（全）.md)</font>

---

## 十一、常见问题与注意事项

1. **依赖加载顺序**  
   必须为：jQuery → TWEEN → zqsdk；否则 zqsdk 可能报错或无法使用。

2. **容器 id**  
   zqsdk默认使用 `id="player"` 的节点作为渲染容器, 必须使用此id作为容器id。

3. **必须在 complete 中调用 start()**  
   只有 `complete` 执行后 `housePlay` 才可用；且只有调用 `housePlay.start()` 后才会真正开始加载场景并渲染，不要遗漏。

4. **先注册事件再 start()**  
   建议在 `complete` 里先 `housePlay.on(...)` 注册所需事件，再 `housePlay.start()`，这样从 initLoading 到 init_UI 的整段过程都能收到回调。

5. **单页应用中的容器**  
   若使用 Vue/React 等，确保 `#player` 所在组件已挂载到 DOM 后再调用 `zqsdk.init`（例如在 onMounted、useEffect 中调用），否则 SDK 可能找不到容器。

---

完成以上步骤后，VR 场景即可在页面中展示；后续可在此基础上按需接入「导览」「AI 讲房」「户型图」「标尺」等功能，参见对应集成指南。本仓库实现可参考 `src/stores/vr.ts` 的 `initSdk`、`src/modules/pc/NestVrPlayer.vue` 的容器与加载/错误展示，以及 `index.html` 的依赖加载顺序。

