跳到主要内容

改变视角

上一章回顾: 展示三维空间

  • 你了解什么是 Work, 以及如何获取和加载。
  • 如何展示三维空间,并且在此基础上开发功能来控制三维空间。
本章你可以学习到
  • 了解什么是 State
  • 如何改变三维空间观察的方向 / 位置。
  • 了解上一章的代码,比如 setState stateChange 是如何工作的。
  • 通过 State 完成了自动环视的功能。

准备工作

和上一章节一样,我们新建一个目录(src/2.knowing-state)以及对应的 html 文件 以及 jsts 文件。

jsts 文件可以先拷贝上一章节的内容。

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <link rel="icon" href="data:;base64,iVBORw0KGgo=" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>展示空间 | Displaying work</title>
    <link
      href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.1/dist/css/bootstrap.min.css"
      rel="stylesheet"
      crossorigin="anonymous"
    />
    <link
      href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.5.0/font/bootstrap-icons.css"
      rel="stylesheet"
    />
    <style>
      html,
      body,
      #app {
        width: 100%;
        height: 100%;
        overflow: hidden;
      }
    </style>
  </head>
  <body>
    <div id="app"></div>
    <!-- 模式切换 -->
    <nav class="navbar fixed-bottom navbar-light bg-light">
      <div class="container-fluid justify-content-center">
        <div class="btn-group">
          <a href="javascript:;" class="btn btn-primary active js-Panorama"
            >全景漫游</a
          >
          <a href="javascript:;" class="btn btn-primary js-Floorplan"
            >空间总览</a
          >
        </div>
      </div>
    </nav>
    <script type="module" src="./index"></script>
  </body>
</html>
import { Five, parseWork } from "@realsee/five";

const workURL =
  "https://vrlab-public.ljcdn.com/release/static/image/release/five/work-sample/07bdc58f413bc5494f05c7cbb5cbdce4/work.json";

const five = new Five();

five.appendTo(document.querySelector("#app"));

fetch(workURL)
  .then((res) => res.json())
  .then((json) => {
    const work = parseWork(json);
    five.load(work);
  });

window.addEventListener("resize", () => five.refresh(), false);

{
  // === 模式切换 ===
  const buttons = {
    Panorama: document.querySelector(".js-Panorama"),
    Floorplan: document.querySelector(".js-Floorplan"),
  };

  for (const [modeName, element] of Object.entries(buttons)) {
    element.addEventListener(
      "click",
      () => {
        five.setState({ mode: modeName });
      },
      false
    );
  }

  five.on("stateChange", (state) => {
    for (const [modeName, element] of Object.entries(buttons)) {
      if (modeName === state.mode) {
        element.classList.add("active");
      } else {
        element.classList.remove("active");
      }
    }
  });
}

export {};

启动服务 npm run dev。 并跳转到当前页面 " <http://localhost:3000/src/2.knowing-state/index.html\> "。

提示

请查看你的控制台,端口号会因为你的配置以及当前端口占用情况变更,请已控制台输出的为准。
如果你使用其他开发构建工具,请按照自己的开发构建工具的要求启动服务。

什么是 State

又来了解概念了,我保证这是现阶段需要了解的最后一个理论知识了。

State 简介

State 用于描述状态的数据结构。上一章我们了解了 Work, Work 是用于描述一个三维空间,那么 State 则是描述在这个三维空间内,目前正在处于什么状态。他包含了模态、位于的采集点位、相机的方向、相机可视角度的信息。

State 的数据结构及说明

interface State {
  mode: Five.Mode;
  panoIndex: number;
  longitude: number;
  latitude: number;
  fov: number;
  offset: THEEE.Vector3;
}

State的数据描述

  • mode: 当前的模态 Five 常用有 5 种模态,可以使用 Five.Mode 获得:

    • Panorama: 全景游走模态,该模态下视图将在采集点间游走,手势操作可以旋转/放大视角/切换采集点,适合查看采集的全景信息。
    • Floorplan: 空间总览模态, 该模态下视图以模型为中心,手势操作可以旋转/放大模型/切换楼层,适合查看模型的整体效果。
    • Topview: 平面图模态,该模态下视图以模型为中心,垂直俯视模型,手势操作可以平移/放大模型/切换楼层,适合查看模型平面结构。
    • Model: 模型游走模态,该模态下视图将在模型中自由游走,手势操作可以旋转/放大视角/位移,适合查看模型的细节,做一些定位操作。
    • VRPanorama: VR 眼镜模态,该模态下可以使用 Cardboard 眼镜 或者他的第三方衍生产品,实现 VR 虚拟显示效果。
  • panoIndex: 采集点位,即在 Panorama 模态可以落脚的位置。是一个 work[observers] 的一个索引。

  • longitude / latitude: 相机的水平角 / 相机的偏航角(弧度),我们使用类似经纬度的方式描述相机位置。

    整个模型场景为一个右手笛卡尔坐标系, XZ平面平行于地面, Y轴垂直于地面。

    右手坐标系图示补充图片

    初始相机方向为原点看向 Z 轴负方向

    • 增加 longitude 相机向左旋转。
    • 减少 longitude 相机向右旋转。
    • 增加 latitude 相机向下旋转。
    • 减少 latitude 相机向上旋转。
  • fov: 相机垂直方向的可视角度 (角度)。

  • offset: 当前相机的三维坐标。

