Change View
Previous chapter recalls: Show 3D space
- You know what you are Work, and how to get and load.
- How to display three-dimensional spaces and develop components again to control three-dimensional space.
- Learn what is State.
- How to change the directions/position of 3D space observation.
- Learn how to work with the code of the previous chapter, e.g.
currentState
setState
and others. - Autoring features via State.
Preparatory work
Like the previous section, we create a new directory (src/2.knowing-state
and corresponding html and jsx or tsx files.
jsx or tsx files can first copy the previous chapter.
<!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>Shift view | Klowing state</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/2.knowing-state/withFetchWork.jsx
import { useState, useEffect } from "react";
import { parseWork } from "@realsee/five";
/**
* React Hook: via work .json address get work object
* @param url work.json data address
* @returns work object, if getting, return 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/2.knowing-state/withWindowDimensions.js
import React, { Component } from "react";
/**
* React HOC: Get the size of the current window
*/
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/2.knowing-state/ModeController.jsx
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: Modal Control
*/
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="Panorama roaming" icon={<DirectionsWalkIcon/>} value={Five.Mode.Panorama}/>
<BottomNavigationAction label="Space overview" icon={<ViewInArIcon/>} value={Five.Mode.Floorplan}/>
</BottomNavigation>
</Paper>;
}
})
export { ModeController };
src/2.knowing-state/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";
import { ModeController } from "./ModeController";
/** Data URL of work.json */
const workURL = "https://vrlab-public.ljcdn.com/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}/>
<ModeController/>;
</FiveProvider>;
}
});
export { App };
src/2.knowing-state/index.jsx
import React from "react";
import ReactDOM from "react-dom";
import { App } from "./App";
ReactDOM.render(<App/>, document.querySelector("#app"));
export {};
src/2.knowing-state/withFetchWork.tsx
import React, { Component, ComponentClass } from "react";
import { Work, parseWork } from "@realsee/five";
/**
* React HOC get work
* @param url work. Son address
*/
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/2.knowing-state/useWindowDimensions.tsx
import React, { Component, ComponentClass } from "react";
/**
* React HOC: Get the current window size
*/
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/2.knowing-state/ModeController.tsx
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: Model Control
*/
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="Panorama roaming" icon={<DirectionsWalkIcon/>} value={Five.Mode.Panorama}/>
<BottomNavigationAction label="Space overview" icon={<ViewInArIcon/>} value={Five.Mode.Floorplan}/>
</BottomNavigation>
</Paper>;
}
})
export { ModeController };
src/2.knowing-state/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";
import { ModeController } from "./ModeController";
/** Data URL of work.json */
const workURL = "https://vrlab-public.ljcdn.com/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}/>
<ModeController/>
</FiveProvider>;
}
});
export { App };
src/2.knowing-state/index.tsx
import React from "react";
import ReactDOM from "react-dom";
import { App } from "./App";
ReactDOM.render(<App/>, document.querySelector("#app"));
export {};
Start service npm run dev
and jump to the current page "http://localhost:3000/src/2.knowing-state/index.html".
Please see your console. The port number will change due to your configuration and current port occupancy, please check the console output. If you use another development build tool, please start the service as required by your own development build tool.
What is State
Having come to understand the concept, I assure you that you are only the last theory that needs to be known at this stage.
State Introduction
State data structure used to describe status.The previous chapter knows Work, Work is used to describe a three-dimensional space, then State is a description of what is currently in this three-dimensional space.He contains modelling, where the collection point is located, the direction of the camera, and the view of the camera.
State Data Structure and Description
interface State {
"mode": Five.Mode,
"panoIndex": number,
"longitude": number,
"latitude": number,
"fov": number,
"offset": THEEE.Vector3
}
Data description for State
mode
: Current mode common to 5 models that can be obtained usingFive.Mode
- Panorama: Panorama: Panoramic Walking Model, under which views will travel between collection points. Gestures can rotation/amplify views/toggle collection points to view the collected landscape information.
- Floorplan: Space Overview Model. The view under this model is model-centric, gesture can rotate/magnify models/toggle the floor to view the model as a whole.
- Topview: Household diagram mode where the view is centered on the model, vertical pitch model, gesture can shift/zoom in the floor/toggle the floor of the model to view the schema structure.
- Model: Modeling Model, where the view will travel freely in the model, gesture can rotate/amplify views/bits, fit to view the details of the model and make some locational actions.
- VR Panorama: VR glasses model that enables VR virtual display using Cardboard glass or his third-party derivative product.
panoIndex
: Gathering point positions, where Panorama models can be set up.is an index ofwork[observers]
.longitude
/latitude
: Horizontal corner of the camera/camera (radians), we describe the position of the camera in a latitude similar way.The whole model scenario is a right-hand cartex coordinate system,
XZ
plane parallel to the ground,Y
axis perpendicular to the ground.Initial camera orientation looks at Z negative direction
- Add
longitude
camera rotate left. - Decrease
longitude
camera rotate right. - Add
latitude
camera rotate down. - Reduces the rotation of
latitude
cameras.
- Add
fov
: Visual angle (angle) in the vertical direction of camera.offset
: three-dimensional coordinates of the current camera.
What is associated with the API
The current state can be obtained by state
currentState
, set by setState
setCurrentState
State / currentState Distinction
currentState is the current state, the state in the picture, the state that is currently shown. state is targeted or stable at the next time.
It can be simply thought of as:
When setSate is called,state will immediately become the value of setState , while currentState will not change immediately, it will gradually approach during the animation transition process state and eventually becomes the same value as state.Just like the animation you see in the screen.
In the example of the last chapter, we have used mode properties to switch to Panorama and Floorplan patterns.You can also try to add some of the other models and see how different they are.
VRPanorama moderequires device gyroscopic information and mobile devices. And if the service is iOS device, it needs to be
https
or iOS does not allow access to Gychrome information.
Development of an auto-ring feature
We've fetched and set mode, this time we've modified longitude / latitude.This time we develop an auto-look feature.Use a button to control the function of activating auto-ring viewing, which will rotate the camera horizontally.
Writing ring Component
- Add a LookAroundController file to write components.
- Designing active React state to control whether or not React state features are enabled for display of buttons.
- The ring function is implemented with:triggering function via setInterval, modifying the five state.
- JavaScript
- TypeScript
import React, { Component } from "react";
import { withFive, createFiveFeature } from "@realsee/five/react";
import { compose } from "@wordpress/compose";
import IconButton from "@mui/material/IconButton";
import Paper from "@mui/material/Paper";
import FlipCameraAndroidIcon from "@mui/icons-material/FlipCameraAndroid";
import PauseIcon from "@mui/icons-material/Pause";
const FEATURES = createFiveFeature("currentState", "setState");
/**
* ReactComponent: Automatic lookaround button
*/
const LookAroundController = compose(
withFive(FEATURES)
)(class extends Component {
timer;
state = { active: false };
toggleActive(active) {
window.clearInterval(this.timer);
this.setState({ active });
if (active === true) {
this.timer = window.setInterval(() => {
this.props.$five.setState({
longitude: this.props.$five.currentState.longitude + Math.PI / 360
});
}, 16);
} else {
delete this.timer;
}
}
render() {
return <Paper sx={{ position: "fixed", top: 10, right: 10 }}>
{this.state.active ?
<IconButton onClick={() => this.toggleActive(false)}><PauseIcon/></IconButton>:
<IconButton onClick={() => this.toggleActive(true)}><FlipCameraAndroidIcon/></IconButton>
}
</Paper>;
}
});
export { LookAroundController };
import React, { Component } from "react";
import { withFive, createFiveFeature, PropTypeOfFiveFeatures } from "@realsee/five/react";
import { compose } from "@wordpress/compose";
import IconButton from "@mui/material/IconButton";
import Paper from "@mui/material/Paper";
import FlipCameraAndroidIcon from "@mui/icons-material/FlipCameraAndroid";
import PauseIcon from "@mui/icons-material/Pause";
const FEATURES = createFiveFeature("currentState", "setState");
type Props = PropTypeOfFiveFeatures<typeof FEATURES>;
type State = { active: boolean };
/**
* ReactComponent: Automatic lookaround button
*/
const LookAroundController = compose(
withFive(FEATURES)
)(class extends Component<Props, State> {
timer?: number;
state = { active: false };
toggleActive(active: boolean) {
window.clearInterval(this.timer);
this.setState({ active });
if (active === true) {
this.timer = window.setInterval(() => {
this.props.$five.setState({
longitude: this.props.$five.currentState.longitude + Math.PI / 360
});
}, 16);
} else {
delete this.timer;
}
}
render() {
return <Paper sx={{ position: "fixed", top: 10, right: 10 }}>
{this.state.active ?
<IconButton onClick={() => this.toggleActive(false)}><PauseIcon/></IconButton>:
<IconButton onClick={() => this.toggleActive(true)}><FlipCameraAndroidIcon/></IconButton>
}
</Paper>;
}
});
export { LookAroundController };
Use Auto-Ring Component
Insert into App file GiveProvider
- 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";
import { ModeController } from "./ModeController";
import { LookAroundController } from "./LookAroundController";
/** Data URL of work.json */
const workURL = "https://vrlab-public.ljcdn.com/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}/>
<ModeController/>;
<LookAroundController/>;
</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";
import { ModeController } from "./ModeController";
import { LookAroundController } from "./LookAroundController";
/** Data URL of work.json */
const workURL = "https://vrlab-public.ljcdn.com/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}/>
<ModeController/>
<LookAroundController/>
</FiveProvider>;
}
});
export { App };
Back to your browser to view it will find a ring button in the top right corner of your page.Click to trigger and close the ring view.
It's a good feature: partying_face: !
The next section will be completed by you
- Record using State to complete user operations
- Restore user screen via State