@realsee/vreo 0.1.6-alpha.4 → 0.2.0-alpha.1
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/docs/assets/search.js +1 -1
- package/docs/classes/Player.Player-1.html +11 -11
- package/docs/enums/Player.InfoPanelTypeEnum.html +1 -1
- package/docs/enums/Player.PanoEffectEnum.html +1 -1
- 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/interfaces/Player.PlayerConfigs.html +4 -2
- package/docs/interfaces/Player.Vertex.html +1 -1
- 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 +2 -2
- 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 +6 -6
- package/docs/modules/Player.html +12 -12
- package/docs/modules/fivePlugins.html +2 -2
- package/docs/modules/react.html +1 -1
- package/lib/Player/App.js +6 -2
- package/lib/Player/index.js +10 -7
- package/lib/Player/modules/Drawer/index.js +1 -1
- package/lib/Player/modules/VideoAgent/VideoAgentScene.js +19 -5
- 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/InfoPanel/index.js +39 -5
- package/lib/Player/modules/keyframes/UpdateVRPanorama/index.js +55 -16
- package/lib/Player/typings.d.ts +5 -0
- package/lib/typings/VreoUnit.d.ts +31 -4
- package/lib/typings/VreoUnit.js +1 -0
- package/package.json +3 -3
- package/resources/Player/App.tsx +3 -0
- package/resources/Player/index.tsx +3 -0
- package/resources/Player/modules/Drawer/index.tsx +1 -1
- package/resources/Player/modules/VideoAgent/VideoAgentScene.ts +9 -0
- package/resources/Player/modules/keyframes/BgMusic/index.tsx +66 -0
- package/resources/Player/modules/keyframes/InfoPanel/index.tsx +34 -3
- package/resources/Player/modules/keyframes/UpdateVRPanorama/index.tsx +63 -15
- package/resources/Player/typings.ts +5 -0
- package/resources/typings/VreoUnit.ts +31 -3
- package/stylesheets/default.css +35 -2
|
@@ -22,37 +22,76 @@ function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj &&
|
|
|
22
22
|
function UpdateVRPanorama() {
|
|
23
23
|
var controller = (0, _hooks.useController)();
|
|
24
24
|
var five = (0, _hooks.useFiveInstance)();
|
|
25
|
+
var rawRef = React.useRef(null);
|
|
26
|
+
var workRef = React.useRef(null);
|
|
27
|
+
var restoreCallback = React.useCallback(function () {
|
|
28
|
+
if (rawRef.current) {
|
|
29
|
+
var work = (0, _five.parseWork)(rawRef.current);
|
|
30
|
+
five.load(work);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
workRef.current = null;
|
|
34
|
+
}, []);
|
|
35
|
+
var pausedCallback = React.useCallback(function () {
|
|
36
|
+
if (rawRef.current) {
|
|
37
|
+
var work = (0, _five.parseWork)(rawRef.current);
|
|
38
|
+
five.load(work);
|
|
39
|
+
}
|
|
40
|
+
}, []);
|
|
41
|
+
var playingCallback = React.useCallback(function () {
|
|
42
|
+
if (!workRef.current) {
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
five.load(workRef.current);
|
|
47
|
+
}, []);
|
|
25
48
|
React.useEffect(function () {
|
|
26
49
|
var callback = function callback(keyframe) {
|
|
27
50
|
var _five$work;
|
|
28
51
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
52
|
+
if (!rawRef.current) {
|
|
53
|
+
rawRef.current = five.work.raw.works;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
var data = keyframe.data; // 如果数据中有新 work 数据,则直接载入
|
|
33
57
|
|
|
34
|
-
if (work) {
|
|
35
|
-
|
|
58
|
+
if (data.work) {
|
|
59
|
+
workRef.current = (0, _five.parseWork)(data.work);
|
|
60
|
+
five.load(workRef.current);
|
|
36
61
|
return;
|
|
37
62
|
}
|
|
38
63
|
|
|
39
|
-
|
|
40
|
-
var newWorkJSON = JSON.parse(JSON.stringify((_five$work = five.work) === null || _five$work === void 0 ? void 0 : _five$work.raw.work)); // // 原Work数据不能被修改
|
|
64
|
+
var lastRawWork = (_five$work = five.work) === null || _five$work === void 0 ? void 0 : _five$work.raw.works[0]; // 动态场景:不提供完成的签名数据,需重新整理
|
|
41
65
|
|
|
42
|
-
if (
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
66
|
+
if (data.dynamic_scene && !data.panorama) {
|
|
67
|
+
var lastWork = JSON.parse(lastRawWork);
|
|
68
|
+
var panorama = lastWork.panorama;
|
|
69
|
+
var index = data.dynamic_scene.images.index;
|
|
70
|
+
panorama.list[index] = data.dynamic_scene.images;
|
|
71
|
+
data.panorama = panorama;
|
|
72
|
+
delete data.dynamic_scene;
|
|
46
73
|
}
|
|
47
74
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
five.load(newWork);
|
|
75
|
+
workRef.current = (0, _five.parseWork)([lastRawWork, data]);
|
|
76
|
+
five.load(workRef.current);
|
|
51
77
|
};
|
|
52
78
|
|
|
53
|
-
controller.on(_VreoUnit.VreoKeyframeEnum.UpdateVRPanorama, callback);
|
|
79
|
+
controller.on(_VreoUnit.VreoKeyframeEnum.UpdateVRPanorama, callback); // 离开 将之前的内容清空
|
|
80
|
+
|
|
81
|
+
controller.on(_VreoUnit.VreoKeyframeEnum.Exit, restoreCallback); // 新载入数据 将之前的内容清空
|
|
82
|
+
|
|
83
|
+
controller.on('loaded', restoreCallback); // 暂停 回到默认状态
|
|
84
|
+
|
|
85
|
+
controller.on('paused', pausedCallback); // 播放 回归暂停前状态
|
|
86
|
+
|
|
87
|
+
controller.on('playing', playingCallback);
|
|
54
88
|
return function () {
|
|
89
|
+
// 清理事件监听
|
|
55
90
|
controller.off(_VreoUnit.VreoKeyframeEnum.UpdateVRPanorama, callback);
|
|
91
|
+
controller.off(_VreoUnit.VreoKeyframeEnum.Exit, restoreCallback);
|
|
92
|
+
controller.off('loaded', restoreCallback);
|
|
93
|
+
controller.off('paused', pausedCallback);
|
|
94
|
+
controller.off('playing', playingCallback);
|
|
56
95
|
};
|
|
57
96
|
}, [controller]);
|
|
58
97
|
return /*#__PURE__*/React.createElement(React.Fragment, null);
|
package/lib/Player/typings.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { ReactNode } from 'react';
|
|
1
2
|
import { VreoKeyframeConfigMap } from '../typings/VreoUnit';
|
|
2
3
|
export interface PlayerConfigs {
|
|
3
4
|
containter?: ReactDOM.Container;
|
|
@@ -11,4 +12,8 @@ export interface PlayerConfigs {
|
|
|
11
12
|
videoEffect?: HTMLVideoElement;
|
|
12
13
|
modelTVVideo?: HTMLVideoElement;
|
|
13
14
|
};
|
|
15
|
+
/**
|
|
16
|
+
* 如果需要在 底部面板添加自定义内容,可以传递个 React 组件。
|
|
17
|
+
*/
|
|
18
|
+
customPanelChildren?: ReactNode;
|
|
14
19
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Mode, Pose
|
|
1
|
+
import { Mode, Pose } from '@realsee/five';
|
|
2
2
|
import { CameraMovementEffect, Rotation } from '../fivePlugins/CameraMovementPlugin/typings';
|
|
3
3
|
export declare enum VreoKeyframeEnum {
|
|
4
4
|
/** 运镜 */
|
|
@@ -35,6 +35,10 @@ export declare enum VreoKeyframeEnum {
|
|
|
35
35
|
* 视频特效
|
|
36
36
|
*/
|
|
37
37
|
VideoEffect = "VideoEffect",
|
|
38
|
+
/**
|
|
39
|
+
* 背景音乐
|
|
40
|
+
*/
|
|
41
|
+
BgMusic = "BgMusic",
|
|
38
42
|
/**
|
|
39
43
|
* 结束
|
|
40
44
|
*/
|
|
@@ -57,6 +61,7 @@ export declare type VreoKeyframeConfigMap = {
|
|
|
57
61
|
* 剧本关键帧
|
|
58
62
|
*/
|
|
59
63
|
export interface VreoKeyframe {
|
|
64
|
+
uuid: string;
|
|
60
65
|
type: VreoKeyframeEnum;
|
|
61
66
|
start: number;
|
|
62
67
|
end: number;
|
|
@@ -89,6 +94,7 @@ export interface VreoUnit {
|
|
|
89
94
|
export declare type VreoKeyframeEvent = {
|
|
90
95
|
[key in VreoKeyframeEnum]: (keyframe: VreoKeyframe) => void;
|
|
91
96
|
} & {
|
|
97
|
+
loaded: (vreoUnit: VreoUnit) => void;
|
|
92
98
|
paused: () => void;
|
|
93
99
|
playing: () => void;
|
|
94
100
|
};
|
|
@@ -113,9 +119,22 @@ export declare type PrompterData = {
|
|
|
113
119
|
* VR 全景切换
|
|
114
120
|
*/
|
|
115
121
|
export declare type UpdateVRPanoramaData = {
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
122
|
+
_signature: string;
|
|
123
|
+
allow_hosts: string[];
|
|
124
|
+
certificate: string;
|
|
125
|
+
expire_at: string;
|
|
126
|
+
dynamic_scene?: {
|
|
127
|
+
images: {
|
|
128
|
+
index: number;
|
|
129
|
+
right: string;
|
|
130
|
+
left: string;
|
|
131
|
+
up: string;
|
|
132
|
+
down: string;
|
|
133
|
+
front: string;
|
|
134
|
+
back: string;
|
|
135
|
+
};
|
|
136
|
+
};
|
|
137
|
+
[key: string]: any;
|
|
119
138
|
};
|
|
120
139
|
/**
|
|
121
140
|
* 顶点
|
|
@@ -196,6 +215,8 @@ export declare enum InfoPanelTypeEnum {
|
|
|
196
215
|
export declare type InfoPanelData = {
|
|
197
216
|
type: InfoPanelTypeEnum;
|
|
198
217
|
url: string;
|
|
218
|
+
tilte?: string;
|
|
219
|
+
subTitle?: string;
|
|
199
220
|
};
|
|
200
221
|
/**
|
|
201
222
|
* 视频特效
|
|
@@ -207,5 +228,11 @@ export declare type VideoEffectData = {
|
|
|
207
228
|
direction?: Vertex;
|
|
208
229
|
vector?: Pick<Pose, 'longitude' | 'latitude'>;
|
|
209
230
|
};
|
|
231
|
+
/**
|
|
232
|
+
* 背景音乐
|
|
233
|
+
*/
|
|
234
|
+
export declare type BgMusicData = {
|
|
235
|
+
url: string;
|
|
236
|
+
};
|
|
210
237
|
/** 自定义序列帧 */
|
|
211
238
|
export declare type CustomData = Record<string, any>;
|
package/lib/typings/VreoUnit.js
CHANGED
|
@@ -21,6 +21,7 @@ exports.VreoKeyframeEnum = VreoKeyframeEnum;
|
|
|
21
21
|
VreoKeyframeEnum["PanoEffect"] = "PanoEffect";
|
|
22
22
|
VreoKeyframeEnum["InfoPanel"] = "InfoPanel";
|
|
23
23
|
VreoKeyframeEnum["VideoEffect"] = "VideoEffect";
|
|
24
|
+
VreoKeyframeEnum["BgMusic"] = "BgMusic";
|
|
24
25
|
VreoKeyframeEnum["Exit"] = "Exit";
|
|
25
26
|
VreoKeyframeEnum["Custom"] = "Custom";
|
|
26
27
|
})(VreoKeyframeEnum || (exports.VreoKeyframeEnum = VreoKeyframeEnum = {}));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@realsee/vreo",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0-alpha.1",
|
|
4
4
|
"description": "Vreo (VR Video 缩写) 是基于如视三维渲染引擎 Five 和 用户界面构建库 React 实现的如视 3D 空间剧本播放器。",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"realsee",
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
"packages": "node ./dev-tools/build-packages.js"
|
|
26
26
|
},
|
|
27
27
|
"peerDependencies": {
|
|
28
|
-
"@realsee/five": "^5.0.0-alpha.
|
|
28
|
+
"@realsee/five": "^5.0.0-alpha.103",
|
|
29
29
|
"react": "^16.0.0",
|
|
30
30
|
"react-dom": "^16.0.0"
|
|
31
31
|
},
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
"@babel/preset-env": "^7.16.4",
|
|
36
36
|
"@babel/preset-react": "^7.16.0",
|
|
37
37
|
"@babel/preset-typescript": "^7.16.0",
|
|
38
|
-
"@realsee/five": "^5.0.0-alpha.
|
|
38
|
+
"@realsee/five": "^5.0.0-alpha.103",
|
|
39
39
|
"@types/offscreencanvas": "^2019.6.4",
|
|
40
40
|
"@types/react": "^17.0.37",
|
|
41
41
|
"@types/react-dom": "^17.0.11",
|
package/resources/Player/App.tsx
CHANGED
|
@@ -13,6 +13,7 @@ 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 }) => {
|
|
18
19
|
return (
|
|
@@ -35,6 +36,7 @@ const AppView = observer(({ controller }: { controller: Controller }) => {
|
|
|
35
36
|
}}
|
|
36
37
|
/>
|
|
37
38
|
<Prompter />
|
|
39
|
+
{controller.configs?.customPanelChildren}
|
|
38
40
|
</div>
|
|
39
41
|
</div>
|
|
40
42
|
)
|
|
@@ -53,6 +55,7 @@ export function App() {
|
|
|
53
55
|
<PanoTextLabel />
|
|
54
56
|
<InfoPanel />
|
|
55
57
|
<PanoEffect />
|
|
58
|
+
<BgMusic />
|
|
56
59
|
</>
|
|
57
60
|
<AppView controller={controller} />
|
|
58
61
|
</>
|
|
@@ -58,6 +58,7 @@ export class Player extends Subscribe<VreoKeyframeEvent> {
|
|
|
58
58
|
async load(vreoUnit: VreoUnit, currentTime = 0) {
|
|
59
59
|
if (!this.controller.visible) {
|
|
60
60
|
this.controller.setVisible(true)
|
|
61
|
+
// 延迟 500ms 规避跟 DOM 动画冲突
|
|
61
62
|
await new Promise((resolve) => {
|
|
62
63
|
setTimeout(() => resolve(true), 500)
|
|
63
64
|
})
|
|
@@ -71,6 +72,8 @@ export class Player extends Subscribe<VreoKeyframeEvent> {
|
|
|
71
72
|
this.controller.vreoUnit = vreoUnit
|
|
72
73
|
this.controller.videoInstance?.pause()
|
|
73
74
|
|
|
75
|
+
// 新数据载入就绪
|
|
76
|
+
this.emit('loaded', vreoUnit)
|
|
74
77
|
if (vreoUnit.video.url) {
|
|
75
78
|
if (this.controller.videoAgentScene?.videoAgentMesh.videoInstance) {
|
|
76
79
|
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) {
|
|
@@ -31,10 +31,19 @@ export class VideoAgentScene {
|
|
|
31
31
|
}
|
|
32
32
|
|
|
33
33
|
run = () => {
|
|
34
|
+
const domElement = this.renderer.domElement
|
|
35
|
+
|
|
34
36
|
// 视频开始 1s 不渲染:规避某些设备黑屏闪烁问题。视频前 2s 是最好是静默的。
|
|
35
37
|
if (!(this.videoAgentMesh.paused || this.videoAgentMesh.freeze) && this.videoAgentMesh.currentTime > 1000) {
|
|
36
38
|
this.renderer.render(this.scene, this.camera)
|
|
37
39
|
}
|
|
40
|
+
|
|
41
|
+
// 兼容非视频场景
|
|
42
|
+
if (!this.videoAgentMesh.videoUrl?.endsWith('.mp4')) {
|
|
43
|
+
if (domElement.style.display !== 'none') domElement.style.display = 'none'
|
|
44
|
+
} else {
|
|
45
|
+
if (domElement.style.display !== 'block') domElement.style.display = 'block'
|
|
46
|
+
}
|
|
38
47
|
requestAnimationFrame(this.run)
|
|
39
48
|
}
|
|
40
49
|
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import * as React from 'react'
|
|
2
|
+
import { BgMusicData, VreoKeyframe, VreoKeyframeEnum } from '../../../../typings/VreoUnit'
|
|
3
|
+
import { useController } from '../../../hooks'
|
|
4
|
+
|
|
5
|
+
export function BgMusic() {
|
|
6
|
+
const controller = useController()
|
|
7
|
+
const timeoutIdRef = React.useRef<NodeJS.Timeout | null>(null)
|
|
8
|
+
const cleanTimeout = React.useCallback( () => {
|
|
9
|
+
if (timeoutIdRef.current) {
|
|
10
|
+
clearTimeout(timeoutIdRef.current)
|
|
11
|
+
timeoutIdRef.current = null
|
|
12
|
+
}
|
|
13
|
+
}, [])
|
|
14
|
+
|
|
15
|
+
React.useEffect(() => {
|
|
16
|
+
const audio = new Audio()
|
|
17
|
+
audio.setAttribute('id', 'vreo-singleton-bgmusic')
|
|
18
|
+
audio.style.display = 'none'
|
|
19
|
+
|
|
20
|
+
audio.setAttribute('playsinline', 'true')
|
|
21
|
+
audio.loop = true
|
|
22
|
+
document.body.append(audio)
|
|
23
|
+
|
|
24
|
+
const callback = (keyframe: VreoKeyframe) => {
|
|
25
|
+
const { start, end } = keyframe
|
|
26
|
+
const { url } = keyframe.data as BgMusicData
|
|
27
|
+
audio.src = url
|
|
28
|
+
const playCallback = () => {
|
|
29
|
+
audio.play()
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
audio.addEventListener('canplaythrough', playCallback)
|
|
33
|
+
|
|
34
|
+
cleanTimeout()
|
|
35
|
+
|
|
36
|
+
const cleanAudio = () => {
|
|
37
|
+
audio.pause()
|
|
38
|
+
audio.currentTime = 0
|
|
39
|
+
audio.src = ''
|
|
40
|
+
cleanTimeout()
|
|
41
|
+
audio.removeEventListener('canplaythrough', playCallback)
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const duration = (end - start) || 5000
|
|
45
|
+
|
|
46
|
+
timeoutIdRef.current = setTimeout(() => {
|
|
47
|
+
cleanAudio()
|
|
48
|
+
}, duration)
|
|
49
|
+
|
|
50
|
+
controller.once('paused', () => {
|
|
51
|
+
cleanAudio()
|
|
52
|
+
})
|
|
53
|
+
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
controller.on(VreoKeyframeEnum.BgMusic, callback)
|
|
57
|
+
|
|
58
|
+
return () => {
|
|
59
|
+
controller.off(VreoKeyframeEnum.BgMusic, callback)
|
|
60
|
+
document.body.removeChild(audio)
|
|
61
|
+
cleanTimeout()
|
|
62
|
+
}
|
|
63
|
+
})
|
|
64
|
+
|
|
65
|
+
return <></>
|
|
66
|
+
}
|
|
@@ -1,23 +1,42 @@
|
|
|
1
1
|
import * as React from 'react'
|
|
2
|
+
import { ReactNode } from 'react'
|
|
2
3
|
import { InfoPanelData, InfoPanelTypeEnum, VreoKeyframe, VreoKeyframeEnum } from '../../../../typings/VreoUnit'
|
|
3
4
|
import { useController } from '../../../hooks'
|
|
4
5
|
|
|
5
|
-
function InfoPanelImg({ url }: { url: string }) {
|
|
6
|
+
function InfoPanelImg({ url, children }: { url: string; children?: ReactNode }) {
|
|
6
7
|
return (
|
|
7
8
|
<div className="vreo-infoPanel vreo-infoPanel--img">
|
|
8
|
-
|
|
9
|
+
{children}
|
|
10
|
+
<div className="vreo-infoPanelImg" style={{backgroundImage: `url(${url})`}} ></div>
|
|
9
11
|
</div>
|
|
10
12
|
)
|
|
11
13
|
}
|
|
12
14
|
|
|
13
|
-
function InfoPanelVideo({ url }: { url: string }) {
|
|
15
|
+
function InfoPanelVideo({ url, children }: { url: string; children?: ReactNode }) {
|
|
14
16
|
return (
|
|
15
17
|
<div className="vreo-infoPanel vreo-infoPanel--video">
|
|
18
|
+
{children}
|
|
16
19
|
<video playsInline autoPlay className="vreo-infoPanelVideo" src={url} />
|
|
17
20
|
</div>
|
|
18
21
|
)
|
|
19
22
|
}
|
|
20
23
|
|
|
24
|
+
interface TitleProps {
|
|
25
|
+
title?: string
|
|
26
|
+
subTitle?: string
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const Title = (props: TitleProps) => {
|
|
30
|
+
if (!props.title && !props.subTitle) {
|
|
31
|
+
return null
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
return <div className="vreo-infoPanel-title">
|
|
35
|
+
<div className="vreo-infoPanel-t1">{props.title}</div>
|
|
36
|
+
<div className="vreo-infoPanel-t2">{props.subTitle}</div>
|
|
37
|
+
</div>
|
|
38
|
+
}
|
|
39
|
+
|
|
21
40
|
export function InfoPanel() {
|
|
22
41
|
const timeoutRef = React.useRef<NodeJS.Timeout | null>()
|
|
23
42
|
const controller = useController()
|
|
@@ -30,6 +49,8 @@ export function InfoPanel() {
|
|
|
30
49
|
const { start, end, data } = keyframe
|
|
31
50
|
|
|
32
51
|
const infoPanelData = data as InfoPanelData
|
|
52
|
+
|
|
53
|
+
|
|
33
54
|
|
|
34
55
|
if (infoPanelData.type === InfoPanelTypeEnum.Image) {
|
|
35
56
|
controller.openDrawer({ content: <InfoPanelImg url={infoPanelData.url} />, height: '60vh' })
|
|
@@ -40,6 +61,16 @@ export function InfoPanel() {
|
|
|
40
61
|
}
|
|
41
62
|
controller.on(VreoKeyframeEnum.InfoPanel, callback)
|
|
42
63
|
|
|
64
|
+
Object.assign(window, { $setInfoPanel: () => {
|
|
65
|
+
controller.openDrawer({ content: (
|
|
66
|
+
|
|
67
|
+
// <InfoPanelImg url='//vrlab-public.ljcdn.com/release/seesay/tools/cat_music___f68fb9bbe1f7cd6d00a16456dd0b09ad.gif'>
|
|
68
|
+
<InfoPanelImg url='http://vrlab-public.ljcdn.com/common/images/web/d94e1bd7-0311-4b20-9c41-a1294fe43554.png'>
|
|
69
|
+
<Title title="场景联动抓拍图片" subTitle={"2022.02.17 14:00"} />
|
|
70
|
+
</InfoPanelImg>
|
|
71
|
+
), height: '60vh' })
|
|
72
|
+
}})
|
|
73
|
+
|
|
43
74
|
return () => {
|
|
44
75
|
controller.off(VreoKeyframeEnum.InfoPanel, callback)
|
|
45
76
|
if (timeoutRef.current) {
|
|
@@ -1,40 +1,88 @@
|
|
|
1
1
|
import * as React from 'react'
|
|
2
|
-
import { parseWork,
|
|
2
|
+
import { parseWork, Work } from '@realsee/five'
|
|
3
3
|
import { useController, useFiveInstance } from '../../../hooks'
|
|
4
4
|
import { UpdateVRPanoramaData, VreoKeyframe, VreoKeyframeEnum } from '../../../../typings/VreoUnit'
|
|
5
5
|
|
|
6
6
|
export function UpdateVRPanorama() {
|
|
7
7
|
const controller = useController()
|
|
8
8
|
const five = useFiveInstance()
|
|
9
|
+
const rawRef = React.useRef<string[] | null>(null)
|
|
10
|
+
const workRef = React.useRef<Work | null>(null)
|
|
11
|
+
|
|
12
|
+
const restoreCallback = React.useCallback(() => {
|
|
13
|
+
if (rawRef.current) {
|
|
14
|
+
const work = parseWork(rawRef.current)
|
|
15
|
+
five.load(work)
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
workRef.current = null
|
|
19
|
+
}, [])
|
|
20
|
+
|
|
21
|
+
const pausedCallback = React.useCallback(() => {
|
|
22
|
+
if (rawRef.current) {
|
|
23
|
+
const work = parseWork(rawRef.current)
|
|
24
|
+
five.load(work)
|
|
25
|
+
}
|
|
26
|
+
}, [])
|
|
27
|
+
|
|
28
|
+
const playingCallback = React.useCallback(() => {
|
|
29
|
+
if (!workRef.current) {
|
|
30
|
+
return
|
|
31
|
+
}
|
|
32
|
+
five.load(workRef.current)
|
|
33
|
+
}, [])
|
|
9
34
|
|
|
10
35
|
React.useEffect(() => {
|
|
11
36
|
const callback = (keyframe: VreoKeyframe) => {
|
|
12
|
-
|
|
37
|
+
if (!rawRef.current) {
|
|
38
|
+
rawRef.current = five.work.raw.works
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const data = keyframe.data as UpdateVRPanoramaData
|
|
13
42
|
|
|
14
|
-
|
|
15
|
-
|
|
43
|
+
// 如果数据中有新 work 数据,则直接载入
|
|
44
|
+
if (data.work) {
|
|
45
|
+
workRef.current = parseWork(data.work)
|
|
46
|
+
five.load(workRef.current)
|
|
16
47
|
return
|
|
17
48
|
}
|
|
18
49
|
|
|
19
|
-
|
|
20
|
-
const newWorkJSON = JSON.parse(JSON.stringify(five.work?.raw.work))
|
|
50
|
+
const lastRawWork = five.work?.raw.works[0]
|
|
21
51
|
|
|
22
|
-
//
|
|
23
|
-
if (
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
52
|
+
// 动态场景:不提供完成的签名数据,需重新整理
|
|
53
|
+
if (data.dynamic_scene && !data.panorama) {
|
|
54
|
+
const lastWork = JSON.parse(lastRawWork)
|
|
55
|
+
const panorama = lastWork.panorama
|
|
56
|
+
const index = data.dynamic_scene.images.index
|
|
57
|
+
panorama.list[index] = data.dynamic_scene.images
|
|
58
|
+
data.panorama = panorama
|
|
59
|
+
delete data.dynamic_scene
|
|
27
60
|
}
|
|
28
61
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
// // 需要还原么?待定 ...
|
|
32
|
-
five.load(newWork)
|
|
62
|
+
workRef.current = parseWork([lastRawWork, data])
|
|
63
|
+
five.load(workRef.current)
|
|
33
64
|
}
|
|
34
65
|
|
|
35
66
|
controller.on(VreoKeyframeEnum.UpdateVRPanorama, callback)
|
|
67
|
+
|
|
68
|
+
// 离开 将之前的内容清空
|
|
69
|
+
controller.on(VreoKeyframeEnum.Exit, restoreCallback)
|
|
70
|
+
|
|
71
|
+
// 新载入数据 将之前的内容清空
|
|
72
|
+
controller.on('loaded', restoreCallback)
|
|
73
|
+
|
|
74
|
+
// 暂停 回到默认状态
|
|
75
|
+
controller.on('paused', pausedCallback)
|
|
76
|
+
// 播放 回归暂停前状态
|
|
77
|
+
controller.on('playing', playingCallback)
|
|
78
|
+
|
|
36
79
|
return () => {
|
|
80
|
+
// 清理事件监听
|
|
37
81
|
controller.off(VreoKeyframeEnum.UpdateVRPanorama, callback)
|
|
82
|
+
controller.off(VreoKeyframeEnum.Exit, restoreCallback)
|
|
83
|
+
controller.off('loaded', restoreCallback)
|
|
84
|
+
controller.off('paused', pausedCallback)
|
|
85
|
+
controller.off('playing', playingCallback)
|
|
38
86
|
}
|
|
39
87
|
}, [controller])
|
|
40
88
|
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { ReactNode } from 'react';
|
|
1
2
|
import { VreoKeyframeConfigMap } from '../typings/VreoUnit'
|
|
2
3
|
|
|
3
4
|
export interface PlayerConfigs {
|
|
@@ -12,4 +13,8 @@ export interface PlayerConfigs {
|
|
|
12
13
|
videoEffect?: HTMLVideoElement
|
|
13
14
|
modelTVVideo?: HTMLVideoElement
|
|
14
15
|
}
|
|
16
|
+
/**
|
|
17
|
+
* 如果需要在 底部面板添加自定义内容,可以传递个 React 组件。
|
|
18
|
+
*/
|
|
19
|
+
customPanelChildren?: ReactNode
|
|
15
20
|
}
|
|
@@ -36,6 +36,10 @@ export enum VreoKeyframeEnum {
|
|
|
36
36
|
* 视频特效
|
|
37
37
|
*/
|
|
38
38
|
VideoEffect = 'VideoEffect',
|
|
39
|
+
/**
|
|
40
|
+
* 背景音乐
|
|
41
|
+
*/
|
|
42
|
+
BgMusic = 'BgMusic',
|
|
39
43
|
/**
|
|
40
44
|
* 结束
|
|
41
45
|
*/
|
|
@@ -61,6 +65,7 @@ export type VreoKeyframeConfigMap = {
|
|
|
61
65
|
* 剧本关键帧
|
|
62
66
|
*/
|
|
63
67
|
export interface VreoKeyframe {
|
|
68
|
+
uuid: string
|
|
64
69
|
type: VreoKeyframeEnum
|
|
65
70
|
start: number
|
|
66
71
|
end: number
|
|
@@ -96,6 +101,7 @@ export interface VreoUnit {
|
|
|
96
101
|
* 剧本事件
|
|
97
102
|
*/
|
|
98
103
|
export type VreoKeyframeEvent = { [key in VreoKeyframeEnum]: (keyframe: VreoKeyframe) => void } & {
|
|
104
|
+
loaded: (vreoUnit: VreoUnit) => void
|
|
99
105
|
paused: () => void
|
|
100
106
|
playing: () => void
|
|
101
107
|
}
|
|
@@ -123,9 +129,22 @@ export type PrompterData = {
|
|
|
123
129
|
* VR 全景切换
|
|
124
130
|
*/
|
|
125
131
|
export type UpdateVRPanoramaData = {
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
132
|
+
_signature: string
|
|
133
|
+
allow_hosts: string[]
|
|
134
|
+
certificate: string
|
|
135
|
+
expire_at: string
|
|
136
|
+
dynamic_scene?: {
|
|
137
|
+
images: {
|
|
138
|
+
index: number
|
|
139
|
+
right: string
|
|
140
|
+
left: string
|
|
141
|
+
up: string
|
|
142
|
+
down: string
|
|
143
|
+
front: string
|
|
144
|
+
back: string
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
[key: string]: any
|
|
129
148
|
}
|
|
130
149
|
|
|
131
150
|
/**
|
|
@@ -216,6 +235,8 @@ export enum InfoPanelTypeEnum {
|
|
|
216
235
|
export type InfoPanelData = {
|
|
217
236
|
type: InfoPanelTypeEnum
|
|
218
237
|
url: string
|
|
238
|
+
tilte?: string
|
|
239
|
+
subTitle?: string
|
|
219
240
|
}
|
|
220
241
|
|
|
221
242
|
/**
|
|
@@ -229,5 +250,12 @@ export type VideoEffectData = {
|
|
|
229
250
|
vector?: Pick<Pose, 'longitude' | 'latitude'>
|
|
230
251
|
}
|
|
231
252
|
|
|
253
|
+
/**
|
|
254
|
+
* 背景音乐
|
|
255
|
+
*/
|
|
256
|
+
export type BgMusicData = {
|
|
257
|
+
url: string
|
|
258
|
+
}
|
|
259
|
+
|
|
232
260
|
/** 自定义序列帧 */
|
|
233
261
|
export type CustomData = Record<string, any>
|
package/stylesheets/default.css
CHANGED
|
@@ -489,14 +489,47 @@
|
|
|
489
489
|
width: 100%;
|
|
490
490
|
height: 100%;
|
|
491
491
|
display: flex;
|
|
492
|
-
justify-content: center;
|
|
493
492
|
align-items: center;
|
|
493
|
+
background-color: rgba(0, 0, 0, 0.75);
|
|
494
|
+
flex-direction: column;
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
.vreo-infoPanel::after {
|
|
498
|
+
content: '';
|
|
499
|
+
display: block;
|
|
500
|
+
width: 100%;
|
|
501
|
+
height: 20px;
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
.vreo-infoPanel-title {
|
|
505
|
+
height: 64px;
|
|
506
|
+
width: 100%;
|
|
507
|
+
color: white;
|
|
508
|
+
display: flex;
|
|
509
|
+
align-items: center;
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
.vreo-infoPanel-t1 {
|
|
513
|
+
font-size: 17px;
|
|
514
|
+
font-weight: 600;
|
|
515
|
+
padding-left: 26px;
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
.vreo-infoPanel-t2 {
|
|
519
|
+
font-size: 12px;
|
|
520
|
+
opacity: 0.6;
|
|
521
|
+
padding-right: 26px;
|
|
522
|
+
padding-left: 8px;
|
|
523
|
+
padding-top: 6px;
|
|
494
524
|
}
|
|
495
525
|
|
|
496
526
|
.vreo-infoPanelImg,
|
|
497
527
|
.vreo-infoPanelVideo {
|
|
528
|
+
flex: 1;
|
|
529
|
+
background-size: contain;
|
|
498
530
|
width: 100%;
|
|
499
|
-
|
|
531
|
+
background-repeat: no-repeat;
|
|
532
|
+
background-position: center center;
|
|
500
533
|
}
|
|
501
534
|
|
|
502
535
|
.vreo-panel--hidden .vreo-panel-inner {
|