@realsee/vreo 0.1.6 → 0.2.0-alpha.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +103 -16
- package/docs/assets/custom.css +4 -4
- package/docs/assets/search.js +1 -1
- package/docs/classes/Player.Controller.html +54 -0
- package/docs/classes/Player.Player-1.html +11 -11
- package/docs/classes/Player.VideoAgentMesh.html +142 -0
- package/docs/classes/Player.VideoAgentScene.html +1 -0
- package/docs/enums/Player.InfoPanelStyleEnum.html +1 -0
- package/docs/enums/Player.InfoPanelTypeEnum.html +1 -1
- package/docs/enums/Player.PanoEffectEnum.html +2 -2
- package/docs/enums/Player.PanoTagEnum.html +1 -1
- package/docs/enums/Player.VreoKeyframeEnum.html +14 -12
- package/docs/enums/fivePlugins.CameraMovementEffect.html +1 -1
- package/docs/enums/fivePlugins.Rotation.html +1 -1
- package/docs/index.html +52 -19
- package/docs/interfaces/Player.CustomVreoKeyframeProps.html +1 -0
- package/docs/interfaces/Player.PlayerConfigs.html +4 -2
- package/docs/interfaces/Player.Vertex.html +1 -1
- package/docs/interfaces/Player.VideoAgentMeshOptions.html +1 -0
- package/docs/interfaces/Player.VreoKeyframe.html +1 -1
- package/docs/interfaces/Player.VreoUnit.html +1 -1
- package/docs/interfaces/Player.VreoVideo.html +1 -1
- package/docs/interfaces/fivePlugins.CameraMovementOptsCallback.html +1 -1
- package/docs/interfaces/fivePlugins.CameraMovementPluginExportType.html +3 -3
- package/docs/interfaces/fivePlugins.CameraMovementPluginParameterType.html +1 -1
- package/docs/interfaces/fivePlugins.ModelTVVideoPluginData.html +1 -1
- package/docs/interfaces/fivePlugins.ModelTVVideoPluginExportType.html +1 -1
- package/docs/interfaces/fivePlugins.ModelTVVideoPluginParameterType.html +1 -1
- package/docs/interfaces/react.VreoActionCallbacks.html +15 -7
- package/docs/interfaces/react.VreoProviderProps.html +1 -0
- package/docs/modules/Player.html +17 -15
- package/docs/modules/custom.html +3 -0
- package/docs/modules/fivePlugins.html +2 -2
- package/docs/modules/react.html +1 -1
- package/docs/modules.html +1 -1
- package/lib/PlayController/createHTMLAudioElement.d.ts +1 -0
- package/lib/PlayController/createHTMLAudioElement.js +47 -0
- package/lib/PlayController/index.d.ts +38 -0
- package/lib/PlayController/index.js +300 -0
- package/lib/Player/App.js +8 -3
- package/lib/Player/Controller.d.ts +2 -0
- package/lib/Player/Controller.js +14 -0
- package/lib/Player/custom/SpatialScenePanel/index.d.ts +22 -0
- package/lib/Player/custom/SpatialScenePanel/index.js +279 -0
- package/lib/Player/index.d.ts +1 -1
- package/lib/Player/index.js +98 -11
- package/lib/Player/modules/Drawer/index.js +1 -1
- package/lib/Player/modules/PopUp/index.d.ts +2 -0
- package/lib/Player/modules/PopUp/index.js +43 -0
- package/lib/Player/modules/VideoAgent/VideoAgentMesh.d.ts +1 -0
- package/lib/Player/modules/VideoAgent/VideoAgentMesh.js +6 -2
- package/lib/Player/modules/VideoAgent/VideoAgentScene.d.ts +4 -1
- package/lib/Player/modules/VideoAgent/VideoAgentScene.js +21 -6
- package/lib/Player/modules/VideoAgent/index.d.ts +2 -0
- package/lib/Player/modules/VideoAgent/index.js +1 -1
- package/lib/Player/modules/keyframes/BgMusic/index.d.ts +2 -0
- package/lib/Player/modules/keyframes/BgMusic/index.js +76 -0
- package/lib/Player/modules/keyframes/CameraMovement/index.js +41 -5
- package/lib/Player/modules/keyframes/InfoPanel/index.js +105 -23
- package/lib/Player/modules/keyframes/ModelVideo/index.js +47 -10
- package/lib/Player/modules/keyframes/PanoTextLabel/index.js +4 -12
- package/lib/Player/modules/keyframes/Prompter/index.js +4 -18
- package/lib/Player/modules/keyframes/UpdateVRPanorama/index.js +71 -12
- package/lib/Player/typings.d.ts +19 -1
- package/lib/fivePlugins/CameraMovementPlugin/index.js +104 -90
- package/lib/fivePlugins/CameraMovementPlugin/typings.d.ts +1 -0
- package/lib/react/index.d.ts +10 -2
- package/lib/react/index.js +1 -1
- package/lib/shared-utils/createTransMatrix.d.ts +1 -0
- package/lib/shared-utils/createTransMatrix.js +43 -0
- package/lib/typings/VreoUnit.d.ts +47 -7
- package/lib/typings/VreoUnit.js +15 -6
- package/package.json +6 -6
- package/resources/PlayController/createHTMLAudioElement.ts +53 -0
- package/resources/PlayController/index.ts +167 -0
- package/resources/Player/App.tsx +6 -1
- package/resources/Player/Controller.ts +12 -0
- package/resources/Player/custom/SpatialScenePanel/index.tsx +264 -0
- package/resources/Player/index.tsx +56 -2
- package/resources/Player/modules/Drawer/index.tsx +1 -1
- package/resources/Player/modules/PopUp/index.tsx +26 -0
- package/resources/Player/modules/VideoAgent/VideoAgentMesh.ts +5 -4
- package/resources/Player/modules/VideoAgent/VideoAgentScene.ts +11 -2
- package/resources/Player/modules/VideoAgent/index.tsx +3 -2
- package/resources/Player/modules/keyframes/BgMusic/index.tsx +66 -0
- package/resources/Player/modules/keyframes/CameraMovement/index.tsx +7 -3
- package/resources/Player/modules/keyframes/InfoPanel/index.tsx +109 -11
- package/resources/Player/modules/keyframes/ModelVideo/index.tsx +38 -5
- package/resources/Player/modules/keyframes/PanoTextLabel/index.tsx +2 -2
- package/resources/Player/modules/keyframes/Prompter/index.tsx +5 -15
- package/resources/Player/modules/keyframes/UpdateVRPanorama/index.tsx +75 -12
- package/resources/Player/typings.ts +21 -1
- package/resources/fivePlugins/CameraMovementPlugin/index.ts +59 -48
- package/resources/fivePlugins/CameraMovementPlugin/typings.ts +2 -1
- package/resources/react/index.tsx +12 -3
- package/resources/shared-utils/createTransMatrix.ts +12 -0
- package/resources/typings/VreoUnit.ts +56 -7
- package/stylesheets/custom/SpatialScenePanel.css +309 -0
- package/stylesheets/default.css +127 -9
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
import { Subscribe } from '@realsee/five'
|
|
2
|
+
import { requestAnimationFrameInterval } from '../shared-utils/animationFrame'
|
|
3
|
+
import { VreoKeyframe, VreoKeyframeEvent, VreoUnit } from '../typings/VreoUnit'
|
|
4
|
+
import { createHTMLAudioElement } from './createHTMLAudioElement'
|
|
5
|
+
|
|
6
|
+
export interface PlayControllerConfig {
|
|
7
|
+
audio?: HTMLAudioElement
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const closures: Record<string, any> = {}
|
|
11
|
+
|
|
12
|
+
export class PlayController extends Subscribe<VreoKeyframeEvent> {
|
|
13
|
+
|
|
14
|
+
vreoUnit?: VreoUnit
|
|
15
|
+
|
|
16
|
+
config: PlayControllerConfig = {}
|
|
17
|
+
stopInterval: () => void
|
|
18
|
+
|
|
19
|
+
get paused() {
|
|
20
|
+
return this.audioInstance.paused
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
get audioInstance() {
|
|
24
|
+
return this.config.audio!
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
get currentTime() {
|
|
28
|
+
return this.audioInstance.currentTime * 1000
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
get currentKeyframes() {
|
|
32
|
+
if (!this.vreoUnit) return []
|
|
33
|
+
const keyframes = this.vreoUnit.keyframes
|
|
34
|
+
// 没有被解析过且开始时间低于当前时间戳 100ms
|
|
35
|
+
return keyframes.filter((keyframe: VreoKeyframe) => {
|
|
36
|
+
if (keyframe.parsed) return false
|
|
37
|
+
const dur = this.currentTime - keyframe.start
|
|
38
|
+
return dur <= 100 && dur >= 0
|
|
39
|
+
})
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* 音频时长 不一定能获取得到,建议以 VreoUnit 配置的时长为准。
|
|
44
|
+
*/
|
|
45
|
+
get duration() {
|
|
46
|
+
return this.audioInstance.duration * 1000
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
constructor(config: PlayControllerConfig = {}) {
|
|
50
|
+
super()
|
|
51
|
+
|
|
52
|
+
if (!config.audio) {
|
|
53
|
+
config.audio = createHTMLAudioElement()
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
this.config = Object.assign(this.config, config)
|
|
57
|
+
|
|
58
|
+
closures.onPlaying = () => this.emit('playing')
|
|
59
|
+
closures.onPaused = () => this.emit('paused')
|
|
60
|
+
closures.onEnded = () => this.emit('paused', true)
|
|
61
|
+
|
|
62
|
+
this.audioInstance.addEventListener('play', closures.onPlaying)
|
|
63
|
+
this.audioInstance.addEventListener('pause', closures.onPaused)
|
|
64
|
+
this.audioInstance.addEventListener('ended', closures.onEnded)
|
|
65
|
+
|
|
66
|
+
this.stopInterval = requestAnimationFrameInterval(() => {
|
|
67
|
+
if (this.audioInstance.paused) {
|
|
68
|
+
return
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
if (!this.vreoUnit) {
|
|
72
|
+
return
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const keyframes = this.vreoUnit.keyframes
|
|
76
|
+
const currentTime = this.currentTime
|
|
77
|
+
// 没有被解析过且开始时间低于当前时间戳 100ms
|
|
78
|
+
const currentKeyframes = keyframes.filter((keyframe: VreoKeyframe) => {
|
|
79
|
+
if (keyframe.parsed) return false
|
|
80
|
+
|
|
81
|
+
const dur = currentTime - keyframe.start
|
|
82
|
+
return dur <= 60 && dur >= 0
|
|
83
|
+
})
|
|
84
|
+
|
|
85
|
+
currentKeyframes.forEach((keyframe) => {
|
|
86
|
+
if (keyframe.parsed) return
|
|
87
|
+
keyframe.parsed = true
|
|
88
|
+
this.emit(keyframe.type, keyframe)
|
|
89
|
+
})
|
|
90
|
+
})
|
|
91
|
+
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* 载入数据
|
|
98
|
+
*/
|
|
99
|
+
async load(vreoUnit: VreoUnit): Promise<boolean> {
|
|
100
|
+
this.vreoUnit = vreoUnit
|
|
101
|
+
|
|
102
|
+
await this.pause()
|
|
103
|
+
return await new Promise((resolve, reject) => {
|
|
104
|
+
if (vreoUnit.video.url === this.audioInstance.src) {
|
|
105
|
+
return resolve(true)
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
this.audioInstance.src = vreoUnit.video.url
|
|
109
|
+
const canplaythrough = () => {
|
|
110
|
+
resolve(true)
|
|
111
|
+
this.audioInstance.removeEventListener('canplaythrough', canplaythrough)
|
|
112
|
+
}
|
|
113
|
+
const error = (err: any) => {
|
|
114
|
+
reject(err)
|
|
115
|
+
this.audioInstance.removeEventListener('error', error)
|
|
116
|
+
}
|
|
117
|
+
this.audioInstance.addEventListener('canplaythrough', canplaythrough)
|
|
118
|
+
this.audioInstance.addEventListener('error', error)
|
|
119
|
+
})
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* 播放
|
|
124
|
+
*/
|
|
125
|
+
async play() {
|
|
126
|
+
closures.playPromise = this.audioInstance.play()
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* 暂停
|
|
131
|
+
*/
|
|
132
|
+
async pause() {
|
|
133
|
+
if (closures.playPromise) {
|
|
134
|
+
await closures.playPromise
|
|
135
|
+
}
|
|
136
|
+
this.audioInstance.pause()
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* 销毁资源
|
|
141
|
+
* @notice 一旦销毁,创建的实例将不可用:需重新创建实例。
|
|
142
|
+
*
|
|
143
|
+
* @deprecated **慎重执行**
|
|
144
|
+
*/
|
|
145
|
+
dispose() {
|
|
146
|
+
// 清理事件监听
|
|
147
|
+
this.audioInstance.removeEventListener('play', closures.onPlaying)
|
|
148
|
+
this.audioInstance.removeEventListener('pasue', closures.onPaused)
|
|
149
|
+
this.audioInstance.removeEventListener('ended', closures.onEnded)
|
|
150
|
+
if (this.stopInterval) {
|
|
151
|
+
this.stopInterval()
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
console.log(`
|
|
157
|
+
|
|
158
|
+
┏━━━┓━━━━━━━━━┏┓━━━━━━━━━━━━━
|
|
159
|
+
┃┏━┓┃━━━━━━━━━┃┃━━━━━━━━━━━━━
|
|
160
|
+
┃┗━┛┃┏━━┓┏━━┓━┃┃━┏━━┓┏━━┓┏━━┓
|
|
161
|
+
┃┏┓┏┛┃┏┓┃┗━┓┃━┃┃━┃━━┫┃┏┓┃┃┏┓┃
|
|
162
|
+
┃┃┃┗┓┃┃━┫┃┗┛┗┓┃┗┓┣━━┃┃┃━┫┃┃━┫
|
|
163
|
+
┗┛┗━┛┗━━┛┗━━━┛┗━┛┗━━┛┗━━┛┗━━┛
|
|
164
|
+
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
165
|
+
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
166
|
+
|
|
167
|
+
`)
|
package/resources/Player/App.tsx
CHANGED
|
@@ -2,7 +2,7 @@ import * as React from 'react'
|
|
|
2
2
|
import classNames from 'classnames'
|
|
3
3
|
import { observer } from 'mobx-react'
|
|
4
4
|
import { Controller } from './Controller'
|
|
5
|
-
import { useController } from './hooks'
|
|
5
|
+
import { useController, useFiveInstance } from './hooks'
|
|
6
6
|
import { CameraMovement } from './modules/keyframes/CameraMovement'
|
|
7
7
|
import { InfoPanel } from './modules/keyframes/InfoPanel'
|
|
8
8
|
import { ModelVideo } from './modules/keyframes/ModelVideo'
|
|
@@ -13,8 +13,10 @@ import { Prompter } from './modules/keyframes/Prompter'
|
|
|
13
13
|
import { UpdateVRPanorama } from './modules/keyframes/UpdateVRPanorama'
|
|
14
14
|
import { VideoEffect } from './modules/keyframes/VideoEffect'
|
|
15
15
|
import { VideoAgent } from './modules/VideoAgent'
|
|
16
|
+
import { BgMusic } from './modules/keyframes/BgMusic'
|
|
16
17
|
|
|
17
18
|
const AppView = observer(({ controller }: { controller: Controller }) => {
|
|
19
|
+
|
|
18
20
|
return (
|
|
19
21
|
<div
|
|
20
22
|
className={classNames('vreo-panel', {
|
|
@@ -33,8 +35,10 @@ const AppView = observer(({ controller }: { controller: Controller }) => {
|
|
|
33
35
|
controller.setPlaying(true)
|
|
34
36
|
}
|
|
35
37
|
}}
|
|
38
|
+
options={controller.configs?.videoAgentMeshOptions || {}}
|
|
36
39
|
/>
|
|
37
40
|
<Prompter />
|
|
41
|
+
{controller.configs?.customPanelChildren}
|
|
38
42
|
</div>
|
|
39
43
|
</div>
|
|
40
44
|
)
|
|
@@ -53,6 +57,7 @@ export function App() {
|
|
|
53
57
|
<PanoTextLabel />
|
|
54
58
|
<InfoPanel />
|
|
55
59
|
<PanoEffect />
|
|
60
|
+
<BgMusic />
|
|
56
61
|
</>
|
|
57
62
|
<AppView controller={controller} />
|
|
58
63
|
</>
|
|
@@ -24,6 +24,16 @@ export class Controller extends Subscribe<VreoKeyframeEvent> {
|
|
|
24
24
|
return !this.videoAgentScene?.videoAgentMesh.videoUrl?.endsWith('.mp4')
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
+
popUp: string | JSX.Element | null = null
|
|
28
|
+
|
|
29
|
+
openPopUp(popUp: string | JSX.Element | false) {
|
|
30
|
+
if (!popUp) {
|
|
31
|
+
this.popUp = null
|
|
32
|
+
return
|
|
33
|
+
}
|
|
34
|
+
this.popUp = popUp
|
|
35
|
+
}
|
|
36
|
+
|
|
27
37
|
drawerConfig: {
|
|
28
38
|
content: string | JSX.Element
|
|
29
39
|
height?: number | string
|
|
@@ -57,6 +67,8 @@ export class Controller extends Subscribe<VreoKeyframeEvent> {
|
|
|
57
67
|
setVisible: action,
|
|
58
68
|
playing: observable,
|
|
59
69
|
setPlaying: action,
|
|
70
|
+
popUp: observable.ref,
|
|
71
|
+
openPopUp: action,
|
|
60
72
|
drawerConfig: observable.ref,
|
|
61
73
|
openDrawer: action,
|
|
62
74
|
})
|
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
import * as React from 'react'
|
|
2
|
+
import ReactDOM from 'react-dom'
|
|
3
|
+
import { Five } from '@realsee/five'
|
|
4
|
+
import * as THREE from 'three'
|
|
5
|
+
import { Object3D, Raycaster, Vector3 } from 'three'
|
|
6
|
+
import { tweenProgress } from '@realsee/dnalogel/shared-utils/animationFrame/BetterTween'
|
|
7
|
+
import { CSS3DRenderPlugin, CSS3DRenderPluginExportType } from '@realsee/dnalogel/plugins/CSS3DRenderPlugin'
|
|
8
|
+
|
|
9
|
+
import { VreoKeyframe, VreoKeyframeEnum } from '../../../typings/VreoUnit'
|
|
10
|
+
import { CustomVreoKeyframeProps } from '../../typings'
|
|
11
|
+
|
|
12
|
+
// __debug__
|
|
13
|
+
// const mockData: SpatialScenePanelData = {
|
|
14
|
+
// customType: 'SpatialScenePanel',
|
|
15
|
+
// "stateList": [
|
|
16
|
+
// {
|
|
17
|
+
// "icon": "http://vrlab-public.ljcdn.com/release/seesay/tools/___fc42f951076607a22fd5c54f005ba553.png",
|
|
18
|
+
// "text": "窗帘关闭"
|
|
19
|
+
// },
|
|
20
|
+
// {
|
|
21
|
+
// "icon": "http://vrlab-public.ljcdn.com/release/seesay/tools/___b331e7ad732debc07250a169a3393b13.png",
|
|
22
|
+
// "text": "摄像头休眠"
|
|
23
|
+
// },
|
|
24
|
+
// {
|
|
25
|
+
// "icon": "http://vrlab-public.ljcdn.com/release/seesay/tools/___2e425a81ac0ad88232d50d4e52a9abfa.png",
|
|
26
|
+
// "text": "氛围音乐"
|
|
27
|
+
// },
|
|
28
|
+
// {
|
|
29
|
+
// "icon": "http://vrlab-public.ljcdn.com/release/seesay/tools/___bff48bb81cde37aa6a319d6f5d56e3a4.png",
|
|
30
|
+
// "text": "空调打开"
|
|
31
|
+
// }
|
|
32
|
+
// ],
|
|
33
|
+
// "temperature": "27",
|
|
34
|
+
// "title": "沉浸式回家"
|
|
35
|
+
// }
|
|
36
|
+
export interface SpatialScenePanelData {
|
|
37
|
+
customType: 'SpatialScenePanel'
|
|
38
|
+
title: string
|
|
39
|
+
temperature: string
|
|
40
|
+
stateList: {
|
|
41
|
+
text: string
|
|
42
|
+
icon: string
|
|
43
|
+
}[]
|
|
44
|
+
position?: { x: number; y: number; z: number }
|
|
45
|
+
quaternion?: [number, number, number, number]
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
interface SearchParams {
|
|
49
|
+
five: Five
|
|
50
|
+
// position: Vector3
|
|
51
|
+
// rotation?: Vector3
|
|
52
|
+
// quaternion: [number, number, number, number]
|
|
53
|
+
dom: HTMLDivElement
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// const getSphere = (position: Vector3, color = 0xffff00) => {
|
|
57
|
+
// const geometry = new SphereGeometry(0.05, 32, 16)
|
|
58
|
+
// const material = new MeshBasicMaterial({ color })
|
|
59
|
+
// const sphere = new Mesh(geometry, material)
|
|
60
|
+
// sphere.position.copy(position)
|
|
61
|
+
// return sphere
|
|
62
|
+
// }
|
|
63
|
+
|
|
64
|
+
const searchV1 = ({ five, dom }: SearchParams) => {
|
|
65
|
+
const cameraDirection = new Vector3()
|
|
66
|
+
five.camera.getWorldDirection(cameraDirection)
|
|
67
|
+
const cameraPosition = five.camera.position
|
|
68
|
+
const [intersect] = five.model.intersectRaycaster(new Raycaster(cameraPosition, cameraDirection))
|
|
69
|
+
const point = five.project2d(intersect.point)
|
|
70
|
+
|
|
71
|
+
const widescreen = window.innerWidth > 560 ? true : false
|
|
72
|
+
// const width = dom!.clientWidth
|
|
73
|
+
const height = dom!.clientHeight
|
|
74
|
+
dom!.style.left = point!.x + 'px'
|
|
75
|
+
dom!.style.top = point!.y - height / 2 + 'px'
|
|
76
|
+
const rect = dom!.getBoundingClientRect()
|
|
77
|
+
const { left, top, bottom, right } = rect
|
|
78
|
+
|
|
79
|
+
const leftBottom = new Vector3(left, bottom)
|
|
80
|
+
const leftTop = new Vector3(left, top)
|
|
81
|
+
const rightTop = new Vector3(right, top)
|
|
82
|
+
const rightBottom = new Vector3(right, bottom)
|
|
83
|
+
|
|
84
|
+
const points = [leftBottom, rightBottom, rightTop, leftTop].map((point) => {
|
|
85
|
+
point.setX((point.x / window.innerWidth) * 2 - (widescreen ? 1.25: 1.75))
|
|
86
|
+
point.setY(-(point.y / window.innerHeight) * 2 + (widescreen ? 1.35: 1.125))
|
|
87
|
+
point.setZ(0.5)
|
|
88
|
+
return point.unproject(five.camera).addScaledVector(cameraDirection, 0.2)
|
|
89
|
+
})
|
|
90
|
+
|
|
91
|
+
// __debug__
|
|
92
|
+
// const colors = [0xdc143c, 0xffff00, 0x0000ff, 0x008000]
|
|
93
|
+
// points.forEach((p, index) => five.scene.add(getSphere(p, colors[index])))
|
|
94
|
+
|
|
95
|
+
// 判断是手机 150
|
|
96
|
+
const ratio = leftBottom.distanceTo(rightBottom) / (widescreen ? 150 : 120)
|
|
97
|
+
return { points, ratio }
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* 空间场景面板
|
|
102
|
+
* @returns
|
|
103
|
+
*/
|
|
104
|
+
export function SpatialScenePanel(props: CustomVreoKeyframeProps) {
|
|
105
|
+
const timeoutRef = React.useRef<NodeJS.Timeout | null>(null)
|
|
106
|
+
const ref = React.useRef<HTMLDivElement>(null)
|
|
107
|
+
const panelRef = React.useRef<HTMLDivElement | null>(null)
|
|
108
|
+
const rendererRef = React.useRef<CSS3DRenderPluginExportType>(CSS3DRenderPlugin(props.five))
|
|
109
|
+
|
|
110
|
+
React.useEffect(() => {
|
|
111
|
+
if (!ref.current) {
|
|
112
|
+
return
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
const callback = (keyframe: VreoKeyframe) => {
|
|
116
|
+
if (keyframe.data.customType !== 'SpatialScenePanel') {
|
|
117
|
+
return
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
const data = keyframe.data as SpatialScenePanelData
|
|
121
|
+
|
|
122
|
+
// const data = mockData
|
|
123
|
+
const { points, ratio } = searchV1({
|
|
124
|
+
five: props.five,
|
|
125
|
+
dom: ref.current!,
|
|
126
|
+
})
|
|
127
|
+
|
|
128
|
+
const { container, dispose, css3DObject, render } =
|
|
129
|
+
rendererRef.current.create3DDomContainer(points, { ratio, autoRender: false }) || {}
|
|
130
|
+
|
|
131
|
+
Object.assign(window, { $css3DObject: css3DObject })
|
|
132
|
+
if (!container) return
|
|
133
|
+
|
|
134
|
+
ReactDOM.render(<Panel ref={(ref) => (panelRef.current = ref)} data={data} />, container)
|
|
135
|
+
const startRotateY = -(Math.PI * 2) / 9
|
|
136
|
+
let lastRotateY = 0
|
|
137
|
+
|
|
138
|
+
tweenProgress(1000)
|
|
139
|
+
.onUpdate(({ progress }) => {
|
|
140
|
+
const needRotateY = (Math.PI / 90) * 11 * progress
|
|
141
|
+
const rotateY = needRotateY - lastRotateY
|
|
142
|
+
css3DObject?.rotateY(rotateY)
|
|
143
|
+
// css3DObject?.rotateZ
|
|
144
|
+
lastRotateY = needRotateY
|
|
145
|
+
})
|
|
146
|
+
.onStart(() => {
|
|
147
|
+
css3DObject?.rotateY(startRotateY)
|
|
148
|
+
render!()
|
|
149
|
+
})
|
|
150
|
+
.onDispose(() => {
|
|
151
|
+
panelRef.current?.classList.add('show')
|
|
152
|
+
})
|
|
153
|
+
.play()
|
|
154
|
+
|
|
155
|
+
// Object.assign(window, { $dispose: () => {
|
|
156
|
+
// panelRef.current?.classList.add('hide')
|
|
157
|
+
// setTimeout(() => dispose?.(), 1500)
|
|
158
|
+
// } })
|
|
159
|
+
|
|
160
|
+
const { start, end } = keyframe
|
|
161
|
+
|
|
162
|
+
timeoutRef.current = setTimeout(() => {
|
|
163
|
+
panelRef.current?.classList.add('hide')
|
|
164
|
+
timeoutRef.current = setTimeout(() => {
|
|
165
|
+
dispose?.()
|
|
166
|
+
timeoutRef.current = null
|
|
167
|
+
}, 1500)
|
|
168
|
+
}, end - start)
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
Object.assign(window, { $callback: callback })
|
|
172
|
+
|
|
173
|
+
// setTimeout(() => {callback()}, 8000)
|
|
174
|
+
props.subscribe.on(VreoKeyframeEnum.Custom, callback)
|
|
175
|
+
|
|
176
|
+
return () => {
|
|
177
|
+
props.subscribe.off(VreoKeyframeEnum.Custom, callback)
|
|
178
|
+
if (timeoutRef.current) {
|
|
179
|
+
clearTimeout(timeoutRef.current)
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
}, [])
|
|
183
|
+
|
|
184
|
+
return <div className="vreo-SpatialScenePanel" ref={ref}></div>
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/** 背景动画:控制延迟 */
|
|
188
|
+
function SpatialScenePanelBg() {
|
|
189
|
+
const ref = React.useRef<HTMLDivElement>(null)
|
|
190
|
+
const timeoutRef = React.useRef<NodeJS.Timeout | null>(null)
|
|
191
|
+
React.useEffect(() => {
|
|
192
|
+
if (!ref.current) return
|
|
193
|
+
|
|
194
|
+
const listener = () => {
|
|
195
|
+
ref.current?.setAttribute('class', 'SpatialScenePanel-bg')
|
|
196
|
+
|
|
197
|
+
timeoutRef.current = setTimeout(() => {
|
|
198
|
+
ref.current?.setAttribute('class', 'SpatialScenePanel-bg SpatialScenePanel-bg--animation')
|
|
199
|
+
timeoutRef.current = null
|
|
200
|
+
}, 3000)
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
ref.current.addEventListener('animationend', listener)
|
|
204
|
+
|
|
205
|
+
return () => {
|
|
206
|
+
if (ref.current) ref.current.removeEventListener('animationend', listener)
|
|
207
|
+
if (timeoutRef.current) clearTimeout(timeoutRef.current)
|
|
208
|
+
}
|
|
209
|
+
}, [])
|
|
210
|
+
return <div ref={ref} className="SpatialScenePanel-bg SpatialScenePanel-bg--animation"></div>
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
const Panel = React.forwardRef(
|
|
214
|
+
(props: { data: SpatialScenePanelData }, myRef: React.LegacyRef<HTMLDivElement> | undefined) => (
|
|
215
|
+
<div className="SpatialScenePanel" ref={myRef}>
|
|
216
|
+
<div className="SpatialScenePanel-show-area">
|
|
217
|
+
<SpatialScenePanelBg />
|
|
218
|
+
<div className="SpatialScenePanel-header">
|
|
219
|
+
<div className="SpatialScenePanel-title">{props.data?.title}</div>
|
|
220
|
+
<div className="SpatialScenePanel-temperature">{props.data?.temperature}℃</div>
|
|
221
|
+
</div>
|
|
222
|
+
<div className="SpatialScenePanel-body">
|
|
223
|
+
{props.data?.stateList?.map((state, index) => (
|
|
224
|
+
<div key={`scene-${state.text}`} className={`SpatialScenePanel-item index_${index}`}>
|
|
225
|
+
<img className="SpatialScenePanel-icon" src={state.icon}></img>
|
|
226
|
+
<div className="SpatialScenePanel-text">{state.text}</div>
|
|
227
|
+
<div className="SpatialScenePanel-dot"></div>
|
|
228
|
+
</div>
|
|
229
|
+
))}
|
|
230
|
+
</div>
|
|
231
|
+
</div>
|
|
232
|
+
|
|
233
|
+
<div className="SpatialScenePanel-disappear-area"></div>
|
|
234
|
+
</div>
|
|
235
|
+
)
|
|
236
|
+
)
|
|
237
|
+
|
|
238
|
+
|
|
239
|
+
// export function SpatialScenePanel() {
|
|
240
|
+
// // const data = mockData
|
|
241
|
+
|
|
242
|
+
// return (
|
|
243
|
+
// <div className="SpatialScenePanel" >
|
|
244
|
+
// <div className="SpatialScenePanel-show-area">
|
|
245
|
+
// <SpatialScenePanelBg />
|
|
246
|
+
// <div className="SpatialScenePanel-header">
|
|
247
|
+
// <div className="SpatialScenePanel-title">{data?.title}</div>
|
|
248
|
+
// <div className="SpatialScenePanel-temperature">{data?.temperature}℃</div>
|
|
249
|
+
// </div>
|
|
250
|
+
// <div className="SpatialScenePanel-body">
|
|
251
|
+
// {data?.stateList?.map((state, index) => (
|
|
252
|
+
// <div key={`scene-${state.text}`} className={`SpatialScenePanel-item index_${index}`}>
|
|
253
|
+
// <img className="SpatialScenePanel-icon" src={state.icon}></img>
|
|
254
|
+
// <div className="SpatialScenePanel-text">{state.text}</div>
|
|
255
|
+
// <div className="SpatialScenePanel-dot"></div>
|
|
256
|
+
// </div>
|
|
257
|
+
// ))}
|
|
258
|
+
// </div>
|
|
259
|
+
// </div>
|
|
260
|
+
|
|
261
|
+
// <div className="SpatialScenePanel-disappear-area"></div>
|
|
262
|
+
// </div>
|
|
263
|
+
// )
|
|
264
|
+
// }
|
|
@@ -4,10 +4,11 @@ import { Five, Subscribe } from '@realsee/five'
|
|
|
4
4
|
|
|
5
5
|
import { App } from './App'
|
|
6
6
|
import { Controller, ControllerContext } from './Controller'
|
|
7
|
-
import {
|
|
7
|
+
import { CameraMovementData, VreoKeyframeEnum, VreoKeyframeEvent, VreoUnit } from '../typings/VreoUnit'
|
|
8
8
|
import { reaction } from 'mobx'
|
|
9
9
|
import { Drawer } from './modules/Drawer'
|
|
10
10
|
import { PlayerConfigs } from './typings'
|
|
11
|
+
import { PopUp } from './modules/PopUp'
|
|
11
12
|
|
|
12
13
|
export class Player extends Subscribe<VreoKeyframeEvent> {
|
|
13
14
|
$five: Five
|
|
@@ -42,6 +43,19 @@ export class Player extends Subscribe<VreoKeyframeEvent> {
|
|
|
42
43
|
<ControllerContext.Provider value={this.controller}>
|
|
43
44
|
<App></App>
|
|
44
45
|
<Drawer />
|
|
46
|
+
<PopUp />
|
|
47
|
+
{this.configs.customKeyframes &&
|
|
48
|
+
this.configs.customKeyframes.map((CustomCmpt, key) => (
|
|
49
|
+
<CustomCmpt
|
|
50
|
+
key={key}
|
|
51
|
+
subscribe={{
|
|
52
|
+
on: (name, callback) => this.on(name, callback),
|
|
53
|
+
once: (name, callback) => this.once(name, callback),
|
|
54
|
+
off: (name, callback) => this.off(name, callback),
|
|
55
|
+
}}
|
|
56
|
+
five={five}
|
|
57
|
+
/>
|
|
58
|
+
))}
|
|
45
59
|
</ControllerContext.Provider>,
|
|
46
60
|
configs.containter
|
|
47
61
|
)
|
|
@@ -55,9 +69,13 @@ export class Player extends Subscribe<VreoKeyframeEvent> {
|
|
|
55
69
|
)
|
|
56
70
|
}
|
|
57
71
|
|
|
58
|
-
async load(vreoUnit: VreoUnit, currentTime = 0) {
|
|
72
|
+
async load(vreoUnit: VreoUnit, currentTime = 0, preload = false, force = false) {
|
|
73
|
+
if (force) {
|
|
74
|
+
vreoUnit = JSON.parse(JSON.stringify(vreoUnit))
|
|
75
|
+
}
|
|
59
76
|
if (!this.controller.visible) {
|
|
60
77
|
this.controller.setVisible(true)
|
|
78
|
+
// 延迟 500ms 规避跟 DOM 动画冲突
|
|
61
79
|
await new Promise((resolve) => {
|
|
62
80
|
setTimeout(() => resolve(true), 500)
|
|
63
81
|
})
|
|
@@ -71,6 +89,42 @@ export class Player extends Subscribe<VreoKeyframeEvent> {
|
|
|
71
89
|
this.controller.vreoUnit = vreoUnit
|
|
72
90
|
this.controller.videoInstance?.pause()
|
|
73
91
|
|
|
92
|
+
// 预载逻辑
|
|
93
|
+
// 是否降低图片分辨率
|
|
94
|
+
if (this.$five.imageOptions.size && this.configs.imageOptions?.size && this.$five.imageOptions.size > this.configs.imageOptions.size) {
|
|
95
|
+
this.$five.imageOptions.size = this.configs.imageOptions.size
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// 预载数据中的点位
|
|
99
|
+
if (preload || (preload === undefined && this.configs.autoPreload)) {
|
|
100
|
+
const panoIndexMap = vreoUnit.keyframes.filter((vreoKeyframe) => {
|
|
101
|
+
if (vreoKeyframe.type !== VreoKeyframeEnum.CameraMovement) {
|
|
102
|
+
return false
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const data = vreoKeyframe.data as CameraMovementData
|
|
106
|
+
if (data.panoIndex === undefined) {
|
|
107
|
+
return false
|
|
108
|
+
}
|
|
109
|
+
return true
|
|
110
|
+
}).reduce((accu: Record<number, boolean>, curr) => {
|
|
111
|
+
const panoIndex = curr.data.panoIndex as number
|
|
112
|
+
if (!accu[panoIndex]) {
|
|
113
|
+
accu[panoIndex] = true
|
|
114
|
+
}
|
|
115
|
+
return accu
|
|
116
|
+
}, {})
|
|
117
|
+
|
|
118
|
+
const panoIndexs = Object.keys(panoIndexMap)
|
|
119
|
+
for (let i = 0; i < panoIndexs.length; i++) {
|
|
120
|
+
await this.$five.preloadPano(Number(panoIndexs[i]))
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// 新数据载入就绪
|
|
125
|
+
this.emit('loaded', vreoUnit)
|
|
126
|
+
this.controller.emit('loaded', vreoUnit)
|
|
127
|
+
|
|
74
128
|
if (vreoUnit.video.url) {
|
|
75
129
|
if (this.controller.videoAgentScene?.videoAgentMesh.videoInstance) {
|
|
76
130
|
this.controller.videoAgentScene.videoAgentMesh.videoInstance.currentTime = currentTime / 1000
|
|
@@ -5,7 +5,7 @@ import { Controller } from '../../Controller'
|
|
|
5
5
|
import { useController } from '../../hooks'
|
|
6
6
|
|
|
7
7
|
const DrawerView = observer(({ controller }: { controller: Controller }) => {
|
|
8
|
-
const maxHeight = '
|
|
8
|
+
const maxHeight = '400px'
|
|
9
9
|
const height = (() => {
|
|
10
10
|
const height = controller.drawerConfig?.height
|
|
11
11
|
if (!height) {
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import * as React from 'react'
|
|
2
|
+
import classNames from 'classnames'
|
|
3
|
+
import { observer } from 'mobx-react'
|
|
4
|
+
import { Controller } from '../../Controller'
|
|
5
|
+
import { useController } from '../../hooks'
|
|
6
|
+
|
|
7
|
+
const PopUpView = observer(({ controller }: { controller: Controller }) => {
|
|
8
|
+
|
|
9
|
+
return (
|
|
10
|
+
<div
|
|
11
|
+
className={classNames('vreo-PopUp', {
|
|
12
|
+
'vreo-PopUp-visible': controller.popUp,
|
|
13
|
+
})}
|
|
14
|
+
onClick={() => controller.openPopUp(false)}
|
|
15
|
+
>
|
|
16
|
+
<div className="vreo-PopUp-inner">
|
|
17
|
+
{controller.popUp || ''}
|
|
18
|
+
</div>
|
|
19
|
+
</div>
|
|
20
|
+
)
|
|
21
|
+
})
|
|
22
|
+
|
|
23
|
+
export function PopUp() {
|
|
24
|
+
const controller = useController()
|
|
25
|
+
return <PopUpView controller={controller} />
|
|
26
|
+
}
|
|
@@ -64,6 +64,7 @@ const cacheInstance: {
|
|
|
64
64
|
|
|
65
65
|
export interface VideoAgentMeshOptions {
|
|
66
66
|
videoInstance?: HTMLVideoElement
|
|
67
|
+
audioInstance?: HTMLAudioElement
|
|
67
68
|
}
|
|
68
69
|
|
|
69
70
|
/**
|
|
@@ -134,10 +135,9 @@ export class VideoAgentMesh extends THREE.Mesh {
|
|
|
134
135
|
this.freeze = false
|
|
135
136
|
this.paused = true
|
|
136
137
|
|
|
137
|
-
{
|
|
138
|
+
if (!options.audioInstance) {
|
|
138
139
|
if (cacheInstance.audioInstance) {
|
|
139
140
|
this.audioInstance = cacheInstance.audioInstance
|
|
140
|
-
|
|
141
141
|
} else {
|
|
142
142
|
const audioInstance = document.createElement('audio')
|
|
143
143
|
audioInstance.crossOrigin = ''
|
|
@@ -151,7 +151,8 @@ export class VideoAgentMesh extends THREE.Mesh {
|
|
|
151
151
|
this.audioInstance = audioInstance
|
|
152
152
|
cacheInstance.audioInstance = audioInstance
|
|
153
153
|
}
|
|
154
|
-
|
|
154
|
+
} else {
|
|
155
|
+
this.audioInstance = options.audioInstance
|
|
155
156
|
}
|
|
156
157
|
|
|
157
158
|
makeObservable(this, { paused: observable })
|
|
@@ -196,7 +197,7 @@ export class VideoAgentMesh extends THREE.Mesh {
|
|
|
196
197
|
}
|
|
197
198
|
|
|
198
199
|
async play(videoUrl = '') {
|
|
199
|
-
videoUrl =
|
|
200
|
+
videoUrl = videoUrl || ''
|
|
200
201
|
if (!videoUrl) {
|
|
201
202
|
if (this.videoUrl) await this.videoInstance.play()
|
|
202
203
|
else console.warn('警告:视频资源未初始化。')
|