@realsee/vreo 0.1.0
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/LICENSE +21 -0
- package/README.md +465 -0
- package/docs/.nojekyll +1 -0
- package/docs/assets/custom.css +52 -0
- package/docs/assets/highlight.css +127 -0
- package/docs/assets/icons.css +1043 -0
- package/docs/assets/icons.png +0 -0
- package/docs/assets/icons@2x.png +0 -0
- package/docs/assets/main.js +52 -0
- package/docs/assets/search.js +1 -0
- package/docs/assets/style.css +1388 -0
- package/docs/assets/widgets.png +0 -0
- package/docs/assets/widgets@2x.png +0 -0
- package/docs/classes/Player.Player-1.html +50 -0
- package/docs/enums/Player.InfoPanelTypeEnum.html +3 -0
- package/docs/enums/Player.PanoEffectEnum.html +5 -0
- package/docs/enums/Player.PanoTagEnum.html +3 -0
- package/docs/enums/Player.VreoKeyframeEnum.html +388 -0
- package/docs/enums/fivePlugins.CameraMovementEffect.html +1 -0
- package/docs/enums/fivePlugins.Rotation.html +1 -0
- package/docs/index.html +204 -0
- package/docs/interfaces/Player.PlayerConfigs.html +4 -0
- package/docs/interfaces/Player.Vertex.html +3 -0
- package/docs/interfaces/Player.VreoKeyframe.html +3 -0
- package/docs/interfaces/Player.VreoUnit.html +3 -0
- package/docs/interfaces/Player.VreoVideo.html +3 -0
- package/docs/interfaces/fivePlugins.CameraMovementOptsCallback.html +1 -0
- package/docs/interfaces/fivePlugins.CameraMovementPluginExportType.html +7 -0
- package/docs/interfaces/fivePlugins.CameraMovementPluginParameterType.html +1 -0
- package/docs/interfaces/fivePlugins.ModelTVVideoPluginData.html +1 -0
- package/docs/interfaces/fivePlugins.ModelTVVideoPluginExportType.html +1 -0
- package/docs/interfaces/fivePlugins.ModelTVVideoPluginParameterType.html +1 -0
- package/docs/interfaces/react.VreoActionCallbacks.html +15 -0
- package/docs/modules/Player.html +29 -0
- package/docs/modules/fivePlugins.html +3 -0
- package/docs/modules/react.html +1 -0
- package/docs/modules.html +1 -0
- package/lib/Player/App.d.ts +2 -0
- package/lib/Player/App.js +68 -0
- package/lib/Player/Controller.d.ts +39 -0
- package/lib/Player/Controller.js +226 -0
- package/lib/Player/hooks.d.ts +4 -0
- package/lib/Player/hooks.js +41 -0
- package/lib/Player/index.d.ts +15 -0
- package/lib/Player/index.js +191 -0
- package/lib/Player/modules/Drawer/index.d.ts +2 -0
- package/lib/Player/modules/Drawer/index.js +67 -0
- package/lib/Player/modules/VideoAgent/VideoAgentMesh.d.ts +30 -0
- package/lib/Player/modules/VideoAgent/VideoAgentMesh.js +324 -0
- package/lib/Player/modules/VideoAgent/VideoAgentScene.d.ts +10 -0
- package/lib/Player/modules/VideoAgent/VideoAgentScene.js +65 -0
- package/lib/Player/modules/VideoAgent/index.d.ts +4 -0
- package/lib/Player/modules/VideoAgent/index.js +44 -0
- package/lib/Player/modules/keyframes/CameraMovement/index.d.ts +2 -0
- package/lib/Player/modules/keyframes/CameraMovement/index.js +73 -0
- package/lib/Player/modules/keyframes/InfoPanel/index.d.ts +2 -0
- package/lib/Player/modules/keyframes/InfoPanel/index.js +89 -0
- package/lib/Player/modules/keyframes/ModelVideo/index.d.ts +2 -0
- package/lib/Player/modules/keyframes/ModelVideo/index.js +87 -0
- package/lib/Player/modules/keyframes/PanoEffect/index.d.ts +2 -0
- package/lib/Player/modules/keyframes/PanoEffect/index.js +142 -0
- package/lib/Player/modules/keyframes/PanoEffect/lineAnime.d.ts +5 -0
- package/lib/Player/modules/keyframes/PanoEffect/lineAnime.js +46 -0
- package/lib/Player/modules/keyframes/PanoTag/index.d.ts +2 -0
- package/lib/Player/modules/keyframes/PanoTag/index.js +168 -0
- package/lib/Player/modules/keyframes/PanoTextLabel/index.d.ts +2 -0
- package/lib/Player/modules/keyframes/PanoTextLabel/index.js +113 -0
- package/lib/Player/modules/keyframes/Prompter/index.d.ts +2 -0
- package/lib/Player/modules/keyframes/Prompter/index.js +105 -0
- package/lib/Player/modules/keyframes/UpdateVRPanorama/index.d.ts +2 -0
- package/lib/Player/modules/keyframes/UpdateVRPanorama/index.js +51 -0
- package/lib/Player/modules/keyframes/VideoEffect/index.d.ts +2 -0
- package/lib/Player/modules/keyframes/VideoEffect/index.js +176 -0
- package/lib/Player/typings.d.ts +14 -0
- package/lib/Player/typings.js +5 -0
- package/lib/fivePlugins/CameraMovementPlugin/index.d.ts +6 -0
- package/lib/fivePlugins/CameraMovementPlugin/index.js +307 -0
- package/lib/fivePlugins/CameraMovementPlugin/typings.d.ts +48 -0
- package/lib/fivePlugins/CameraMovementPlugin/typings.js +25 -0
- package/lib/fivePlugins/ModelTVVideoPlugin/index.d.ts +20 -0
- package/lib/fivePlugins/ModelTVVideoPlugin/index.js +305 -0
- package/lib/index.d.ts +1 -0
- package/lib/index.js +13 -0
- package/lib/react/index.d.ts +42 -0
- package/lib/react/index.js +120 -0
- package/lib/shared-utils/Preloader.d.ts +6 -0
- package/lib/shared-utils/Preloader.js +51 -0
- package/lib/shared-utils/animationFrame/BetterTween.d.ts +11 -0
- package/lib/shared-utils/animationFrame/BetterTween.js +102 -0
- package/lib/shared-utils/animationFrame/index.d.ts +15 -0
- package/lib/shared-utils/animationFrame/index.js +68 -0
- package/lib/shared-utils/animationFrame/tween.d.ts +75 -0
- package/lib/shared-utils/animationFrame/tween.js +76 -0
- package/lib/typings/VreoUnit.d.ts +204 -0
- package/lib/typings/VreoUnit.js +65 -0
- package/package.json +55 -0
- package/resources/Player/App.tsx +57 -0
- package/resources/Player/Controller.ts +154 -0
- package/resources/Player/hooks.ts +28 -0
- package/resources/Player/index.tsx +115 -0
- package/resources/Player/modules/Drawer/index.tsx +40 -0
- package/resources/Player/modules/VideoAgent/VideoAgentMesh.ts +202 -0
- package/resources/Player/modules/VideoAgent/VideoAgentScene.ts +38 -0
- package/resources/Player/modules/VideoAgent/index.tsx +31 -0
- package/resources/Player/modules/keyframes/CameraMovement/index.tsx +33 -0
- package/resources/Player/modules/keyframes/InfoPanel/index.tsx +52 -0
- package/resources/Player/modules/keyframes/ModelVideo/index.tsx +44 -0
- package/resources/Player/modules/keyframes/PanoEffect/index.tsx +99 -0
- package/resources/Player/modules/keyframes/PanoEffect/lineAnime.ts +31 -0
- package/resources/Player/modules/keyframes/PanoTag/index.tsx +127 -0
- package/resources/Player/modules/keyframes/PanoTextLabel/index.tsx +65 -0
- package/resources/Player/modules/keyframes/Prompter/index.tsx +65 -0
- package/resources/Player/modules/keyframes/UpdateVRPanorama/index.tsx +36 -0
- package/resources/Player/modules/keyframes/VideoEffect/index.tsx +115 -0
- package/resources/Player/typings.ts +15 -0
- package/resources/fivePlugins/CameraMovementPlugin/index.ts +203 -0
- package/resources/fivePlugins/CameraMovementPlugin/typings.ts +57 -0
- package/resources/fivePlugins/ModelTVVideoPlugin/index.ts +278 -0
- package/resources/index.ts +1 -0
- package/resources/react/index.tsx +95 -0
- package/resources/shared-utils/Preloader.ts +35 -0
- package/resources/shared-utils/animationFrame/BetterTween.ts +33 -0
- package/resources/shared-utils/animationFrame/index.ts +48 -0
- package/resources/shared-utils/animationFrame/tween.ts +51 -0
- package/resources/typings/VreoUnit.ts +225 -0
- package/stylesheets/default.css +497 -0
|
@@ -0,0 +1,57 @@
|
|
|
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
|
+
import { CameraMovement } from './modules/keyframes/CameraMovement'
|
|
7
|
+
import { InfoPanel } from './modules/keyframes/InfoPanel'
|
|
8
|
+
import { ModelVideo } from './modules/keyframes/ModelVideo'
|
|
9
|
+
import { PanoEffect } from './modules/keyframes/PanoEffect'
|
|
10
|
+
import { PanoTag } from './modules/keyframes/PanoTag'
|
|
11
|
+
import { PanoTextLabel } from './modules/keyframes/PanoTextLabel'
|
|
12
|
+
import { Prompter } from './modules/keyframes/Prompter'
|
|
13
|
+
import { UpdateVRPanorama } from './modules/keyframes/UpdateVRPanorama'
|
|
14
|
+
import { VideoEffect } from './modules/keyframes/VideoEffect'
|
|
15
|
+
import { VideoAgent } from './modules/VideoAgent'
|
|
16
|
+
|
|
17
|
+
const AppView = observer(({ controller }: { controller: Controller }) => {
|
|
18
|
+
return (
|
|
19
|
+
<div
|
|
20
|
+
className={classNames('vreo-panel', {
|
|
21
|
+
'vreo-panel--hidden': !controller.visible,
|
|
22
|
+
})}
|
|
23
|
+
>
|
|
24
|
+
<div className="vreo-panel-inner">
|
|
25
|
+
<VideoAgent
|
|
26
|
+
onClick={() => {
|
|
27
|
+
if (controller.playing) {
|
|
28
|
+
controller.setPlaying(false)
|
|
29
|
+
} else {
|
|
30
|
+
controller.setPlaying(true)
|
|
31
|
+
}
|
|
32
|
+
}}
|
|
33
|
+
/>
|
|
34
|
+
<Prompter />
|
|
35
|
+
</div>
|
|
36
|
+
</div>
|
|
37
|
+
)
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
export function App() {
|
|
41
|
+
const controller = useController()
|
|
42
|
+
return (
|
|
43
|
+
<>
|
|
44
|
+
<>
|
|
45
|
+
<CameraMovement />
|
|
46
|
+
<ModelVideo />
|
|
47
|
+
<UpdateVRPanorama />
|
|
48
|
+
<VideoEffect />
|
|
49
|
+
<PanoTag />
|
|
50
|
+
<PanoTextLabel />
|
|
51
|
+
<InfoPanel />
|
|
52
|
+
<PanoEffect />
|
|
53
|
+
</>
|
|
54
|
+
<AppView controller={controller} />
|
|
55
|
+
</>
|
|
56
|
+
)
|
|
57
|
+
}
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
import { Five, Subscribe } from '@realsee/five'
|
|
2
|
+
import { action, makeObservable, observable, reaction } from 'mobx'
|
|
3
|
+
import * as React from 'react'
|
|
4
|
+
import { requestAnimationFrameInterval } from '../shared-utils/animationFrame'
|
|
5
|
+
import { VreoKeyframe, VreoKeyframeEnum, VreoKeyframeEvent, VreoUnit } from '../typings/VreoUnit'
|
|
6
|
+
import { VideoAgentScene } from './modules/VideoAgent/VideoAgentScene'
|
|
7
|
+
import { PlayerConfigs } from './typings'
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* 逻辑控制器:内部状态。
|
|
11
|
+
*/
|
|
12
|
+
export class Controller extends Subscribe<VreoKeyframeEvent> {
|
|
13
|
+
|
|
14
|
+
$five?: Five
|
|
15
|
+
configs?: PlayerConfigs
|
|
16
|
+
videoAgentScene?: VideoAgentScene
|
|
17
|
+
vreoUnit?: VreoUnit
|
|
18
|
+
stopInterval?: () => void
|
|
19
|
+
playing = false
|
|
20
|
+
|
|
21
|
+
visible = false
|
|
22
|
+
|
|
23
|
+
drawerConfig?: {
|
|
24
|
+
content: string | JSX.Element
|
|
25
|
+
height?: number | string
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
setVisible(v: boolean) {
|
|
29
|
+
this.visible = v
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
setPlaying(playing: boolean) {
|
|
33
|
+
this.playing = playing
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
openDrawer(drawerConfig?: false | { content: string | JSX.Element; height?: number | string }) {
|
|
37
|
+
if (!drawerConfig) {
|
|
38
|
+
this.drawerConfig = {
|
|
39
|
+
content: '',
|
|
40
|
+
height: this.drawerConfig?.height
|
|
41
|
+
}
|
|
42
|
+
return
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
this.drawerConfig = drawerConfig
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
constructor() {
|
|
49
|
+
super()
|
|
50
|
+
|
|
51
|
+
makeObservable(this, {
|
|
52
|
+
visible: observable,
|
|
53
|
+
setVisible: action,
|
|
54
|
+
playing: observable,
|
|
55
|
+
setPlaying: action,
|
|
56
|
+
drawerConfig: observable.ref,
|
|
57
|
+
openDrawer: action,
|
|
58
|
+
|
|
59
|
+
})
|
|
60
|
+
|
|
61
|
+
// 监听播放情况:抛出触发时机
|
|
62
|
+
reaction(
|
|
63
|
+
() => this.playing,
|
|
64
|
+
(playing) => {
|
|
65
|
+
this.emit(playing ? 'playing' : 'paused')
|
|
66
|
+
}
|
|
67
|
+
)
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
get ready() {
|
|
71
|
+
return !!this.videoAgentScene
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
get currentTime() {
|
|
75
|
+
return this.videoAgentScene?.videoAgentMesh.currentTime || 0
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
get currentKeyframes() {
|
|
79
|
+
if (!this.vreoUnit) return []
|
|
80
|
+
const keyframes = this.vreoUnit.keyframes
|
|
81
|
+
// 没有被解析过且开始时间低于当前时间戳 100ms
|
|
82
|
+
return keyframes.filter((keyframe: VreoKeyframe) => {
|
|
83
|
+
if (keyframe.parsed) return false
|
|
84
|
+
const dur = this.currentTime - keyframe.start
|
|
85
|
+
return dur <= 100 && dur >= 0
|
|
86
|
+
})
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
get videoInstance() {
|
|
90
|
+
return this.videoAgentScene?.videoAgentMesh.videoInstance
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* 逐帧任务
|
|
95
|
+
*/
|
|
96
|
+
requestAnimationFrameLoop(callback: (type: VreoKeyframeEnum, keyframe: VreoKeyframe) => void) {
|
|
97
|
+
if (this.videoInstance?.ended) {
|
|
98
|
+
this.setPlaying(false)
|
|
99
|
+
this.videoInstance.pause()
|
|
100
|
+
return
|
|
101
|
+
}
|
|
102
|
+
if (!this.playing) {
|
|
103
|
+
if (!this.videoInstance?.paused) {
|
|
104
|
+
this.videoInstance?.pause()
|
|
105
|
+
}
|
|
106
|
+
return
|
|
107
|
+
}
|
|
108
|
+
if (this.videoInstance?.paused && this.playing) {
|
|
109
|
+
this.videoInstance.play()
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
const currentKeyframes = this.currentKeyframes
|
|
113
|
+
|
|
114
|
+
currentKeyframes.forEach((keyframe) => {
|
|
115
|
+
if (keyframe.parsed) return
|
|
116
|
+
keyframe.parsed = true
|
|
117
|
+
this.emit(keyframe.type, keyframe)
|
|
118
|
+
if (callback) {
|
|
119
|
+
callback(keyframe.type, keyframe)
|
|
120
|
+
}
|
|
121
|
+
})
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
run(callback: (type: VreoKeyframeEnum, keyframe: VreoKeyframe) => void) {
|
|
125
|
+
if (this.stopInterval) return
|
|
126
|
+
this.stopInterval = requestAnimationFrameInterval(() => this.requestAnimationFrameLoop(callback))
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
dispose() {
|
|
130
|
+
this.setPlaying(false)
|
|
131
|
+
|
|
132
|
+
if (this.currentKeyframes) {
|
|
133
|
+
this.currentKeyframes.forEach((keyframe) => (keyframe.parsed = false))
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* 清理掉数据
|
|
138
|
+
*/
|
|
139
|
+
this.vreoUnit = undefined
|
|
140
|
+
if (this.videoInstance) {
|
|
141
|
+
this.videoInstance.currentTime = 0
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
if (this.stopInterval) {
|
|
145
|
+
this.stopInterval()
|
|
146
|
+
this.stopInterval = undefined
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
export const ControllerContext = React.createContext<Controller | null>(null)
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import { Vector3 } from 'three'
|
|
3
|
+
import { ControllerContext } from './Controller'
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
export function useController() {
|
|
8
|
+
const controller = React.useContext(ControllerContext)
|
|
9
|
+
if (!controller) {
|
|
10
|
+
throw new Error('没有找到 "ControllerContext"')
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
return controller
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function useFiveInstance() {
|
|
17
|
+
const controller = useController()
|
|
18
|
+
if (!controller.$five) {
|
|
19
|
+
throw new Error('没有找到 "five" 实例')
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
return controller.$five
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function useFiveProject2d() {
|
|
26
|
+
const five = useFiveInstance()
|
|
27
|
+
return (vector: Vector3) => five.project2d(vector, false)
|
|
28
|
+
}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import * as React from 'react'
|
|
2
|
+
import * as ReactDOM from 'react-dom'
|
|
3
|
+
import { Five, Subscribe } from '@realsee/five'
|
|
4
|
+
|
|
5
|
+
import { App } from './App'
|
|
6
|
+
import { Controller, ControllerContext } from './Controller'
|
|
7
|
+
import { VreoKeyframeEvent, VreoKeyframeConfigMap, VreoUnit } from '../typings/VreoUnit'
|
|
8
|
+
import { reaction } from 'mobx'
|
|
9
|
+
import { Drawer } from './modules/Drawer'
|
|
10
|
+
import { PlayerConfigs } from './typings'
|
|
11
|
+
|
|
12
|
+
const controller = new Controller()
|
|
13
|
+
|
|
14
|
+
export class Player extends Subscribe<VreoKeyframeEvent> {
|
|
15
|
+
$five: Five
|
|
16
|
+
configs: Readonly<PlayerConfigs>
|
|
17
|
+
|
|
18
|
+
constructor(five: Five, configs: Partial<PlayerConfigs> = {}) {
|
|
19
|
+
super()
|
|
20
|
+
this.$five = controller.$five = five
|
|
21
|
+
|
|
22
|
+
if (!configs.containter) {
|
|
23
|
+
const containter = document.createElement('div')
|
|
24
|
+
containter.setAttribute('id', 'vreo-app')
|
|
25
|
+
configs.containter = containter
|
|
26
|
+
document.body.append(containter)
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
this.configs = Object.freeze(
|
|
30
|
+
Object.assign(
|
|
31
|
+
{
|
|
32
|
+
keyframeMap: {},
|
|
33
|
+
},
|
|
34
|
+
configs
|
|
35
|
+
)
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
controller.configs = this.configs
|
|
39
|
+
|
|
40
|
+
ReactDOM.render(
|
|
41
|
+
<ControllerContext.Provider value={controller}>
|
|
42
|
+
<App></App>
|
|
43
|
+
<Drawer />
|
|
44
|
+
</ControllerContext.Provider>,
|
|
45
|
+
configs.containter
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
// 监听播放情况:抛出触发时机
|
|
49
|
+
reaction(
|
|
50
|
+
() => controller.playing,
|
|
51
|
+
(playing) => {
|
|
52
|
+
this.emit(playing ? 'playing' : 'paused')
|
|
53
|
+
}
|
|
54
|
+
)
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
async load(vreoUnit: VreoUnit, currentTime = 0) {
|
|
58
|
+
if (controller.stopInterval) {
|
|
59
|
+
controller.stopInterval()
|
|
60
|
+
controller.stopInterval = undefined
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
controller.vreoUnit = vreoUnit
|
|
64
|
+
controller.videoInstance?.pause()
|
|
65
|
+
|
|
66
|
+
if (vreoUnit.video.url) {
|
|
67
|
+
if (controller.videoAgentScene?.videoAgentMesh.videoInstance) {
|
|
68
|
+
controller.videoAgentScene.videoAgentMesh.videoInstance.currentTime = currentTime / 1000
|
|
69
|
+
}
|
|
70
|
+
await controller.videoAgentScene?.videoAgentMesh.play(vreoUnit.video.url)
|
|
71
|
+
this.play()
|
|
72
|
+
}
|
|
73
|
+
controller.run((type, keyframe) => this.emit(type, keyframe))
|
|
74
|
+
return true
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
get paused() {
|
|
78
|
+
return !controller.playing
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
play() {
|
|
82
|
+
if (controller.playing) return true
|
|
83
|
+
controller.setPlaying(true)
|
|
84
|
+
return true
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
pause() {
|
|
88
|
+
controller.setPlaying(false)
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
show() {
|
|
92
|
+
controller.setVisible(true)
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
hide() {
|
|
96
|
+
controller.setVisible(false)
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
dispose() {
|
|
100
|
+
controller.dispose()
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
console.log(`
|
|
105
|
+
|
|
106
|
+
┏━━━┓━━━━━━━━━┏┓━━━━━━━━━━━━━
|
|
107
|
+
┃┏━┓┃━━━━━━━━━┃┃━━━━━━━━━━━━━
|
|
108
|
+
┃┗━┛┃┏━━┓┏━━┓━┃┃━┏━━┓┏━━┓┏━━┓
|
|
109
|
+
┃┏┓┏┛┃┏┓┃┗━┓┃━┃┃━┃━━┫┃┏┓┃┃┏┓┃
|
|
110
|
+
┃┃┃┗┓┃┃━┫┃┗┛┗┓┃┗┓┣━━┃┃┃━┫┃┃━┫
|
|
111
|
+
┗┛┗━┛┗━━┛┗━━━┛┗━┛┗━━┛┗━━┛┗━━┛
|
|
112
|
+
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
113
|
+
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
114
|
+
|
|
115
|
+
`)
|
|
@@ -0,0 +1,40 @@
|
|
|
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 DrawerView = observer(({ controller }: { controller: Controller }) => {
|
|
8
|
+
const maxHeight = '540px'
|
|
9
|
+
const height = (() => {
|
|
10
|
+
const height = controller.drawerConfig?.height
|
|
11
|
+
if (!height) {
|
|
12
|
+
return 'max-content'
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
if (typeof height === 'number') {
|
|
16
|
+
return height + 'px'
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
return height
|
|
20
|
+
})()
|
|
21
|
+
|
|
22
|
+
return (
|
|
23
|
+
<div
|
|
24
|
+
className={classNames('vreo-drawer', {
|
|
25
|
+
'vreo-drawer-visible': controller.drawerConfig && controller.drawerConfig.content,
|
|
26
|
+
})}
|
|
27
|
+
onClick={() => controller.openDrawer(false)}
|
|
28
|
+
>
|
|
29
|
+
<div className="vreo-drawer-inner" style={{ height, maxHeight }}>
|
|
30
|
+
{controller.drawerConfig?.content || ''}
|
|
31
|
+
</div>
|
|
32
|
+
</div>
|
|
33
|
+
)
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
export function Drawer() {
|
|
37
|
+
const controller = useController()
|
|
38
|
+
|
|
39
|
+
return <DrawerView controller={controller} />
|
|
40
|
+
}
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
import * as THREE from 'three'
|
|
2
|
+
import { Preloader } from '../../../shared-utils/Preloader'
|
|
3
|
+
import { makeObservable, observable, runInAction } from 'mobx'
|
|
4
|
+
|
|
5
|
+
const vertexShader = `
|
|
6
|
+
varying vec2 vUv;
|
|
7
|
+
varying vec2 vColorUv;
|
|
8
|
+
varying vec2 vMaskUv;
|
|
9
|
+
void main(){
|
|
10
|
+
vUv = uv;
|
|
11
|
+
vColorUv = vec2(uv.x / 2.0, uv.y);
|
|
12
|
+
vMaskUv = vec2(0.5 + uv.x / 2.0, uv.y);
|
|
13
|
+
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
|
|
14
|
+
}
|
|
15
|
+
`
|
|
16
|
+
const fragmentShaderTpl = () => `
|
|
17
|
+
uniform int enable;
|
|
18
|
+
uniform sampler2D map;
|
|
19
|
+
varying vec2 vUv;
|
|
20
|
+
varying vec2 vColorUv;
|
|
21
|
+
varying vec2 vMaskUv;
|
|
22
|
+
|
|
23
|
+
vec3 hsv2rgb(vec3 c) {
|
|
24
|
+
const vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0);
|
|
25
|
+
vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www);
|
|
26
|
+
return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
vec3 rgb2hsv(vec3 c) {
|
|
30
|
+
const vec4 K = vec4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0);
|
|
31
|
+
vec4 p = mix(vec4(c.bg, K.wz), vec4(c.gb, K.xy), step(c.b, c.g));
|
|
32
|
+
vec4 q = mix(vec4(p.xyw, c.r), vec4(c.r, p.yzx), step(p.x, c.r));
|
|
33
|
+
|
|
34
|
+
float d = q.x - min(q.w, q.y);
|
|
35
|
+
return vec3(abs(q.z + (q.w - q.y) / (6.0 * d + 0.001)), d / (q.x + 0.001), q.x);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
void main(void) {
|
|
39
|
+
vec3 color = texture2D(map, vUv).xyz;
|
|
40
|
+
float amask = 1.0;
|
|
41
|
+
|
|
42
|
+
float ref = color.r;
|
|
43
|
+
if (ref < color.b) ref = color.b;
|
|
44
|
+
amask = color.g - ref + 0.1;
|
|
45
|
+
amask = smoothstep(0.1, 0.3, amask);
|
|
46
|
+
amask = 1.0 - amask;
|
|
47
|
+
|
|
48
|
+
vec3 target = rgb2hsv(vec3(0.0 / 255.0, 255.0 / 255.0, 0.0 / 255.0));
|
|
49
|
+
vec3 hsv = rgb2hsv(color);
|
|
50
|
+
float distance = abs(hsv.x - target.x);
|
|
51
|
+
if (distance < 0.15) {
|
|
52
|
+
hsv.y = 0.0;
|
|
53
|
+
}
|
|
54
|
+
color = hsv2rgb(hsv);
|
|
55
|
+
|
|
56
|
+
gl_FragColor = vec4(color.rgb, amask * float(enable));
|
|
57
|
+
}
|
|
58
|
+
`
|
|
59
|
+
|
|
60
|
+
export interface VideoAgentMeshOptions {
|
|
61
|
+
videoInstance?: HTMLVideoElement
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* 视频经纪人MESH
|
|
66
|
+
*/
|
|
67
|
+
export class VideoAgentMesh extends THREE.Mesh {
|
|
68
|
+
options: VideoAgentMeshOptions
|
|
69
|
+
videoUrl?: string
|
|
70
|
+
freeze: boolean
|
|
71
|
+
paused: boolean
|
|
72
|
+
audioInstance: HTMLAudioElement
|
|
73
|
+
|
|
74
|
+
get videoInstance(): HTMLAudioElement {
|
|
75
|
+
if (this.videoUrl?.endsWith('mp3')) {
|
|
76
|
+
return this.audioInstance
|
|
77
|
+
}
|
|
78
|
+
const uniforms = (this.material as THREE.ShaderMaterial).uniforms
|
|
79
|
+
const videoInstance = uniforms.map.value.image as HTMLVideoElement
|
|
80
|
+
return videoInstance
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
*
|
|
85
|
+
* @param width
|
|
86
|
+
* @param height
|
|
87
|
+
* @param widthSegments
|
|
88
|
+
* @param heightSegments
|
|
89
|
+
* @param options
|
|
90
|
+
*/
|
|
91
|
+
constructor(
|
|
92
|
+
width: number,
|
|
93
|
+
height: number,
|
|
94
|
+
widthSegments: number,
|
|
95
|
+
heightSegments: number,
|
|
96
|
+
options: VideoAgentMeshOptions = {},
|
|
97
|
+
) {
|
|
98
|
+
if (!options.videoInstance) {
|
|
99
|
+
const videoInstance = document.createElement('video')
|
|
100
|
+
videoInstance.style.opacity = '0'
|
|
101
|
+
videoInstance.style.display = 'none'
|
|
102
|
+
document.body.append(videoInstance)
|
|
103
|
+
options.videoInstance = videoInstance
|
|
104
|
+
videoInstance.playsInline = true
|
|
105
|
+
videoInstance.controls = false
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
const geometry = new THREE.PlaneGeometry(width, height, widthSegments, heightSegments)
|
|
109
|
+
const videoTexture = new THREE.VideoTexture(options.videoInstance)
|
|
110
|
+
const material = new THREE.ShaderMaterial({
|
|
111
|
+
uniforms: {
|
|
112
|
+
map: { value: videoTexture },
|
|
113
|
+
enable: { value: 0 },
|
|
114
|
+
},
|
|
115
|
+
vertexShader,
|
|
116
|
+
fragmentShader: fragmentShaderTpl(),
|
|
117
|
+
transparent: true,
|
|
118
|
+
})
|
|
119
|
+
|
|
120
|
+
super(geometry, material)
|
|
121
|
+
this.options = options
|
|
122
|
+
this.freeze = false
|
|
123
|
+
this.paused = true
|
|
124
|
+
|
|
125
|
+
{
|
|
126
|
+
const audioInstance = document.createElement('audio')
|
|
127
|
+
audioInstance.crossOrigin = ''
|
|
128
|
+
// videoInstance.muted = true
|
|
129
|
+
audioInstance.muted = false
|
|
130
|
+
audioInstance.setAttribute('playsinline', 'true')
|
|
131
|
+
audioInstance.setAttribute('webkit-playsinline', 'true')
|
|
132
|
+
audioInstance.setAttribute('autoplay', 'false')
|
|
133
|
+
audioInstance.setAttribute('style', 'display: none;')
|
|
134
|
+
document.body.appendChild(audioInstance)
|
|
135
|
+
this.audioInstance = audioInstance
|
|
136
|
+
}
|
|
137
|
+
makeObservable(this, { paused: observable })
|
|
138
|
+
|
|
139
|
+
const updatePaused = (paused: boolean) => runInAction(() => (this.paused = paused))
|
|
140
|
+
this.videoInstance.addEventListener('pause', () => updatePaused(true))
|
|
141
|
+
this.videoInstance.addEventListener('play', () => updatePaused(false))
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
private async update(videoUrl: string) {
|
|
145
|
+
if (this.videoUrl === videoUrl) {
|
|
146
|
+
return
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
this.videoUrl = videoUrl
|
|
150
|
+
this.freeze = true
|
|
151
|
+
await this.videoInstance.pause()
|
|
152
|
+
|
|
153
|
+
const uniforms = (this.material as THREE.ShaderMaterial).uniforms
|
|
154
|
+
this.videoInstance.muted = true
|
|
155
|
+
this.videoInstance.src = await URL.createObjectURL((await Preloader.blob(this.videoUrl)) as unknown as Blob)
|
|
156
|
+
this.videoInstance.setAttribute('data-src', this.videoUrl)
|
|
157
|
+
|
|
158
|
+
const onStart = () => {
|
|
159
|
+
if (this.videoInstance.currentTime === 0) return
|
|
160
|
+
this.freeze = false
|
|
161
|
+
this.videoInstance.muted = false
|
|
162
|
+
uniforms.enable.value = 1
|
|
163
|
+
this.videoInstance.removeEventListener('timeupdate', onStart, false)
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
this.videoInstance.addEventListener('timeupdate', onStart, false)
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
async play(videoUrl = '') {
|
|
170
|
+
videoUrl = (videoUrl || '').replace(/https*:/, '')
|
|
171
|
+
if (!videoUrl) {
|
|
172
|
+
if (this.videoUrl) await this.videoInstance.play()
|
|
173
|
+
else console.warn('警告:视频资源未初始化。')
|
|
174
|
+
return true
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
if (videoUrl === this.videoUrl) {
|
|
178
|
+
this.videoInstance.currentTime = 0
|
|
179
|
+
await this.videoInstance.play()
|
|
180
|
+
return true
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
await this.update(videoUrl)
|
|
184
|
+
|
|
185
|
+
this.videoInstance.pause()
|
|
186
|
+
|
|
187
|
+
return await new Promise((resolve) =>
|
|
188
|
+
setTimeout(async () => {
|
|
189
|
+
this.videoInstance.currentTime = 0
|
|
190
|
+
await this.videoInstance.play()
|
|
191
|
+
resolve(true)
|
|
192
|
+
}, 20),
|
|
193
|
+
)
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* 转成毫秒,保障精准度
|
|
198
|
+
*/
|
|
199
|
+
get currentTime() {
|
|
200
|
+
return this.videoInstance.currentTime * 1000
|
|
201
|
+
}
|
|
202
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import * as THREE from 'three'
|
|
2
|
+
import { VideoAgentMesh } from './VideoAgentMesh'
|
|
3
|
+
|
|
4
|
+
export class VideoAgentScene {
|
|
5
|
+
videoAgentMesh: VideoAgentMesh
|
|
6
|
+
|
|
7
|
+
scene = new THREE.Scene()
|
|
8
|
+
camera = new THREE.OrthographicCamera(-240, 240, 135, -135)
|
|
9
|
+
renderer = new THREE.WebGLRenderer({ alpha: true })
|
|
10
|
+
|
|
11
|
+
constructor(container: HTMLElement, needRender = true) {
|
|
12
|
+
this.camera.position.set(0, 0, 10)
|
|
13
|
+
this.camera.lookAt(0, 0, 0)
|
|
14
|
+
const state = {
|
|
15
|
+
width: container.offsetWidth,
|
|
16
|
+
height: container.offsetHeight,
|
|
17
|
+
}
|
|
18
|
+
const domElement = this.renderer.domElement
|
|
19
|
+
domElement.classList.add('VideoAgent-canvas')
|
|
20
|
+
this.renderer.setSize(state.width * window.devicePixelRatio, state.height * window.devicePixelRatio)
|
|
21
|
+
domElement.setAttribute('style', `width: ${state.width}px;height: ${state.height}px;`)
|
|
22
|
+
|
|
23
|
+
this.videoAgentMesh = new VideoAgentMesh(480, 270, 1, 1)
|
|
24
|
+
if (needRender) {
|
|
25
|
+
container.append(domElement)
|
|
26
|
+
this.scene.add(this.videoAgentMesh)
|
|
27
|
+
this.run()
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
run = () => {
|
|
32
|
+
// 视频开始 1s 不渲染:规避某些设备黑屏闪烁问题。视频前 2s 是最好是静默的。
|
|
33
|
+
if (!(this.videoAgentMesh.paused || this.videoAgentMesh.freeze) && this.videoAgentMesh.currentTime > 1000) {
|
|
34
|
+
this.renderer.render(this.scene, this.camera)
|
|
35
|
+
}
|
|
36
|
+
requestAnimationFrame(this.run)
|
|
37
|
+
}
|
|
38
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import * as React from 'react'
|
|
2
|
+
import { useController } from '../../hooks'
|
|
3
|
+
import { VideoAgentScene } from './VideoAgentScene'
|
|
4
|
+
|
|
5
|
+
export function VideoAgent(props: { onClick?: () => void }) {
|
|
6
|
+
const ref = React.useRef<HTMLDivElement>(null)
|
|
7
|
+
const controller = useController()
|
|
8
|
+
|
|
9
|
+
React.useEffect(() => {
|
|
10
|
+
if (!ref.current) throw new Error('React 渲染异常,请稍后重试 ...')
|
|
11
|
+
if (controller.videoAgentScene) {
|
|
12
|
+
console.warn('"VideoAgentScene" 重复初始化,已被过滤 ...')
|
|
13
|
+
return
|
|
14
|
+
}
|
|
15
|
+
const videoAgentScene = new VideoAgentScene(ref.current)
|
|
16
|
+
controller.videoAgentScene = videoAgentScene
|
|
17
|
+
}, [])
|
|
18
|
+
|
|
19
|
+
return (
|
|
20
|
+
<div
|
|
21
|
+
className="VideoAgent"
|
|
22
|
+
onClick={() => {
|
|
23
|
+
if (!props.onClick) return
|
|
24
|
+
props.onClick()
|
|
25
|
+
}}
|
|
26
|
+
ref={ref}
|
|
27
|
+
>
|
|
28
|
+
<div className="VideoAgent-play"></div>
|
|
29
|
+
</div>
|
|
30
|
+
)
|
|
31
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import * as React from 'react'
|
|
2
|
+
import { CameraMovementPlugin } from '../../../../fivePlugins/CameraMovementPlugin'
|
|
3
|
+
import { CameraMovementEffect, RotateArgs } from '../../../../fivePlugins/CameraMovementPlugin/typings'
|
|
4
|
+
import { CameraMovementData, VreoKeyframe, VreoKeyframeEnum } from '../../../../typings/VreoUnit'
|
|
5
|
+
import { useController, useFiveInstance } from '../../../hooks'
|
|
6
|
+
|
|
7
|
+
export function CameraMovement() {
|
|
8
|
+
const controller = useController()
|
|
9
|
+
const five = useFiveInstance()
|
|
10
|
+
const ref = React.useRef<ReturnType<typeof CameraMovementPlugin>>()
|
|
11
|
+
|
|
12
|
+
React.useEffect(() => {
|
|
13
|
+
const callback = async (keyframe: VreoKeyframe) => {
|
|
14
|
+
if (!ref.current) {
|
|
15
|
+
ref.current = CameraMovementPlugin(five, {})
|
|
16
|
+
}
|
|
17
|
+
const { start, end, data } = keyframe
|
|
18
|
+
const cameraMovementData = data as CameraMovementData
|
|
19
|
+
const effect = cameraMovementData.effect
|
|
20
|
+
if (effect === CameraMovementEffect.Rotate) {
|
|
21
|
+
ref.current.rotate(data as RotateArgs, end - start)
|
|
22
|
+
} else {
|
|
23
|
+
ref.current.move(data, end - start)
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
controller.on(VreoKeyframeEnum.CameraMovement, callback)
|
|
28
|
+
return () => {
|
|
29
|
+
controller.off(VreoKeyframeEnum.CameraMovement, callback)
|
|
30
|
+
}
|
|
31
|
+
}, [controller])
|
|
32
|
+
return <></>
|
|
33
|
+
}
|