跳到主要内容

起步

React 框架上通过 Function Components and Hooks 模式集成 Five 开发,完整源码示例:JavaScript | TypeScript

本章你可以学习到
  • 准备开发环境。
  • 如何引入 Five SDK。
  • 将一个三维空间展示到屏幕中。

准备工作

开发环境

  • 你需要准备一个现代浏览器。
信息

Five 的浏览器支持如下,请自行选择一个你熟悉的:

SafariSafari on iOSChromeChrome for AndroidEdgeFirefox
>= 9>= 9>= 49>= 93>= 13>= 45
  • 你需要安装 Node.js,并且 Node.js 版本尽量 >= 12.x,以规避历史遗留的兼容问题。

使用开发构建工具

本示例通过 Vite 初始化开发环境。你可以通过下方代码自行初始化。

# npm 6.x
npm create vite@latest my-vue-app --template react

# npm 7+, extra double-dash is needed:
npm create vite@latest my-vue-app -- --template react

src 下建立本次教程的目录 src/0.getting-started

每一个教程均会创建一个新目录作为记录,便于总结和查找。当课程结束你会得到这样的目录结构。

src
├── 0.getting-started
├── 1.displaying-work
├── 2.knowing-state
...

完整代码样例也是这样的目录结构,你可以随时参考。

提示

如果你熟悉其他的开发构建工具如 Webpack Snowpack parcel 你也可以使用他们。

创建 HTML 文件

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <link rel="icon" href="data:;base64,iVBORw0KGgo=">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>起步 | Getting started</title>
  <!-- highlight-start -->
  <style>
    * { margin: 0; padding: 0; }
    html, body, #app { width: 100%; height: 100%; overflow: hidden; }
  </style>
  <!-- highlight-end -->
</head>
<body>
  <!-- highlight-start -->
  <div id="app"></div>
  <script type="module" src="./index"></script>
  <!-- highlight-end -->
</body>
</html>

使用直接引入 <script type="module" src="./index"></script>Vite 的特征, 如果你使用其他开发构建工具请自行处理代码引入和入口文件。 比如 Webpack 下使用 HtmlWebpackPlugin 等。

书写测试逻辑代码

我们先创建一个简单的 Hello World 来确保整体代码能够跑通。

const app = document.querySelector("#app");
// highlight-start
app.innerHTML = "Hello World.";
// highlight-end
export {};

最后的 export {}; 是因为 Vite 使用 type="module" 引入的关系,每个文件都需要是一个 module, 需要 export。 如果你使用其他开发构建工具,请按照自己的开发构建工具的要求编写。

启动服务 npm run dev。 并跳转到当前页面 "http://localhost:3000/src/0.getting-started/index.html"。

信息

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

然后你会在页面看到

Hello World.

这样的输出,说明开发构建工具的已经完成。

后面几个章节将不再详细叙述上述步骤,请一并完成即可。

从 npm 安装依赖包

在你的工程目录安装依赖

npm install @realsee/five three@0.117 react react-dom

依赖需要

  • @realsee/five Five
  • three three.js 是 Five 的依赖图形/数学库。 目前请使用 0.117.x 的版本
  • react React 框架
  • react-dom React 的浏览器端渲染器
信息

如果使用npm install 安装依赖报错,可以尝试添加 -- force 命令

渲染一个三维空间

是时候渲染一个VR场景看看了

加载三维空间

删除你之前的 Hello World 代码。我们来重新编写。 你可以先不清楚接下来编写的代码的含义,你在下一章会学习到。

import React, { useState, useEffect } from "react";
import ReactDOM from "react-dom";
import { parseWork } from "@realsee/five";
import { createFiveProvider, FiveCanvas } from "@realsee/five/react";

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

/**
 * React Hook: 通过 work.json 的地址 获取 work 对象
 * @param url work.json 的数据地址
 * @returns work 对象,如果获取中,返回 null
 */
function useFetchWork(url) {
  const [work, setWork] = useState(null);
  useEffect(() => {
    setWork(null);
    fetch(url)
      .then(response => response.text())
      .then(text => setWork(parseWork(text)));
  },[url]);
  return work;
}

const FiveProvider = createFiveProvider();
const App = () => {
  const work = useFetchWork(workURL);
  return work && <FiveProvider initialWork={work}>
    <FiveCanvas width={512} height={512}/>
  </FiveProvider>;
};

ReactDOM.render(<App/>, document.querySelector("#app"));

export {};

回到你的浏览器,看看是不是已经有一个三维空间已经展示在左上的位置。 你可以用鼠标或者触摸手势操作画面,基本的浏览功能都已经附带了。

