@realsee/vreo 0.2.0-alpha.1 → 0.2.0-alpha.12
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 +104 -17
- 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 +9 -9
- 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.PanoTagStyleEnum.html +7 -0
- package/docs/enums/Player.VreoKeyframeEnum.html +13 -13
- package/docs/enums/fivePlugins.CameraMovementEffect.html +1 -1
- package/docs/enums/fivePlugins.Rotation.html +1 -1
- package/docs/index.html +53 -20
- package/docs/interfaces/Player.CustomVreoKeyframeProps.html +1 -0
- package/docs/interfaces/Player.PlayerConfigs.html +3 -3
- 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 +4 -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 +2 -2
- package/lib/Player/index.js +102 -12
- 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 +2 -1
- package/lib/Player/modules/VideoAgent/VideoAgentMesh.js +26 -20
- package/lib/Player/modules/VideoAgent/VideoAgentScene.d.ts +4 -1
- package/lib/Player/modules/VideoAgent/VideoAgentScene.js +2 -1
- package/lib/Player/modules/VideoAgent/index.d.ts +2 -0
- package/lib/Player/modules/VideoAgent/index.js +1 -1
- package/lib/Player/modules/keyframes/CameraMovement/index.js +41 -5
- package/lib/Player/modules/keyframes/InfoPanel/index.js +84 -36
- package/lib/Player/modules/keyframes/ModelVideo/index.js +45 -8
- package/lib/Player/modules/keyframes/PanoTag/index.js +19 -6
- 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 +30 -18
- package/lib/Player/typings.d.ts +15 -2
- package/lib/fivePlugins/CameraMovementPlugin/index.js +104 -90
- package/lib/fivePlugins/CameraMovementPlugin/typings.d.ts +1 -0
- package/lib/react/index.d.ts +12 -4
- package/lib/react/index.js +3 -3
- package/lib/shared-utils/createTransMatrix.d.ts +1 -0
- package/lib/shared-utils/createTransMatrix.js +43 -0
- package/lib/typings/VreoUnit.d.ts +32 -6
- package/lib/typings/VreoUnit.js +26 -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 +3 -1
- package/resources/Player/Controller.ts +12 -0
- package/resources/Player/custom/SpatialScenePanel/index.tsx +264 -0
- package/resources/Player/index.tsx +59 -4
- package/resources/Player/modules/PopUp/index.tsx +26 -0
- package/resources/Player/modules/VideoAgent/VideoAgentMesh.ts +7 -6
- package/resources/Player/modules/VideoAgent/VideoAgentScene.ts +2 -2
- package/resources/Player/modules/VideoAgent/index.tsx +3 -2
- package/resources/Player/modules/keyframes/CameraMovement/index.tsx +7 -3
- package/resources/Player/modules/keyframes/InfoPanel/index.tsx +90 -23
- package/resources/Player/modules/keyframes/ModelVideo/index.tsx +36 -3
- package/resources/Player/modules/keyframes/PanoTag/index.tsx +31 -12
- 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 +25 -16
- package/resources/Player/typings.ts +17 -2
- package/resources/fivePlugins/CameraMovementPlugin/index.ts +59 -48
- package/resources/fivePlugins/CameraMovementPlugin/typings.ts +2 -1
- package/resources/react/index.tsx +14 -5
- package/resources/shared-utils/createTransMatrix.ts +12 -0
- package/resources/typings/VreoUnit.ts +42 -7
- package/stylesheets/custom/SpatialScenePanel.css +309 -0
- package/stylesheets/default.css +116 -10
|
@@ -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,7 +69,10 @@ 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)
|
|
61
78
|
// 延迟 500ms 规避跟 DOM 动画冲突
|
|
@@ -72,13 +89,47 @@ export class Player extends Subscribe<VreoKeyframeEvent> {
|
|
|
72
89
|
this.controller.vreoUnit = vreoUnit
|
|
73
90
|
this.controller.videoInstance?.pause()
|
|
74
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
|
+
|
|
75
124
|
// 新数据载入就绪
|
|
76
125
|
this.emit('loaded', vreoUnit)
|
|
126
|
+
this.controller.emit('loaded', vreoUnit)
|
|
127
|
+
|
|
77
128
|
if (vreoUnit.video.url) {
|
|
78
129
|
if (this.controller.videoAgentScene?.videoAgentMesh.videoInstance) {
|
|
79
130
|
this.controller.videoAgentScene.videoAgentMesh.videoInstance.currentTime = currentTime / 1000
|
|
80
131
|
}
|
|
81
|
-
await this.controller.videoAgentScene?.videoAgentMesh.play(vreoUnit.video.url)
|
|
132
|
+
await this.controller.videoAgentScene?.videoAgentMesh.play(vreoUnit.video.url, currentTime / 1000)
|
|
82
133
|
this.play()
|
|
83
134
|
}
|
|
84
135
|
this.controller.run((type, keyframe) => this.emit(type, keyframe))
|
|
@@ -89,8 +140,12 @@ export class Player extends Subscribe<VreoKeyframeEvent> {
|
|
|
89
140
|
return !this.controller.playing
|
|
90
141
|
}
|
|
91
142
|
|
|
92
|
-
play() {
|
|
143
|
+
play(currentTime?: number) {
|
|
93
144
|
if (this.controller.playing) return true
|
|
145
|
+
if (currentTime && this.controller.videoInstance) {
|
|
146
|
+
// Object.assign(window, {$vreoController: this.controller})
|
|
147
|
+
this.controller.videoInstance.currentTime = currentTime / 1000
|
|
148
|
+
}
|
|
94
149
|
this.controller.setPlaying(true)
|
|
95
150
|
return true
|
|
96
151
|
}
|
|
@@ -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 })
|
|
@@ -195,7 +196,7 @@ export class VideoAgentMesh extends THREE.Mesh {
|
|
|
195
196
|
this.videoInstance.addEventListener('timeupdate', onStart, false)
|
|
196
197
|
}
|
|
197
198
|
|
|
198
|
-
async play(videoUrl = '') {
|
|
199
|
+
async play(videoUrl = '', currentTime = 0) {
|
|
199
200
|
videoUrl = videoUrl || ''
|
|
200
201
|
if (!videoUrl) {
|
|
201
202
|
if (this.videoUrl) await this.videoInstance.play()
|
|
@@ -204,7 +205,7 @@ export class VideoAgentMesh extends THREE.Mesh {
|
|
|
204
205
|
}
|
|
205
206
|
|
|
206
207
|
if (videoUrl === this.videoUrl) {
|
|
207
|
-
this.videoInstance.currentTime =
|
|
208
|
+
this.videoInstance.currentTime = currentTime
|
|
208
209
|
await this.videoInstance.play()
|
|
209
210
|
return true
|
|
210
211
|
}
|
|
@@ -215,7 +216,7 @@ export class VideoAgentMesh extends THREE.Mesh {
|
|
|
215
216
|
|
|
216
217
|
return await new Promise((resolve) =>
|
|
217
218
|
setTimeout(async () => {
|
|
218
|
-
this.videoInstance.currentTime =
|
|
219
|
+
this.videoInstance.currentTime = currentTime
|
|
219
220
|
await this.videoInstance.play()
|
|
220
221
|
resolve(true)
|
|
221
222
|
}, 20),
|
|
@@ -9,7 +9,7 @@ export class VideoAgentScene {
|
|
|
9
9
|
renderer = new THREE.WebGLRenderer({ alpha: true })
|
|
10
10
|
container: HTMLElement
|
|
11
11
|
|
|
12
|
-
constructor(container: HTMLElement, needRender = true) {
|
|
12
|
+
constructor(container: HTMLElement, needRender = true, options: { videoInstance?: HTMLVideoElement, audioInstance?: HTMLAudioElement } = {}) {
|
|
13
13
|
this.container = container
|
|
14
14
|
this.camera.position.set(0, 0, 10)
|
|
15
15
|
this.camera.lookAt(0, 0, 0)
|
|
@@ -22,7 +22,7 @@ export class VideoAgentScene {
|
|
|
22
22
|
this.renderer.setSize(state.width * window.devicePixelRatio, state.height * window.devicePixelRatio)
|
|
23
23
|
domElement.setAttribute('style', `width: ${state.width}px;height: ${state.height}px;`)
|
|
24
24
|
|
|
25
|
-
this.videoAgentMesh = new VideoAgentMesh(480, 270, 1, 1)
|
|
25
|
+
this.videoAgentMesh = new VideoAgentMesh(480, 270, 1, 1, options)
|
|
26
26
|
if (needRender) {
|
|
27
27
|
container.append(domElement)
|
|
28
28
|
this.scene.add(this.videoAgentMesh)
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import * as React from 'react'
|
|
2
2
|
import { useController } from '../../hooks'
|
|
3
|
+
import { VideoAgentMeshOptions } from './VideoAgentMesh'
|
|
3
4
|
import { VideoAgentScene } from './VideoAgentScene'
|
|
4
5
|
|
|
5
|
-
export function VideoAgent(props: { onClick?: () => void }) {
|
|
6
|
+
export function VideoAgent(props: { onClick?: () => void; options?: VideoAgentMeshOptions }) {
|
|
6
7
|
const ref = React.useRef<HTMLDivElement>(null)
|
|
7
8
|
const controller = useController()
|
|
8
9
|
|
|
@@ -12,7 +13,7 @@ export function VideoAgent(props: { onClick?: () => void }) {
|
|
|
12
13
|
console.warn('"VideoAgentScene" 重复初始化,已被过滤 ...')
|
|
13
14
|
return
|
|
14
15
|
}
|
|
15
|
-
const videoAgentScene = new VideoAgentScene(ref.current)
|
|
16
|
+
const videoAgentScene = new VideoAgentScene(ref.current, true, props.options || {})
|
|
16
17
|
controller.videoAgentScene = videoAgentScene
|
|
17
18
|
|
|
18
19
|
return () => {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as React from 'react'
|
|
2
2
|
import { CameraMovementPlugin } from '../../../../fivePlugins/CameraMovementPlugin'
|
|
3
|
-
import { CameraMovementEffect, RotateArgs } from '../../../../fivePlugins/CameraMovementPlugin/typings'
|
|
3
|
+
import { CameraMovementEffect, MoveArgs, RotateArgs } from '../../../../fivePlugins/CameraMovementPlugin/typings'
|
|
4
4
|
import { CameraMovementData, VreoKeyframe, VreoKeyframeEnum } from '../../../../typings/VreoUnit'
|
|
5
5
|
import { useController, useFiveInstance } from '../../../hooks'
|
|
6
6
|
|
|
@@ -18,9 +18,13 @@ export function CameraMovement() {
|
|
|
18
18
|
const cameraMovementData = data as CameraMovementData
|
|
19
19
|
const effect = cameraMovementData.effect
|
|
20
20
|
if (effect === CameraMovementEffect.Rotate) {
|
|
21
|
-
ref.current.rotate(data as RotateArgs, end - start)
|
|
21
|
+
await ref.current.rotate(data as RotateArgs, end - start)
|
|
22
|
+
} else if (effect === CameraMovementEffect.Move) {
|
|
23
|
+
await ref.current.move(data as MoveArgs, end - start)
|
|
24
|
+
} else if (cameraMovementData.panoIndex !== undefined && cameraMovementData.panoIndex !== five.panoIndex) {
|
|
25
|
+
await ref.current.move(data as MoveArgs, end - start)
|
|
22
26
|
} else {
|
|
23
|
-
ref.current.
|
|
27
|
+
await ref.current.rotate(data as RotateArgs, end - start)
|
|
24
28
|
}
|
|
25
29
|
}
|
|
26
30
|
|
|
@@ -1,13 +1,19 @@
|
|
|
1
1
|
import * as React from 'react'
|
|
2
2
|
import { ReactNode } from 'react'
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
InfoPanelData,
|
|
5
|
+
InfoPanelStyleEnum,
|
|
6
|
+
InfoPanelTypeEnum,
|
|
7
|
+
VreoKeyframe,
|
|
8
|
+
VreoKeyframeEnum,
|
|
9
|
+
} from '../../../../typings/VreoUnit'
|
|
4
10
|
import { useController } from '../../../hooks'
|
|
5
11
|
|
|
6
12
|
function InfoPanelImg({ url, children }: { url: string; children?: ReactNode }) {
|
|
7
13
|
return (
|
|
8
14
|
<div className="vreo-infoPanel vreo-infoPanel--img">
|
|
9
15
|
{children}
|
|
10
|
-
<div className="vreo-infoPanelImg" style={{backgroundImage: `url(${url})`}}
|
|
16
|
+
<div className="vreo-infoPanelImg" style={{ backgroundImage: `url(${url})` }}></div>
|
|
11
17
|
</div>
|
|
12
18
|
)
|
|
13
19
|
}
|
|
@@ -16,7 +22,9 @@ function InfoPanelVideo({ url, children }: { url: string; children?: ReactNode }
|
|
|
16
22
|
return (
|
|
17
23
|
<div className="vreo-infoPanel vreo-infoPanel--video">
|
|
18
24
|
{children}
|
|
19
|
-
<
|
|
25
|
+
<div className="vreo-infoPanelWrapper">
|
|
26
|
+
<video playsInline autoPlay className="vreo-infoPanelVideo" src={url} />
|
|
27
|
+
</div>
|
|
20
28
|
</div>
|
|
21
29
|
)
|
|
22
30
|
}
|
|
@@ -31,10 +39,12 @@ const Title = (props: TitleProps) => {
|
|
|
31
39
|
return null
|
|
32
40
|
}
|
|
33
41
|
|
|
34
|
-
return
|
|
35
|
-
<div className="vreo-infoPanel-
|
|
36
|
-
|
|
37
|
-
|
|
42
|
+
return (
|
|
43
|
+
<div className="vreo-infoPanel-title">
|
|
44
|
+
<div className="vreo-infoPanel-t1">{props.title}</div>
|
|
45
|
+
<div className="vreo-infoPanel-t2">{props.subTitle}</div>
|
|
46
|
+
</div>
|
|
47
|
+
)
|
|
38
48
|
}
|
|
39
49
|
|
|
40
50
|
export function InfoPanel() {
|
|
@@ -45,31 +55,88 @@ export function InfoPanel() {
|
|
|
45
55
|
if (controller.configs?.keyframeMap.InfoPanel === false) {
|
|
46
56
|
return
|
|
47
57
|
}
|
|
58
|
+
|
|
48
59
|
const callback = (keyframe: VreoKeyframe) => {
|
|
49
60
|
const { start, end, data } = keyframe
|
|
50
61
|
|
|
51
62
|
const infoPanelData = data as InfoPanelData
|
|
52
|
-
|
|
53
63
|
|
|
64
|
+
const { title, subTitle, style } = infoPanelData
|
|
65
|
+
do {
|
|
66
|
+
if (style === InfoPanelStyleEnum.PopUp) {
|
|
67
|
+
if (infoPanelData.type === InfoPanelTypeEnum.Image) {
|
|
68
|
+
controller.openPopUp(
|
|
69
|
+
<InfoPanelImg url={infoPanelData.url}>
|
|
70
|
+
<Title title={title} subTitle={subTitle} />
|
|
71
|
+
</InfoPanelImg>
|
|
72
|
+
)
|
|
73
|
+
} else if (infoPanelData.type === InfoPanelTypeEnum.Video) {
|
|
74
|
+
controller.openPopUp(
|
|
75
|
+
<InfoPanelVideo url={infoPanelData.url}>
|
|
76
|
+
<Title title={title} subTitle={subTitle} />
|
|
77
|
+
</InfoPanelVideo>
|
|
78
|
+
)
|
|
79
|
+
}
|
|
80
|
+
break
|
|
81
|
+
}
|
|
82
|
+
if (infoPanelData.type === InfoPanelTypeEnum.Image) {
|
|
83
|
+
controller.openDrawer({
|
|
84
|
+
content: (
|
|
85
|
+
<InfoPanelImg url={infoPanelData.url}>
|
|
86
|
+
<Title title={title} subTitle={subTitle} />
|
|
87
|
+
</InfoPanelImg>
|
|
88
|
+
),
|
|
89
|
+
height: '60vh',
|
|
90
|
+
})
|
|
91
|
+
} else if (infoPanelData.type === InfoPanelTypeEnum.Video) {
|
|
92
|
+
controller.openDrawer({
|
|
93
|
+
content: (
|
|
94
|
+
<InfoPanelVideo url={infoPanelData.url}>
|
|
95
|
+
<Title title={title} subTitle={subTitle} />
|
|
96
|
+
</InfoPanelVideo>
|
|
97
|
+
),
|
|
98
|
+
height: '60vh',
|
|
99
|
+
})
|
|
100
|
+
}
|
|
101
|
+
} while (false)
|
|
54
102
|
|
|
55
|
-
|
|
56
|
-
controller.openDrawer(
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
}
|
|
60
|
-
timeoutRef.current = setTimeout(() => controller.openDrawer(false), end - start)
|
|
103
|
+
timeoutRef.current = setTimeout(() => {
|
|
104
|
+
controller.openDrawer(false)
|
|
105
|
+
controller.openPopUp(false)
|
|
106
|
+
}, end - start)
|
|
61
107
|
}
|
|
62
108
|
controller.on(VreoKeyframeEnum.InfoPanel, callback)
|
|
63
109
|
|
|
64
|
-
Object.assign(window, {
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
}
|
|
110
|
+
// Object.assign(window, {
|
|
111
|
+
// $setInfoPanel: () => {
|
|
112
|
+
// controller.openPopUp(
|
|
113
|
+
// // <InfoPanelImg url='//vrlab-static.ljcdn.com/release/web/psq/a.8e868cb2.gif' />
|
|
114
|
+
// // <InfoPanelImg url='//vrlab-public.ljcdn.com/release/web/psq/b.f9a1ed52.png' />
|
|
115
|
+
// // <InfoPanelImg url='//vrlab-public.ljcdn.com/release/web/psq/c.4f88c112.png' />
|
|
116
|
+
// // <InfoPanelImg url='//vrlab-public.ljcdn.com/release/seesay/tools/cat_music___f68fb9bbe1f7cd6d00a16456dd0b09ad.gif' />
|
|
117
|
+
// <InfoPanelImg url="http://vrlab-public.ljcdn.com/common/images/web/d94e1bd7-0311-4b20-9c41-a1294fe43554.png">
|
|
118
|
+
// <Title title="场景联动抓拍图片" subTitle={'2022.02.17 14:00'} />
|
|
119
|
+
// </InfoPanelImg>
|
|
120
|
+
// // <InfoPanelVideo url="//vrlab-public.ljcdn.com/release/seesay/tools/2022011713142___b320f74a5e1b5ad4bb46b4dc69d73ecc.mp4">
|
|
121
|
+
// // <Title title="场景联动抓拍图片" subTitle={'2022.02.17 14:00'} />
|
|
122
|
+
// // </InfoPanelVideo>
|
|
123
|
+
// )
|
|
124
|
+
// },
|
|
125
|
+
// $setInfoPanel2: () => {
|
|
126
|
+
// controller.openPopUp(
|
|
127
|
+
// <InfoPanelImg url='//vrlab-static.ljcdn.com/release/web/psq/a.8e868cb2.gif' />
|
|
128
|
+
// // <InfoPanelImg url='//vrlab-public.ljcdn.com/release/web/psq/b.f9a1ed52.png' />
|
|
129
|
+
// // <InfoPanelImg url='//vrlab-public.ljcdn.com/release/web/psq/c.4f88c112.png' />
|
|
130
|
+
// // <InfoPanelImg url='//vrlab-public.ljcdn.com/release/seesay/tools/cat_music___f68fb9bbe1f7cd6d00a16456dd0b09ad.gif' />
|
|
131
|
+
// // <InfoPanelImg url="http://vrlab-public.ljcdn.com/common/images/web/d94e1bd7-0311-4b20-9c41-a1294fe43554.png">
|
|
132
|
+
// // <Title title="场景联动抓拍图片" subTitle={'2022.02.17 14:00'} />
|
|
133
|
+
// // </InfoPanelImg>
|
|
134
|
+
// // <InfoPanelVideo url="//vrlab-public.ljcdn.com/release/seesay/tools/2022011713142___b320f74a5e1b5ad4bb46b4dc69d73ecc.mp4">
|
|
135
|
+
// // <Title title="场景联动抓拍图片" subTitle={'2022.02.17 14:00'} />
|
|
136
|
+
// // </InfoPanelVideo>
|
|
137
|
+
// )
|
|
138
|
+
// },
|
|
139
|
+
// })
|
|
73
140
|
|
|
74
141
|
return () => {
|
|
75
142
|
controller.off(VreoKeyframeEnum.InfoPanel, callback)
|