展示三维空间
上一章回顾: 起步
- 你学会了如何初始化环境。
- 并且你书写了一段代码,虽然不知道这段代码干了什么,不过也顺利得完成了载入并展示一个 VR 的工作。
- 了解什么是 Work
- 了解
FiveProvider
/FiveCanvas
组件是如何工作的
准备工作
和上一章节一样,我们新建一个目录(src/1.displaying-work
)以及对应的 html 文件 以及 jsx 或 tsx 文件。
jsx 或 tsx 文件可以先拷贝上一章节的内容。
<!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>展示空间 | Display a work</title>
<style>
* { margin: 0; padding: 0; }
html, body #app { width: 100%; height: 100%; overflow: hidden; }
</style>
</head>
<body>
<div id="app"></div>
<script type="module" src="./index"></script>
</body>
</html>
- JavaScript
- TypeScript
src/1.displaying-work/withFetchWork.jsx
import React, { Component } from "react";
import { parseWork } from "@realsee/five";
/**
* React HOC 获取 work
* @param url work.json 的地址
*/
function withFetchWork(url) {
return function(Compnent) {
return class extends Component {
state = { work: null };
componentDidMount() {
fetch(url).then(res => res.json()).then(json => {
this.setState({ work: parseWork(json) });
});
}
render() {
if (this.state.work === null) return null;
return <Compnent work={this.state.work} {...this.props}/>;
}
}
}
}
export { withFetchWork };
src/1.displaying-work/withWindowDimensions.jsx
import React, { Component } from "react";
/**
* React HOC: 获取当前窗口的尺寸
*/
function withWindowDimensions() {
return function(Compnent) {
return class extends Component {
state = this.getWindowDimensions();
resizeListener = () => {
this.setState(this.getWindowDimensions());
};
getWindowDimensions() {
return { width: window.innerWidth, height: window.innerHeight };
}
componentDidMount() {
window.addEventListener("resize", this.resizeListener, false);
}
componentWillUnmount() {
window.removeEventListener("resize", this.resizeListener, false);
}
render() {
const dimensions = { width: this.state.width, height: this.state.height };
return <Compnent windowDimensions={dimensions} {...this.props}/>;
}
}
}
}
export { withWindowDimensions };
src/1.displaying-work/App.jsx
import React, { Component } from "react";
import { compose } from "@wordpress/compose";
import { createFiveProvider, FiveCanvas } from "@realsee/five/react";
import { withFetchWork } from "./withFetchWork";
import { withWindowDimensions } from "./withWindowDimensions";
/** work.json 的数据 URL */
const workURL = "https://vr-public.realsee-cdn.cn/release/static/image/release/five/work-sample/07bdc58f413bc5494f05c7cbb5cbdce4/work.json";
const FiveProvider = createFiveProvider();
const App = compose(
withFetchWork(workURL),
withWindowDimensions()
)(class extends Component {
render() {
const { work, windowDimensions } = this.props;
return <FiveProvider initialWork={work}>
<FiveCanvas width={windowDimensions.width} height={windowDimensions.height}/>
</FiveProvider>;
}
});
export { App };
src/1.displaying-work/index.jsx
import React from "react";
import ReactDOM from "react-dom";
import { App } from "./App";
ReactDOM.render(<App/>, document.querySelector("#app"));
export {};
src/1.displaying-work/withFetchWork.tsx
import React, { Component, ComponentClass } from "react";
import { Work, parseWork } from "@realsee/five";
/**
* React HOC 获取 work
* @param url work.json 的地址
*/
function withFetchWork<P extends Record<string, any>>(url: string) {
return function(Compnent: ComponentClass<P & { work: Work }>): ComponentClass<P> {
return class extends Component<P, {work: Work | null}> {
state = { work: null };
componentDidMount() {
fetch(url).then(res => res.json()).then(json => {
this.setState({ work: parseWork(json) });
});
}
render() {
if (this.state.work === null) return null;
return <Compnent work={this.state.work} {...this.props}/>;
}
}
}
}
export { withFetchWork };
src/1.displaying-work/withWindowDimensions.tsx
import React, { Component, ComponentClass } from "react";
/**
* React HOC: 获取当前窗口的尺寸
*/
function withWindowDimensions<P extends Record<string, any>>() {
return function(Compnent: ComponentClass<P & { windowDimensions: { width: number, height: number} }>): ComponentClass<P> {
return class extends Component<P, {width: number, height: number}> {
state = this.getWindowDimensions();
resizeListener = () => {
this.setState(this.getWindowDimensions());
};
getWindowDimensions() {
return { width: window.innerWidth, height: window.innerHeight };
}
componentDidMount() {
window.addEventListener("resize", this.resizeListener, false);
}
componentWillUnmount() {
window.removeEventListener("resize", this.resizeListener, false);
}
render() {
const dimensions = { width: this.state.width, height: this.state.height };
return <Compnent windowDimensions={dimensions} {...this.props}/>;
}
}
}
}
export { withWindowDimensions };
src/1.displaying-work/App.tsx
import React, { Component } from "react";
import { Work } from "@realsee/five";
import { createFiveProvider, FiveCanvas } from "@realsee/five/react";
import { compose } from "@wordpress/compose";
import { withFetchWork } from "./withFetchWork";
import { withWindowDimensions } from "./withWindowDimensions";
/** work.json 的数据 URL */
const workURL = "https://vr-public.realsee-cdn.cn/release/static/image/release/five/work-sample/07bdc58f413bc5494f05c7cbb5cbdce4/work.json";
const FiveProvider = createFiveProvider();
const App = compose(
withFetchWork(workURL),
withWindowDimensions()
)(class extends Component<{work: Work, windowDimensions: { width: number, height: number }}> {
render() {
const { work, windowDimensions } = this.props;
return <FiveProvider initialWork={work}>
<FiveCanvas width={windowDimensions.width} height={windowDimensions.height}/>
</FiveProvider>;
}
});
export { App };
src/1.displaying-work/index.tsx
import React from "react";
import ReactDOM from "react-dom";
import { App } from "./App";
ReactDOM.render(<App/>, document.querySelector("#app"));
export {};
启动服务 npm run dev
。 并跳转到当前页面 "http://localhost:3000/src/1.displaying-work/index.html"。
请查看你的控制台,端口号会因为你的配置以及当前端口占用情况变更,请已控制台输出的为准。 如果你使用其他开发构建工具,请按照自己的开发构建工具的要求启动服务。
什么是 Work
接下来停下写代码的双手,我们来理解一些概念,放心很简单。
Work 简介
我们将一个三维空间叫做一个 Work。 那么一个 Work 是通过 json 的结构体描述的,称之为 work.json。 Work 你可以通过如视开放平台接口获取。 也可以像本次示例一样将之静态化使用。
Five 提供了 parseWork
的方法,用于解析 work.json,然后便可以得到 work 对象。
将 work 对象传入 FiveProvider
的 initialWork
属性中就可以初始化场景。你在上一章也是这么做的。
work 的使用限制
你需要在如视开发者中心配置你的 安全域名 以及 work数据的 可用时间。 在生成环境中使用 Five SDK 对对此做校验来保护开发者以及如视的数据安全。
在以下情况不会校验上述限制,便于开发和测试:
- 域名为 localhost
- 域名为 ip 地址,如 127.0.0.1, 192.168.0.5, 172.30.2.0, 10.33.10.2 等
- 在非 http 情况下访问,比如 file:// 协议下
work.json的数据样例及数据说明
你并不需要了解 work.json 的内容也可能很好的使用 Five SDK. 虽然多了解一些,也不是坏事。
点击查看work.json
{
"_signature": "LLzk2WHDbRfzQ5R+v82uVQqAGWMPfvgQ05hTwxgWKGeXbTPKtoQ8eI6FmCxJjtVjQW+wuZmZfxpzXePv4MGrWidG/05i5RRSELlPLEFZN3qYPykJLEIYhg3Wtvr8XkOI8wdeMgV82CP4Hyi84k/W2g+S+HcFs5oXfbfbFXPL/Gs=",
"allow_hosts": [
"*"
],
"base_url": "https://vr-public.realsee-cdn.cn/release/auto3dhd/9380ce27b46cbc5ba8d47fcf0e4c9e4f/",
"certificate": "-----BEGIN CERTIFICATE-----\nMIIEMzCCAhsCCQDYAS/7ATZRmTANBgkqhkiG9w0BAQsFADCBkzELMAkGA1UEBhMC\nQ04xEDAOBgNVBAgMB0JlaWppbmcxEDAOBgNVBAcMB0JlaWppbmcxFDASBgNVBAoM\nC2xpYW5qaWEuY29tMRAwDgYDVQQLDAdSZWFsc2VlMREwDwYDVQQDDAhIYXJkd2Fy\nZTElMCMGCSqGSIb3DQEJARYWbml1aGFpcWluZ0BsaWFuamlhLmNvbTAeFw0yMTA5\nMTAwNTIwMDBaFw0zMTA5MDgwNTIwMDBaMIGmMQswCQYDVQQGEwJDTjEQMA4GA1UE\nCAwHQmVpSmluZzEQMA4GA1UEBwwHQmVpSmluZzEQMA4GA1UECgwHUmVhbHNlZTEZ\nMBcGA1UECwwQUmVhbHNlZUFwcEdldHdheTEgMB4GA1UEAwwXYXBwLWdhdGV3YXku\ncmVhbHNlZS5jb20xJDAiBgkqhkiG9w0BCQEWFWRldmVsb3BlckByZWFsc2VlLmNv\nbTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAuv/y3Ezsy/wh3LCA8vomPbgI\nSO9iO5kyR+oAetklD+epMU6J/ZbvTDEomZxuS5iyyKGBupzAh2ZFLIy7tsE71Vx1\nIIvT7Kdyq66lMU4YzdrpKUcxv7oOQnO8DA1orKluNa4jkyXBywHKs/Q+20LVc+RD\ngKXqFGJUdo8mAxEScs0CAwEAATANBgkqhkiG9w0BAQsFAAOCAgEAkMxsU4VLPd4J\n0rElBNBIyqPtvnlTs6VkhIK0l4oM58wtDKc1uG9UPSX5j29NguZM6LOe0jCsU2Vg\nEpUseMWQjx4o2yBg7MokQyjWc1zu6PppKhQ+RqHQy/biJ2zsIMpX3oMASXffvnW5\nn4Bjyo1JdDJiLm1fLvLlVVxQoraJD+rtpqWDEYixGVREUo5OIL5Y5dVjkHG2r9RQ\nQuu3yEiyr9gAW8yhz3YR6/sJ6boyGK8NC0v8Jih7NnCdT+9ML+3jn3P5F3TeXdSf\nVeYIm5oWAOTe3AjjKP8ARMb2RYACjg80/AcowD/dvRRjbwQmyucUNug2pXJynXpD\nNfx1IBmUmzSAT1Z5yNuY/f3VRBJvmIQ6Jpmef+g0/wUJpyS4SObguItyYlFPLqRH\nK1oKqNX/uV0GWWEQl6Lml986TzlHxc4ljtHBhjzlKYIYYZLWWipk4JiB8hxJcTK+\ncrgvclEQSxFlmAyoqxYFClrOOsPqZJdBhDTvoUWnnWuJLQt7DLHpyInp+S75Gg3o\n0zgHpt9m26B3YbjQGYMQlYmhl2VLQa+Ey0W8UZQXLcTvoRT4p+8crqr6cNNsxCyZ\nm08vBbEMIMvhBeLQvpM75oaMBmelegipFl2eelxVIHdGJWoyJSZQUdXN0uSidhZp\nI7AIgzhqK1Ku/IXK0OSXJonn+/9X/VI=\n-----END CERTIFICATE-----",
"create_time": "2020-07-29T15:18:06+08:00",
"expire_at": "2132653492169",
"initial": {
"flag_position": [
-1.8683282713225706,
-1.1184803014502747,
-2.536637882840082
],
"fov": 95,
"heading": 0,
"latitude": 0,
"longitude": 5.58602282229265,
"pano": 4,
"pano_index": 4
},
"model": {
"file_url": "model/auto3d-syVb5hzy8ZL1gZut_VvGDo.at3d",
"material_base_url": "materials/",
"material_textures": [
"texture_0.jpg",
"texture_1.jpg",
"texture_2.jpg",
"texture_3.jpg",
"texture_4.jpg",
"texture_5.jpg",
"texture_6.jpg",
"texture_7.jpg",
"texture_8.jpg"
],
"modify_time": "2020-07-29T15:37:17+08:00",
"type": 0
},
"observers": [
{
"accessible_nodes": [
1,
2,
3,
4,
6,
7,
8
],
"floor_index": 0,
"index": 0,
"offset_point_count": 551,
"position": [
-2.9148900508880615,
0,
1.392490029335022
],
"quaternion": {
"w": 0.44238951628615497,
"x": -0.0011834399273419546,
"y": -0.8968221073969266,
"z": -0.00045692663054024737
},
"standing_position": [
-2.9148900508880615,
-1.1193245547539874,
1.392490029335022
],
"visible_nodes": [
1,
2,
3,
4,
6,
7,
8
]
},
{
"accessible_nodes": [
0,
2,
3,
4
],
"floor_index": 0,
"index": 1,
"offset_point_count": 1382,
"position": [
-1.780460000038147,
-0.00217098998837173,
4.450079917907715
],
"quaternion": {
"w": 0.46776604948048534,
"x": -0.0011699393533674196,
"y": -0.883851410579187,
"z": -0.000490464139922521
},
"standing_position": [
-1.780460000038147,
-1.1214462854241507,
4.450079917907715
],
"visible_nodes": [
0,
2,
3,
4
]
},
{
"accessible_nodes": [
0,
1,
3,
4
],
"floor_index": 0,
"index": 2,
"offset_point_count": 378,
"position": [
-1.3428200483322144,
-0.0028426700737327337,
6.358610153198242
],
"quaternion": {
"w": 0.6069850728938949,
"x": -0.001073151580086956,
"y": -0.7947122170838032,
"z": -0.0006765059598702671
},
"standing_position": [
-1.3428200483322144,
-1.1225737228916606,
6.358610153198242
],
"visible_nodes": [
0,
1,
3,
4
]
},
{
"accessible_nodes": [
0,
1,
2,
4,
6,
7,
8
],
"floor_index": 0,
"index": 3,
"offset_point_count": 0,
"position": [
-2.554189920425415,
-0.005082080140709877,
6.343259811401367
],
"quaternion": {
"w": -0.030997155509175898,
"x": -0.0033666821765646494,
"y": -0.9994984128781037,
"z": -0.005546881036692224
},
"standing_position": [
-2.554189920425415,
-1.1224907918710612,
6.343259811401367
],
"visible_nodes": [
0,
1,
2,
4,
6,
7,
8
]
},
{
"accessible_nodes": [
0,
1,
2,
3,
5,
6,
7,
8,
9,
10,
11
],
"floor_index": 0,
"index": 4,
"offset_point_count": 941,
"position": [
-2.91471004486084,
-0.0003035110130440444,
0.06049229949712753
],
"quaternion": {
"w": 0.0513025987418296,
"x": -0.0012676475973127641,
"y": -0.9986823460816978,
"z": 0.00004879134915086605
},
"standing_position": [
-2.91471004486084,
-1.1184803014502747,
0.06049229949712753
],
"visible_nodes": [
0,
1,
2,
3,
5,
6,
7,
8,
9,
10,
11
]
},
{
"accessible_nodes": [
4,
6,
9,
10
],
"floor_index": 0,
"index": 5,
"offset_point_count": 863,
"position": [
-1.0017000436782837,
-0.005517140030860901,
-1.6075899600982666
],
"quaternion": {
"w": -0.0344319061946854,
"x": -0.001258810265919515,
"y": -0.9994062434209862,
"z": 0.0001571970577815824
},
"standing_position": [
-1.0017000436782837,
-1.1178040247150276,
-1.6075899600982666
],
"visible_nodes": [
4,
6,
9,
10
]
},
{
"accessible_nodes": [
0,
3,
4,
5,
7,
8
],
"floor_index": 0,
"index": 6,
"offset_point_count": 1322,
"position": [
-2.846139907836914,
-0.001211759983561933,
-3.169840097427368
],
"quaternion": {
"w": 0.32066123052403445,
"x": -0.0012332355032137405,
"y": -0.9471930957831524,
"z": -0.00029739399948980815
},
"standing_position": [
-2.846139907836914,
-1.1164889512469,
-3.169840097427368
],
"visible_nodes": [
0,
3,
4,
5,
7,
8
]
},
{
"accessible_nodes": [
0,
3,
4,
6,
8
],
"floor_index": 0,
"index": 7,
"offset_point_count": 2356,
"position": [
-3.0448501110076904,
-0.0010134399635717273,
-4.505109786987305
],
"quaternion": {
"w": 0.49087982982843503,
"x": -0.001156616652291604,
"y": -0.8712263733357108,
"z": -0.0005211039435373182
},
"standing_position": [
-3.0448501110076904,
-1.11562920091362,
-4.505109786987305
],
"visible_nodes": [
0,
3,
4,
6,
8
]
},
{
"accessible_nodes": [
0,
3,
4,
6,
7
],
"floor_index": 0,
"index": 8,
"offset_point_count": 234,
"position": [
-2.9152801036834717,
-0.01438829954713583,
-6.370659828186035
],
"quaternion": {
"w": -0.30469427695314993,
"x": 0.0033588028429806095,
"y": -0.952412964681632,
"z": -0.007724194714338225
},
"standing_position": [
-2.9152801036834717,
-1.1144715989725875,
-6.370659828186035
],
"visible_nodes": [
0,
3,
4,
6,
7
]
},
{
"accessible_nodes": [
4,
5,
10,
11,
12,
13
],
"floor_index": 0,
"index": 9,
"offset_point_count": 4886,
"position": [
-4.0423102378845215,
0.003333129920065403,
-0.03953389823436737
],
"quaternion": {
"w": -0.18957592794754674,
"x": 0.010895134657991246,
"y": -0.9818002618819436,
"z": -0.0032427031038127978
},
"standing_position": [
-4.0423102378845215,
-1.118231507556375,
-0.03953389823436737
],
"visible_nodes": [
4,
5,
10,
11,
12,
13
]
},
{
"accessible_nodes": [
4,
5,
9,
11
],
"floor_index": 0,
"index": 10,
"offset_point_count": 1773,
"position": [
-4.850550174713135,
0.004613489843904972,
0.1713390052318573
],
"quaternion": {
"w": 0.12768161943121548,
"x": -0.0012676586552423492,
"y": -0.9918143930048722,
"z": -0.00004849317962342722
},
"standing_position": [
-4.850550174713135,
-1.1182253683511887,
0.1713390052318573
],
"visible_nodes": [
4,
5,
9,
11
]
},
{
"accessible_nodes": [
4,
9,
10
],
"floor_index": 0,
"index": 11,
"offset_point_count": 654,
"position": [
-5.53085994720459,
-0.013371099717915058,
-0.143778994679451
],
"quaternion": {
"w": -0.0030183054753861976,
"x": -0.00047414000140664293,
"y": -0.9999897553992034,
"z": -0.0033406236879980804
},
"standing_position": [
-5.53085994720459,
-1.1179010210789888,
-0.143778994679451
],
"visible_nodes": [
4,
9,
10
]
},
{
"accessible_nodes": [
9,
13
],
"floor_index": 0,
"index": 12,
"offset_point_count": 1237,
"position": [
-4.178860187530518,
-0.013643399812281132,
1.3469799757003784
],
"quaternion": {
"w": 0.06683888675991313,
"x": -0.0047767000805582485,
"y": -0.9977055393311836,
"z": -0.00966420424898995
},
"standing_position": [
-4.178860187530518,
-1.1190794461899993,
1.3469799757003784
],
"visible_nodes": [
9,
13
]
},
{
"accessible_nodes": [
9,
12
],
"floor_index": 0,
"index": 13,
"offset_point_count": 466,
"position": [
-4.963950157165527,
-0.005961719900369644,
3.710700035095215
],
"quaternion": {
"w": -0.19462188396064967,
"x": 0.004454742146828046,
"y": -0.9807853643462634,
"z": -0.012749200565824454
},
"standing_position": [
-4.963950157165527,
-1.12043610464341,
3.710700035095215
],
"visible_nodes": [
9,
12
]
}
],
"panorama": {
"count": 14,
"list": [
{
"back": "images/cube_2048/0/9bf403506f2bc22eea0a81cdb09a8e22/0_b.jpg",
"down": "images/cube_2048/0/9bf403506f2bc22eea0a81cdb09a8e22/0_d.jpg",
"front": "images/cube_2048/0/9bf403506f2bc22eea0a81cdb09a8e22/0_f.jpg",
"index": 0,
"left": "images/cube_2048/0/9bf403506f2bc22eea0a81cdb09a8e22/0_l.jpg",
"right": "images/cube_2048/0/9bf403506f2bc22eea0a81cdb09a8e22/0_r.jpg",
"up": "images/cube_2048/0/9bf403506f2bc22eea0a81cdb09a8e22/0_u.jpg"
},
{
"back": "images/cube_2048/1/cb8db51bc30afdd40cf6e4f3f7341c40/1_b.jpg",
"down": "images/cube_2048/1/cb8db51bc30afdd40cf6e4f3f7341c40/1_d.jpg",
"front": "images/cube_2048/1/cb8db51bc30afdd40cf6e4f3f7341c40/1_f.jpg",
"index": 1,
"left": "images/cube_2048/1/cb8db51bc30afdd40cf6e4f3f7341c40/1_l.jpg",
"right": "images/cube_2048/1/cb8db51bc30afdd40cf6e4f3f7341c40/1_r.jpg",
"up": "images/cube_2048/1/cb8db51bc30afdd40cf6e4f3f7341c40/1_u.jpg"
},
{
"back": "images/cube_2048/2/67cdadb9caa0f844f56fee0aac7bb56d/2_b.jpg",
"down": "images/cube_2048/2/67cdadb9caa0f844f56fee0aac7bb56d/2_d.jpg",
"front": "images/cube_2048/2/67cdadb9caa0f844f56fee0aac7bb56d/2_f.jpg",
"index": 2,
"left": "images/cube_2048/2/67cdadb9caa0f844f56fee0aac7bb56d/2_l.jpg",
"right": "images/cube_2048/2/67cdadb9caa0f844f56fee0aac7bb56d/2_r.jpg",
"up": "images/cube_2048/2/67cdadb9caa0f844f56fee0aac7bb56d/2_u.jpg"
},
{
"back": "images/cube_2048/3/791c38edaf6bbcc16e19682543745e9b/3_b.jpg",
"down": "images/cube_2048/3/791c38edaf6bbcc16e19682543745e9b/3_d.jpg",
"front": "images/cube_2048/3/791c38edaf6bbcc16e19682543745e9b/3_f.jpg",
"index": 3,
"left": "images/cube_2048/3/791c38edaf6bbcc16e19682543745e9b/3_l.jpg",
"right": "images/cube_2048/3/791c38edaf6bbcc16e19682543745e9b/3_r.jpg",
"up": "images/cube_2048/3/791c38edaf6bbcc16e19682543745e9b/3_u.jpg"
},
{
"back": "images/cube_2048/4/ee318d536c51ef3de38c4aa059d65ef0/4_b.jpg",
"down": "images/cube_2048/4/ee318d536c51ef3de38c4aa059d65ef0/4_d.jpg",
"front": "images/cube_2048/4/ee318d536c51ef3de38c4aa059d65ef0/4_f.jpg",
"index": 4,
"left": "images/cube_2048/4/ee318d536c51ef3de38c4aa059d65ef0/4_l.jpg",
"right": "images/cube_2048/4/ee318d536c51ef3de38c4aa059d65ef0/4_r.jpg",
"up": "images/cube_2048/4/ee318d536c51ef3de38c4aa059d65ef0/4_u.jpg"
},
{
"back": "images/cube_2048/5/b19d98dc23ec04d86df755dd1a15fdb3/5_b.jpg",
"down": "images/cube_2048/5/b19d98dc23ec04d86df755dd1a15fdb3/5_d.jpg",
"front": "images/cube_2048/5/b19d98dc23ec04d86df755dd1a15fdb3/5_f.jpg",
"index": 5,
"left": "images/cube_2048/5/b19d98dc23ec04d86df755dd1a15fdb3/5_l.jpg",
"right": "images/cube_2048/5/b19d98dc23ec04d86df755dd1a15fdb3/5_r.jpg",
"up": "images/cube_2048/5/b19d98dc23ec04d86df755dd1a15fdb3/5_u.jpg"
},
{
"back": "images/cube_2048/6/b8f425e5f8943c38ad1386ab391b54bc/6_b.jpg",
"down": "images/cube_2048/6/b8f425e5f8943c38ad1386ab391b54bc/6_d.jpg",
"front": "images/cube_2048/6/b8f425e5f8943c38ad1386ab391b54bc/6_f.jpg",
"index": 6,
"left": "images/cube_2048/6/b8f425e5f8943c38ad1386ab391b54bc/6_l.jpg",
"right": "images/cube_2048/6/b8f425e5f8943c38ad1386ab391b54bc/6_r.jpg",
"up": "images/cube_2048/6/b8f425e5f8943c38ad1386ab391b54bc/6_u.jpg"
},
{
"back": "images/cube_2048/7/a05b06a01171babb1aceb5aad8406ab8/7_b.jpg",
"down": "images/cube_2048/7/a05b06a01171babb1aceb5aad8406ab8/7_d.jpg",
"front": "images/cube_2048/7/a05b06a01171babb1aceb5aad8406ab8/7_f.jpg",
"index": 7,
"left": "images/cube_2048/7/a05b06a01171babb1aceb5aad8406ab8/7_l.jpg",
"right": "images/cube_2048/7/a05b06a01171babb1aceb5aad8406ab8/7_r.jpg",
"up": "images/cube_2048/7/a05b06a01171babb1aceb5aad8406ab8/7_u.jpg"
},
{
"back": "images/cube_2048/8/2cd717b9c97f48ae79511855cc7e5138/8_b.jpg",
"down": "images/cube_2048/8/2cd717b9c97f48ae79511855cc7e5138/8_d.jpg",
"front": "images/cube_2048/8/2cd717b9c97f48ae79511855cc7e5138/8_f.jpg",
"index": 8,
"left": "images/cube_2048/8/2cd717b9c97f48ae79511855cc7e5138/8_l.jpg",
"right": "images/cube_2048/8/2cd717b9c97f48ae79511855cc7e5138/8_r.jpg",
"up": "images/cube_2048/8/2cd717b9c97f48ae79511855cc7e5138/8_u.jpg"
},
{
"back": "images/cube_2048/9/0b634fe985df7011472b81c75babfd95/9_b.jpg",
"down": "images/cube_2048/9/0b634fe985df7011472b81c75babfd95/9_d.jpg",
"front": "images/cube_2048/9/0b634fe985df7011472b81c75babfd95/9_f.jpg",
"index": 9,
"left": "images/cube_2048/9/0b634fe985df7011472b81c75babfd95/9_l.jpg",
"right": "images/cube_2048/9/0b634fe985df7011472b81c75babfd95/9_r.jpg",
"up": "images/cube_2048/9/0b634fe985df7011472b81c75babfd95/9_u.jpg"
},
{
"back": "images/cube_2048/10/40c1a6463b8c7462aadfca2a5f80f685/10_b.jpg",
"down": "images/cube_2048/10/40c1a6463b8c7462aadfca2a5f80f685/10_d.jpg",
"front": "images/cube_2048/10/40c1a6463b8c7462aadfca2a5f80f685/10_f.jpg",
"index": 10,
"left": "images/cube_2048/10/40c1a6463b8c7462aadfca2a5f80f685/10_l.jpg",
"right": "images/cube_2048/10/40c1a6463b8c7462aadfca2a5f80f685/10_r.jpg",
"up": "images/cube_2048/10/40c1a6463b8c7462aadfca2a5f80f685/10_u.jpg"
},
{
"back": "images/cube_2048/11/9663b7885a119f2f06da06212986bc9b/11_b.jpg",
"down": "images/cube_2048/11/9663b7885a119f2f06da06212986bc9b/11_d.jpg",
"front": "images/cube_2048/11/9663b7885a119f2f06da06212986bc9b/11_f.jpg",
"index": 11,
"left": "images/cube_2048/11/9663b7885a119f2f06da06212986bc9b/11_l.jpg",
"right": "images/cube_2048/11/9663b7885a119f2f06da06212986bc9b/11_r.jpg",
"up": "images/cube_2048/11/9663b7885a119f2f06da06212986bc9b/11_u.jpg"
},
{
"back": "images/cube_2048/12/01f84f2edd8642d0bf5e4874d01a28ac/12_b.jpg",
"down": "images/cube_2048/12/01f84f2edd8642d0bf5e4874d01a28ac/12_d.jpg",
"front": "images/cube_2048/12/01f84f2edd8642d0bf5e4874d01a28ac/12_f.jpg",
"index": 12,
"left": "images/cube_2048/12/01f84f2edd8642d0bf5e4874d01a28ac/12_l.jpg",
"right": "images/cube_2048/12/01f84f2edd8642d0bf5e4874d01a28ac/12_r.jpg",
"up": "images/cube_2048/12/01f84f2edd8642d0bf5e4874d01a28ac/12_u.jpg"
},
{
"back": "images/cube_2048/13/e9ce1249af9000a33d566c653560c3d6/13_b.jpg",
"down": "images/cube_2048/13/e9ce1249af9000a33d566c653560c3d6/13_d.jpg",
"front": "images/cube_2048/13/e9ce1249af9000a33d566c653560c3d6/13_f.jpg",
"index": 13,
"left": "images/cube_2048/13/e9ce1249af9000a33d566c653560c3d6/13_l.jpg",
"right": "images/cube_2048/13/e9ce1249af9000a33d566c653560c3d6/13_r.jpg",
"up": "images/cube_2048/13/e9ce1249af9000a33d566c653560c3d6/13_u.jpg"
}
]
},
"picture_url": "https://vr-public.realsee-cdn.cn/release/auto3dhd/9380ce27b46cbc5ba8d47fcf0e4c9e4f/screenshot/1596005211_4/pc0_3aoiit6zp.jpg",
"title_picture_url": "https://vr-public.realsee-cdn.cn/release/auto3dhd/9380ce27b46cbc5ba8d47fcf0e4c9e4f/screenshot/1596005211_4/pc1_iQLf2N6a6_1.jpg",
"vr_code": "8054djM0oR6ky3Ey8e"
}
-
_signature / certificate: work 内容的数字签名
-
expire_at: work 的过期时间
-
allow_hosts: work 的安全域名
-
initial: 初始化数据,是一个 State 数据。描述 Work 被加载初始状态的位姿,也叫做VR的初始视角
- mode: 模态
- pano_index: 初始化点位
- longitude: 相机的水平角
- latitude: 相机的偏航角
- fov: 相机垂直方向的可视角度
-
model: 三维模型
- file_url: 三维模型的资源地址,文件为 .at3d 为如视定制的模型格式
- material_textures: 三维模型的贴图资源地址
-
panorama: 全景彩色信息
- list:
- up / down / left / right / front / back: 全景彩色信息以 cubemap 方式存储和使用。
- list:
-
observers: 采集点信息
- visible_nodes: 采集点之间的可见性列表
- accessible_nodes: 采集点之间的连通性列表
- quaternion: 采集点与模型坐标的旋转偏移量
- standing_position: 采集点地面坐标
- position: 采集点坐标
- floor_index: 采集点楼层
Work 对象
相比上面的 work.json, 你可能更常用到的是 Work 对象,他是 parseWork
函数的产物。
Work 对象 相比 work.json 会做很好的 序列化,你可以在 Five API 文档 中查看详细说明。
了解 FiveProvider 及 FiveCanvas
FiveProvider 及 FiveCanvas 的由来
FiveProvider
/ FiveCanvas
是通过 React Context API 的思路开发的。
我们可以认为 FiveProvider
创建了一个三维空间实例。而 FiveCanvas
是对应显示到 DOM 结构中的画布(cnavas)。在 FiveProvider
内的子组件均可以通过 context 获取到 FiveProvider
上提供的方法来获取三维空间的信息或者控制三维空间,我们还开发了一系列的 hooks, 便于你更方便的完成上述事情。
这样的文字描述可能不好理解,那么我们动手做一些看看。
添加模态控制按钮
我们这次完成的工作是添加模态控制按钮,Five 内置了多种模态,默认状态展示的是 全 景态,就和上一章最终得到的结果一样,我们还可以展示 模型态 来查看三维空间的宏观状态。切换我们会用到 Five SDK 提供的 useCurrentState
的 React hooks。关于 模态,State, useCurrentState 等会在下一章详细学习到,而本章我们要做的事情是:
- 编写一个 React 组件
- 将组件安插为 FiveProvider 的子组件
- 通过 Five 提供的 api 控制三维空间
安装 ui 组件
为了减少示例代码复杂度,这边使用 material-UI 作为 UI 组件库, 你也可以使用 Ant Design Fluent UI 等。
npm install @mui/material @mui/icons-material @emotion/react @emotion/styled
了解 withFive
import { withFive } from "@realsee/react"
是 Five SDK 提供的 React HOC。 可以通过 withFive 注入三维空间相关的属性/特征和方法。
他通过 import { createFiveFeature } from "@realsee/react"
的 createFiveFeature 方法创建需要的依赖注入后,通过 this.props.$five
即可使用。
本次我们需要注入 currentState
和 setState
来切换模态。
接下来开始编写代码
- 添加一个 ModeController 文件,用于编写组件。
- 首先在头部从
@realsee/five
引用Five
Mode
。 Mode
是模态的类型声明。Five.Mode[string]
是模态的枚举。- 首先在头部从
@realsee/five/react
引用withFive
createFiveFeature
。 - 书写
ModeController
组件。 - 通过 material-UI 组件,添加界面。
- 通过
this.props.$five.currentState
this.props.$five.setState()
可以获取/设置 三维空间的模态(mode)。
- JavaScript
- TypeScript
import React, { Component } from "react";
import { Five } from "@realsee/five";
import { withFive, createFiveFeature } from "@realsee/five/react";
import { compose } from "@wordpress/compose";
import BottomNavigation from "@mui/material/BottomNavigation";
import BottomNavigationAction from "@mui/material/BottomNavigationAction";
import Paper from "@mui/material/Paper";
import DirectionsWalkIcon from "@mui/icons-material/DirectionsWalk";
import ViewInArIcon from "@mui/icons-material/ViewInAr";
const FEATURES = createFiveFeature("currentState", "setState");
/**
* React Component: 模态控制
*/
const ModeController = compose(
withFive(FEATURES)
)(class extends Component {
render() {
return <Paper sx={{ position: "fixed", bottom: 0, left: 0, right: 0 }}>
<BottomNavigation
showLabels
value={this.props.$five.currentState.mode}
onChange={(_, newValue) => {
this.props.$five.setState({ mode: newValue });
}}
>
<BottomNavigationAction label="全景漫游" icon={<DirectionsWalkIcon/>} value={Five.Mode.Panorama}/>
<BottomNavigationAction label="空间总览" icon={<ViewInArIcon/>} value={Five.Mode.Floorplan}/>
</BottomNavigation>
</Paper>;
}
})
export { ModeController };
import React, { Component } from "react";
import { Five, Mode } from "@realsee/five";
import { withFive, createFiveFeature, PropTypeOfFiveFeatures } from "@realsee/five/react";
import { compose } from "@wordpress/compose";
import BottomNavigation from "@mui/material/BottomNavigation";
import BottomNavigationAction from "@mui/material/BottomNavigationAction";
import Paper from "@mui/material/Paper";
import DirectionsWalkIcon from "@mui/icons-material/DirectionsWalk";
import ViewInArIcon from "@mui/icons-material/ViewInAr";
const FEATURES = createFiveFeature("currentState", "setState");
type Props = PropTypeOfFiveFeatures<typeof FEATURES>;
/**
* React Component: 模态控制
*/
const ModeController = compose(
withFive(FEATURES)
)(class extends Component<Props> {
render() {
return <Paper sx={{ position: "fixed", bottom: 0, left: 0, right: 0 }}>
<BottomNavigation
showLabels
value={this.props.$five.currentState.mode}
onChange={(_, newValue: Mode) => {
this.props.$five.setState({ mode: newValue });
}}
>
<BottomNavigationAction label="全景漫游" icon={<DirectionsWalkIcon/>} value={Five.Mode.Panorama}/>
<BottomNavigationAction label="空间总览" icon={<ViewInArIcon/>} value={Five.Mode.Floorplan}/>
</BottomNavigation>
</Paper>;
}
})
export { ModeController };
使用模态控制按钮组件
接下来将 ModeController
组件插入到 App 文件的 FiveProvider 中。
- JavaScript
- TypeScript
import React, { Component } from "react";
import { compose } from "@wordpress/compose";
import { createFiveProvider, FiveCanvas } from "@realsee/five/react";
import { withFetchWork } from "./withFetchWork";
import { withWindowDimensions } from "./withWindowDimensions";
//highlight-start
import { ModeController } from "./ModeController";
//highlight-end
/** work.json 的数据 URL */
const workURL = "https://vr-public.realsee-cdn.cn/release/static/image/release/five/work-sample/07bdc58f413bc5494f05c7cbb5cbdce4/work.json";
const FiveProvider = createFiveProvider();
const App = compose(
withFetchWork(workURL),
withWindowDimensions()
)(class extends Component {
render() {
const { work, windowDimensions } = this.props;
return <FiveProvider initialWork={work}>
<FiveCanvas width={windowDimensions.width} height={windowDimensions.height}/>
//highlight-start
<ModeController/>;
//highlight-end
</FiveProvider>;
}
});
export { App };
import React, { Component } from "react";
import { Work } from "@realsee/five";
import { createFiveProvider, FiveCanvas } from "@realsee/five/react";
import { compose } from "@wordpress/compose";
import { withFetchWork } from "./withFetchWork";
import { withWindowDimensions } from "./withWindowDimensions";
//highlight-start
import { ModeController } from "./ModeController";
//highlight-end
/** work.json 的数据 URL */
const workURL = "https://vr-public.realsee-cdn.cn/release/static/image/release/five/work-sample/07bdc58f413bc5494f05c7cbb5cbdce4/work.json";
const FiveProvider = createFiveProvider();
const App = compose(
withFetchWork(workURL),
withWindowDimensions()
)(class extends Component<{work: Work, windowDimensions: { width: number, height: number }}> {
render() {
const { work, windowDimensions } = this.props;
return <FiveProvider initialWork={work}>
<FiveCanvas width={windowDimensions.width} height={windowDimensions.height}/>
//highlight-start
<ModeController/>
//highlight-end
</FiveProvider>;
}
});
export { App };
回到你的浏览器查看,会发现你的页面底部出现了控制条,点击不同的按钮 全景漫游 空间总览 可以让三维空间改变状态。
真不错 🥳 !
下一章节你会学到
- 了解什么是 State
- 如何改变三维空间观察的方向 / 位置
- 了解刚才的代码,比如
currentState
setState
及其他响应的是如何工作的 - 通过 State 完成了自动环视的功能