state 相关的 api 有什么

可以通过 state currentState 获取当前状态,通过 setState setCurrentState 设置状态。

state / currentState 的区别

currentState 是当前状态,就是画面中的状态,目前展示的状态。 state 是目标状态或者说下一时间的稳定状态。

可以简单的这样认为:
setSate 被调用后,state 当即会变成 setState 的值,而 currentState 则不会马上改变,他会在动画过渡的过程中逐步逼近 state 并最终变成和 state 一样的值。就和你在画面中看到的动画一样。

在上一章的代码示例中,我们已经使用了 mode 属性,来切换 Panorama 以及 Floorplan 模态。你也可以尝试把其他的几种模态都补充上,看看各种模态下有什么区别。

VRPanorama 模态需要使用设备陀螺仪信息,需要使用移动设备。 并且如果在 iOS 设备下,需要服务处于 https 下,否则 iOS 不允许访问陀螺仪信息。

开发一个自动环视的功能

我们已经获取和设置过了 mode,这次我们修改下 longitude / latitude 试试。这次我们开发一个自动环视的功能。通过一个按钮控制激活自动环视的功能,该功能会自动水平旋转镜头。

书写环视功能

添加环视功能的 UI 按钮

在屏幕右上角添加两个按钮,开始停止

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <link rel="icon" href="data:;base64,iVBORw0KGgo=" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>改变视角 | Knowing state</title>
    <link
      href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.1/dist/css/bootstrap.min.css"
      rel="stylesheet"
      crossorigin="anonymous"
    />
    <link
      href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.5.0/font/bootstrap-icons.css"
      rel="stylesheet"
    />
    <style>
      html,
      body,
      #app {
        width: 100%;
        height: 100%;
        overflow: hidden;
      }
    </style>
  </head>
  <body>
    <div id="app"></div>
    <!-- 模式切换 -->
    <nav class="navbar fixed-bottom navbar-light bg-light">
      <div class="container-fluid justify-content-center">
        <div class="btn-group">
          <button class="btn btn-primary active js-Panorama">全景漫游</button>
          <button class="btn btn-primary js-Floorplan">空间总览</button>
        </div>
      </div>
    </nav>
    <!-- highlight-start -->
    <!-- 环视 -->
    <div class="card position-fixed m-2 top-0 end-0">
      <button class="btn btn-light js-lookAround-start">
        <i class="bi bi-arrow-repeat"></i>
      </button>
      <button class="btn btn-light js-lookAround-stop d-none">
        <i class="bi bi-pause"></i>
      </button>
    </div>
    <!-- highlight-end -->
    <script type="module" src="./index"></script>
  </body>
</html>

书写逻辑代码

  1. 通过 setInterval 定时触发器,修改 statelongitude 值。
  2. 开始 按钮点击时,触发定时器开始。
  3. 停止 按钮点击时,触发定时器停止。

在上一张的 模式切换 的代码后追加:

{
  // === 环视 ===
  let timer;
  const startButton = document.querySelector(".js-lookAround-start");
  const stopButton = document.querySelector(".js-lookAround-stop");
  startButton.addEventListener(
    "click",
    () => {
      window.clearInterval(timer);
      timer = window.setInterval(() => {
        five.setState({ longitude: five.state.longitude + Math.PI / 360 });
      }, 16);
      startButton.classList.add("d-none");
      stopButton.classList.remove("d-none");
    },
    false
  );
  stopButton.addEventListener(
    "click",
    () => {
      window.clearInterval(timer);
      startButton.classList.remove("d-none");
      stopButton.classList.add("d-none");
    },
    false
  );
}

回到你的浏览器查看,会发现你的页面右上角出现一个环视按钮。点击可以触发和关闭环视。

真是一个不错的功能 🥳 !

下一章节你会学到

下一章我们再使用 State 做一些复杂的功能,深入体会一下 State 的能力。
  • 通过 State 完成用户操作的录制。
  • 通过 State 还原用户操作画面。