让画面铺满整个屏幕

虽然可能并不能完全理解上述代码的工作原理,但是我们可以看到 FiveCanvas 上有 widthheight 属性,非常像画面尺寸的样子。并且在浏览器中查看,画面在左上角的位置,也印证了这个猜想。对没错,他们就是用于设置画面尺寸的。

那我们尝试用我们熟悉的 React Hooks 方式,将它铺满屏幕。

import React, { useState, useEffect } from "react";
import ReactDOM from "react-dom";
import { parseWork } from "@realsee/five";
import { createFiveProvider, FiveCanvas } from "@realsee/five/react";

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

/**
 * React Hook: 通过 work.json 的地址 获取 work 对象
 * @param url work.json 的数据地址
 * @returns work 对象,如果获取中,返回 null
 */
function useFetchWork(url) {
  const [work, setWork] = useState(null);
  useEffect(() => {
    setWork(null);
    fetch(url)
      .then(response => response.text())
      .then(text => setWork(parseWork(text)));
  },[url]);
  return work;
}

// highlight-start
/**
 * 获取当前窗口的尺寸
 */
function getWindowDimensions() {
  return { width: window.innerWidth, height: window.innerHeight };
}

/**
 * React Hook: 获取当前窗口的尺寸
 */
function useWindowDimensions() {
  const [size, setSize] = useState(getWindowDimensions);
  useEffect(() => {
    const listener = () => setSize(getWindowDimensions());
    window.addEventListener("resize", listener, false);
    return () => window.removeEventListener("resize", listener, false);
  });
  return size;
}
// highlight-end

const FiveProvider = createFiveProvider();
const App = () => {
  const work = useFetchWork(workURL);
  // highlight-start
  const size = useWindowDimensions();
  // highlight-end
  return work && <FiveProvider initialWork={work}>
    // highlight-start
    <FiveCanvas {...size}/>
    // highlight-end
  </FiveProvider>;
};

ReactDOM.render(<App/>, document.querySelector("#app"));

export {};

回到你的浏览器,看看是不是符合预期。

你真棒 🥳 !

代码整理拆分

目前我们所有的代码都写在的了一个 js / ts 文件里。虽然一眼就能看到所有的逻辑,但是比较杂乱。我们将内容拆分到多个文件将会改善这一点。

  • App 拆分到单独文件。
  • useFetchWork 方法拆分到单独文件。
  • useWindowDimensions 方法拆分到单独文件。
import { useState, useEffect } from "react";
import { parseWork } from "@realsee/five";

/**
 * React Hook: 通过 work.json 的地址 获取 work 对象
 * @param url work.json 的数据地址
 * @returns work 对象,如果获取中,返回 null
 */
 function useFetchWork(url) {
  const [work, setWork] = useState(null);
  useEffect(() => {
    setWork(null);
    fetch(url)
      .then(response => response.text())
      .then(text => setWork(parseWork(text)));
  },[url]);
  return work;
}

export { useFetchWork };
src/0.getting-started/useWindowDimensions.js
import { useState, useEffect } from "react";

/**
* 获取当前窗口的尺寸
*/
function getWindowDimensions() {
return { width: window.innerWidth, height: window.innerHeight };
}

/**
* React Hook: 获取当前窗口的尺寸
*/
function useWindowDimensions() {
const [size, setSize] = useState(getWindowDimensions);
useEffect(() => {
const listener = () => setSize(getWindowDimensions());
window.addEventListener("resize", listener, false);
return () => window.removeEventListener("resize", listener, false);
});
return size;
}

export { useWindowDimensions };
src/0.getting-started/App.jsx
import React from "react";
import { createFiveProvider, FiveCanvas } from "@realsee/five/react";
import { useFetchWork } from "./useFetchWork";
import { useWindowDimensions } from "./useWindowDimensions";

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

const FiveProvider = createFiveProvider();
const App = () => {
const work = useFetchWork(workURL);
const size = useWindowDimensions();
return work && <FiveProvider initialWork={work}>
<FiveCanvas {...size}/>
</FiveProvider>;
};

export { App };
src/0.getting-started/index.jsx
import React from "react";
import ReactDOM from "react-dom";
import { App } from "./App";

ReactDOM.render(<App/>, document.querySelector("#app"));

export {};

是不是舒服多了。每个文件都非常简洁,并且很容易看明白他们分别的作用。

下一章节你会学到

下一章节你会学到
  • 了解什么是 Work。
  • 了解刚才的代码,比如 FiveProvider / FiveCanvas 组件是如何工作的。