@realsee/vreo 0.2.0-alpha.8 → 1.0.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.
Files changed (143) hide show
  1. package/CHANGELOG.md +30 -0
  2. package/README.md +34 -3
  3. package/docs/assets/highlight.css +1 -1
  4. package/docs/assets/main.js +2 -2
  5. package/docs/assets/search.js +1 -1
  6. package/docs/assets/style.css +28 -2
  7. package/docs/classes/Player.AudioLike.html +52 -0
  8. package/docs/classes/Player.Controller.html +13 -13
  9. package/docs/classes/Player.Player-1.html +11 -11
  10. package/docs/classes/Player.VideoAgentMesh.html +53 -53
  11. package/docs/classes/Player.VideoAgentScene.html +1 -1
  12. package/docs/demo/assets/AppReact.d0ee843a.js +1 -0
  13. package/docs/demo/assets/default.63925584.js +3984 -0
  14. package/docs/demo/assets/default.f3f3ca8c.css +1 -0
  15. package/docs/demo/assets/dynamic.76506c2c.js +1 -0
  16. package/docs/demo/assets/examples.3d8323db.js +25 -0
  17. package/docs/demo/assets/examples.dac4a6fe.css +1 -0
  18. package/docs/demo/assets/index.7e31122c.js +1 -0
  19. package/docs/demo/assets/index.9331038f.css +1 -0
  20. package/docs/demo/assets/main.79b11b9d.js +1 -0
  21. package/docs/demo/assets/react.2ee8565f.css +1 -0
  22. package/docs/demo/assets/react.c1c07c14.js +25 -0
  23. package/docs/demo/assets/znRoyv06SZQeqA7m.0057e8ae.js +1 -0
  24. package/docs/demo/examples.html +17 -0
  25. package/docs/demo/index-react-dynamic.html +20 -0
  26. package/docs/demo/index-react.html +20 -0
  27. package/docs/demo/index.html +19 -0
  28. package/docs/enums/Player.InfoPanelStyleEnum.html +1 -1
  29. package/docs/enums/Player.InfoPanelTypeEnum.html +2 -2
  30. package/docs/enums/Player.PanoEffectEnum.html +2 -2
  31. package/docs/enums/Player.PanoTagEnum.html +2 -2
  32. package/docs/enums/Player.PanoTagStyleEnum.html +7 -0
  33. package/docs/enums/Player.VreoKeyframeEnum.html +12 -12
  34. package/docs/enums/fivePlugins.CameraMovementEffect.html +1 -1
  35. package/docs/enums/fivePlugins.Rotation.html +1 -1
  36. package/docs/index.html +13 -6
  37. package/docs/interfaces/Player.CustomVreoKeyframeProps.html +1 -1
  38. package/docs/interfaces/Player.PlayerConfigs.html +3 -3
  39. package/docs/interfaces/Player.Vertex.html +2 -2
  40. package/docs/interfaces/Player.VideoAgentMeshOptions.html +8 -1
  41. package/docs/interfaces/Player.VreoKeyframe.html +2 -2
  42. package/docs/interfaces/Player.VreoUnit.html +2 -2
  43. package/docs/interfaces/Player.VreoVideo.html +2 -2
  44. package/docs/interfaces/fivePlugins.CSS3DRenderPluginExportType.html +4 -0
  45. package/docs/interfaces/fivePlugins.CameraMovementOptsCallback.html +1 -1
  46. package/docs/interfaces/fivePlugins.CameraMovementPluginExportType.html +2 -2
  47. package/docs/interfaces/fivePlugins.CameraMovementPluginParameterType.html +1 -1
  48. package/docs/interfaces/fivePlugins.ModelTVVideoPluginData.html +1 -1
  49. package/docs/interfaces/fivePlugins.ModelTVVideoPluginExportType.html +1 -1
  50. package/docs/interfaces/fivePlugins.ModelTVVideoPluginParameterType.html +1 -1
  51. package/docs/interfaces/fivePlugins.Vector3Position.html +3 -0
  52. package/docs/interfaces/react.VreoActionCallbacks.html +15 -7
  53. package/docs/interfaces/react.VreoProviderProps.html +1 -1
  54. package/docs/modules/Player.html +21 -15
  55. package/docs/modules/custom.html +1 -1
  56. package/docs/modules/fivePlugins.html +5 -2
  57. package/docs/modules/react.html +1 -1
  58. package/docs/modules.html +1 -1
  59. package/lib/PlayController/index.js +3 -3
  60. package/lib/Player/App.js +5 -4
  61. package/lib/Player/Controller.d.ts +3 -1
  62. package/lib/Player/Controller.js +24 -5
  63. package/lib/Player/custom/SpatialScenePanel/index.js +3 -3
  64. package/lib/Player/index.d.ts +3 -2
  65. package/lib/Player/index.js +73 -42
  66. package/lib/Player/modules/Drawer/index.js +1 -1
  67. package/lib/Player/modules/PopUp/index.js +1 -1
  68. package/lib/Player/modules/VideoAgent/VideoAgentMesh.d.ts +17 -2
  69. package/lib/Player/modules/VideoAgent/VideoAgentMesh.js +113 -37
  70. package/lib/Player/modules/VideoAgent/VideoAgentScene.d.ts +5 -1
  71. package/lib/Player/modules/VideoAgent/VideoAgentScene.js +9 -4
  72. package/lib/Player/modules/VideoAgent/index.d.ts +2 -0
  73. package/lib/Player/modules/VideoAgent/index.js +2 -2
  74. package/lib/Player/modules/keyframes/BgMusic/index.js +1 -1
  75. package/lib/Player/modules/keyframes/CameraMovement/index.js +1 -1
  76. package/lib/Player/modules/keyframes/InfoPanel/index.js +2 -2
  77. package/lib/Player/modules/keyframes/ModelVideo/index.js +46 -9
  78. package/lib/Player/modules/keyframes/PanoEffect/index.js +1 -1
  79. package/lib/Player/modules/keyframes/PanoEffect/lineAnime.js +1 -1
  80. package/lib/Player/modules/keyframes/PanoTag/index.js +20 -7
  81. package/lib/Player/modules/keyframes/PanoTextLabel/index.d.ts +3 -0
  82. package/lib/Player/modules/keyframes/PanoTextLabel/index.js +97 -44
  83. package/lib/Player/modules/keyframes/Prompter/index.js +5 -19
  84. package/lib/Player/modules/keyframes/UpdateVRPanorama/index.js +10 -6
  85. package/lib/Player/modules/keyframes/VideoEffect/index.js +1 -1
  86. package/lib/Player/typings.d.ts +6 -0
  87. package/lib/fivePlugins/CSS3DRenderPlugin/centerPoint.d.ts +2 -0
  88. package/lib/fivePlugins/CSS3DRenderPlugin/centerPoint.js +12 -0
  89. package/lib/fivePlugins/CSS3DRenderPlugin/createResizeObserver.d.ts +7 -0
  90. package/lib/fivePlugins/CSS3DRenderPlugin/createResizeObserver.js +32 -0
  91. package/lib/fivePlugins/CSS3DRenderPlugin/evenNumber.d.ts +3 -0
  92. package/lib/fivePlugins/CSS3DRenderPlugin/evenNumber.js +11 -0
  93. package/lib/fivePlugins/CSS3DRenderPlugin/index.d.ts +56 -0
  94. package/lib/fivePlugins/CSS3DRenderPlugin/index.js +275 -0
  95. package/lib/fivePlugins/CSS3DRenderPlugin/transformPositionToVector3.d.ts +7 -0
  96. package/lib/fivePlugins/CSS3DRenderPlugin/transformPositionToVector3.js +18 -0
  97. package/lib/fivePlugins/CameraMovementPlugin/index.js +18 -14
  98. package/lib/fivePlugins/ModelTVVideoPlugin/index.js +1 -1
  99. package/lib/react/index.d.ts +7 -3
  100. package/lib/react/index.js +3 -3
  101. package/lib/shared-utils/AduioLike.d.ts +37 -0
  102. package/lib/shared-utils/AduioLike.js +165 -0
  103. package/lib/shared-utils/animationFrame/BetterTween.js +7 -3
  104. package/lib/shared-utils/animationFrame/tween.js +1 -1
  105. package/lib/shared-utils/createTransMatrix.d.ts +1 -0
  106. package/lib/shared-utils/createTransMatrix.js +43 -0
  107. package/lib/typings/VreoUnit.d.ts +33 -2
  108. package/lib/typings/VreoUnit.js +13 -1
  109. package/package.json +10 -10
  110. package/resources/PlayController/index.ts +0 -13
  111. package/resources/Player/App.tsx +7 -4
  112. package/resources/Player/Controller.ts +277 -146
  113. package/resources/Player/custom/SpatialScenePanel/index.tsx +5 -5
  114. package/resources/Player/hooks.ts +3 -5
  115. package/resources/Player/index.tsx +189 -141
  116. package/resources/Player/modules/ResizeObserver/index.tsx +23 -0
  117. package/resources/Player/modules/VideoAgent/VideoAgentMesh.ts +91 -34
  118. package/resources/Player/modules/VideoAgent/VideoAgentScene.ts +28 -25
  119. package/resources/Player/modules/VideoAgent/index.tsx +30 -13
  120. package/resources/Player/modules/Wave/index.tsx +18 -0
  121. package/resources/Player/modules/keyframes/InfoPanel/index.tsx +26 -11
  122. package/resources/Player/modules/keyframes/ModelVideo/index.tsx +36 -3
  123. package/resources/Player/modules/keyframes/PanoTag/index.tsx +53 -97
  124. package/resources/Player/modules/keyframes/PanoTextLabel/index.tsx +86 -34
  125. package/resources/Player/modules/keyframes/Prompter/index.tsx +6 -16
  126. package/resources/Player/modules/keyframes/UpdateVRPanorama/index.tsx +6 -2
  127. package/resources/Player/typings.ts +18 -1
  128. package/resources/fivePlugins/CSS3DRenderPlugin/centerPoint.ts +5 -0
  129. package/resources/fivePlugins/CSS3DRenderPlugin/createResizeObserver.ts +17 -0
  130. package/resources/fivePlugins/CSS3DRenderPlugin/evenNumber.ts +4 -0
  131. package/resources/fivePlugins/CSS3DRenderPlugin/index.ts +254 -0
  132. package/resources/fivePlugins/CSS3DRenderPlugin/transformPositionToVector3.ts +5 -0
  133. package/resources/fivePlugins/CameraMovementPlugin/index.ts +19 -12
  134. package/resources/react/index.tsx +7 -3
  135. package/resources/shared-utils/AduioLike.ts +109 -0
  136. package/resources/shared-utils/addResizeListener.ts +46 -0
  137. package/resources/shared-utils/createTransMatrix.ts +12 -0
  138. package/resources/shared-utils/getFileExpandedName.ts +6 -0
  139. package/resources/shared-utils/getMediaInfo.ts +52 -0
  140. package/resources/shared-utils/setElementDataset.ts +18 -0
  141. package/resources/typings/VreoUnit.ts +41 -3
  142. package/stylesheets/custom/SpatialScenePanel.css +2 -1
  143. package/stylesheets/default.css +348 -84
@@ -1,4 +1,3 @@
1
- import * as React from 'react'
2
1
  import * as ReactDOM from 'react-dom'
3
2
  import { Five, Subscribe } from '@realsee/five'
4
3
 
@@ -7,160 +6,209 @@ import { Controller, ControllerContext } from './Controller'
7
6
  import { CameraMovementData, VreoKeyframeEnum, VreoKeyframeEvent, VreoUnit } from '../typings/VreoUnit'
8
7
  import { reaction } from 'mobx'
9
8
  import { Drawer } from './modules/Drawer'
10
- import { PlayerConfigs } from './typings'
9
+ import { Appearance, PlayerConfigs, WaveAppearance } from './typings'
11
10
  import { PopUp } from './modules/PopUp'
12
11
 
13
12
  export class Player extends Subscribe<VreoKeyframeEvent> {
14
- $five: Five
15
- private controller: Controller
16
- configs: Readonly<PlayerConfigs>
17
-
18
- constructor(five: Five, configs: Partial<PlayerConfigs> = {}) {
19
- super()
20
- this.controller = new Controller()
21
- this.$five = this.controller.$five = five
22
-
23
- if (!configs.containter) {
24
- const containter = document.querySelector('#vreo-app') || document.createElement('div')
25
- ReactDOM.unmountComponentAtNode(containter)
26
- containter.setAttribute('id', 'vreo-app')
27
- configs.containter = containter
28
- document.body.append(containter)
29
- }
13
+ $five: Five
14
+ private controller: Controller
15
+ configs: Readonly<PlayerConfigs>
30
16
 
31
- this.configs = Object.freeze(
32
- Object.assign(
33
- {
34
- keyframeMap: {},
35
- },
36
- configs
37
- )
38
- )
39
-
40
- this.controller.configs = this.configs
41
-
42
- ReactDOM.render(
43
- <ControllerContext.Provider value={this.controller}>
44
- <App></App>
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
- ))}
59
- </ControllerContext.Provider>,
60
- configs.containter
61
- )
62
-
63
- // 监听播放情况:抛出触发时机
64
- reaction(
65
- () => this.controller.playing,
66
- (playing) => {
67
- this.emit(playing ? 'playing' : 'paused')
68
- }
69
- )
70
- }
71
-
72
- async load(vreoUnit: VreoUnit, currentTime = 0, preload = false) {
73
- if (!this.controller.visible) {
74
- this.controller.setVisible(true)
75
- // 延迟 500ms 规避跟 DOM 动画冲突
76
- await new Promise((resolve) => {
77
- setTimeout(() => resolve(true), 500)
78
- })
79
- }
17
+ constructor(five: Five, configs: Partial<PlayerConfigs> = {}) {
18
+ super()
19
+ this.$five = five
80
20
 
81
- if (this.controller.stopInterval) {
82
- this.controller.stopInterval()
83
- this.controller.stopInterval = undefined
84
- }
85
21
 
86
- this.controller.vreoUnit = vreoUnit
87
- this.controller.videoInstance?.pause()
22
+ if (!configs.container) {
23
+ configs.container = configs.containter
24
+ }
25
+ if (!configs.container) {
26
+ const container = document.getElementById('vreo-app') || document.createElement('div')
27
+ ReactDOM.unmountComponentAtNode(container)
28
+ container.setAttribute('id', 'vreo-app')
29
+ configs.container = container
30
+ const fiveCanvasDomParent = five.getElement()?.parentNode
31
+ if (fiveCanvasDomParent) {
32
+ fiveCanvasDomParent.append(container)
33
+ } else {
34
+ document.body.append(container)
35
+ }
36
+ }
88
37
 
89
- // 预载逻辑
90
- // 是否降低图片分辨率
91
- if (this.$five.imageOptions.size && this.configs.imageOptions?.size && this.$five.imageOptions.size > this.configs.imageOptions.size) {
92
- this.$five.imageOptions.size = this.configs.imageOptions.size
38
+ if (!configs.container.classList.contains('vreo-app')) configs.container.classList.add('vreo-app')
39
+
40
+ this.configs = Object.freeze( Object.assign({ keyframeMap: {} }, configs) )
41
+
42
+ this.controller = new Controller({five, container:configs.container, configs: this.configs})
43
+
44
+
45
+ ReactDOM.render(
46
+ <ControllerContext.Provider value={this.controller}>
47
+ <App />
48
+ <Drawer />
49
+ <PopUp />
50
+ {this.configs.customKeyframes &&
51
+ this.configs.customKeyframes.map((CustomCmpt, key) => (
52
+ <CustomCmpt
53
+ key={key}
54
+ subscribe={{
55
+ on: (name, callback) => this.on(name, callback),
56
+ once: (name, callback) => this.once(name, callback),
57
+ off: (name, callback) => this.off(name, callback),
58
+ }}
59
+ five={five}
60
+ />
61
+ ))}
62
+ </ControllerContext.Provider>,
63
+ configs.container
64
+ )
65
+
66
+ // 监听播放情况:抛出触发时机
67
+ reaction(
68
+ () => this.controller.ended,
69
+ (ended) => {
70
+ if (ended) {
71
+ this.emit('paused', true)
72
+ }
73
+ }
74
+ )
75
+
76
+ reaction(
77
+ () => this.controller.playing,
78
+ (playing) => {
79
+ if (!this.controller.ended) {
80
+ this.emit(playing ? 'playing' : 'paused')
81
+ }
82
+ }
83
+ )
93
84
  }
94
-
95
- // 预载数据中的点位
96
- if (preload || (preload === undefined && this.configs.autoPreload)) {
97
- const panoIndexMap = vreoUnit.keyframes.filter((vreoKeyframe) => {
98
- if (vreoKeyframe.type !== VreoKeyframeEnum.CameraMovement) {
99
- return false
85
+
86
+ async load(vreoUnit: VreoUnit, currentTime = 0, preload = false, force = false) {
87
+ this.controller.setLoading(true)
88
+ if (force) {
89
+ vreoUnit = JSON.parse(JSON.stringify(vreoUnit))
90
+ }
91
+ if (!this.controller.visible) {
92
+ this.controller.setVisible(true)
93
+ // 延迟 500ms 规避跟 DOM 动画冲突
94
+ await new Promise((resolve) => {
95
+ setTimeout(() => resolve(true), 500)
96
+ })
100
97
  }
101
-
102
- const data = vreoKeyframe.data as CameraMovementData
103
- if (data.panoIndex === undefined) {
104
- return false
98
+
99
+ if (this.controller.stopInterval) {
100
+ this.controller.stopInterval()
101
+ this.controller.stopInterval = undefined
102
+ }
103
+
104
+ this.controller.vreoUnit = vreoUnit
105
+ this.controller.mediaInstance?.pause()
106
+
107
+ // 预载逻辑
108
+ // 是否降低图片分辨率
109
+ if (
110
+ this.$five.imageOptions.size &&
111
+ this.configs.imageOptions?.size &&
112
+ this.$five.imageOptions.size > this.configs.imageOptions.size
113
+ ) {
114
+ this.$five.imageOptions.size = this.configs.imageOptions.size
105
115
  }
116
+
117
+ // 预载数据中的点位
118
+ if (preload || (preload === undefined && this.configs.autoPreload)) {
119
+ const panoIndexMap = vreoUnit.keyframes
120
+ .filter((vreoKeyframe) => {
121
+ if (vreoKeyframe.type !== VreoKeyframeEnum.CameraMovement) {
122
+ return false
123
+ }
124
+
125
+ const data = vreoKeyframe.data as CameraMovementData
126
+ if (data.panoIndex === undefined) {
127
+ return false
128
+ }
129
+ return true
130
+ })
131
+ .reduce((accu: Record<number, boolean>, curr) => {
132
+ const panoIndex = curr.data.panoIndex as number
133
+ if (!accu[panoIndex]) {
134
+ accu[panoIndex] = true
135
+ }
136
+ return accu
137
+ }, {})
138
+
139
+ const panoIndexes = Object.keys(panoIndexMap)
140
+ for (let i = 0; i < panoIndexes.length; i++) {
141
+ await this.$five.preloadPano(Number(panoIndexes[i]))
142
+ }
143
+ }
144
+
145
+ // 新数据载入就绪
146
+ this.emit('loaded', vreoUnit)
147
+ this.controller.emit('loaded', vreoUnit)
148
+
149
+ if (this.controller.videoAgentScene?.videoAgentMesh.mediaInstance) {
150
+ this.controller.videoAgentScene.videoAgentMesh.mediaInstance.currentTime = currentTime / 1000
151
+ }
152
+
153
+ this.controller.setAvatar(vreoUnit.video.avatar)
154
+
155
+ await this.controller.videoAgentScene?.videoAgentMesh.play(
156
+ vreoUnit.video.url,
157
+ currentTime / 1000,
158
+ vreoUnit.video.duration
159
+ )
160
+
161
+ this.controller.setEnded(false)
162
+ this.play()
163
+
164
+ this.controller.run((type, keyframe) => this.emit(type, keyframe))
165
+ this.controller.setLoading(false)
106
166
  return true
107
- }).reduce((accu: Record<number, boolean>, curr) => {
108
- const panoIndex = curr.data.panoIndex as number
109
- if (!accu[panoIndex]) {
110
- accu[panoIndex] = true
167
+ }
168
+
169
+ get paused() {
170
+ return !this.controller.playing
171
+ }
172
+
173
+ play(currentTime?: number) {
174
+ if (this.controller.playing) return true
175
+ if (currentTime && this.controller.mediaInstance) {
176
+ this.controller.mediaInstance.currentTime = currentTime / 1000
111
177
  }
112
- return accu
113
- }, {})
114
-
115
- const panoIndexs = Object.keys(panoIndexMap)
116
- for (let i = 0; i < panoIndexs.length; i++) {
117
- await this.$five.preloadPano(Number(panoIndexs[i]))
118
- }
178
+ Object.assign(window, { $vreoController: this.controller })
179
+
180
+ this.controller.setPlaying(true)
181
+ return true
119
182
  }
120
-
121
- // 新数据载入就绪
122
- this.emit('loaded', vreoUnit)
123
- if (vreoUnit.video.url) {
124
- if (this.controller.videoAgentScene?.videoAgentMesh.videoInstance) {
125
- this.controller.videoAgentScene.videoAgentMesh.videoInstance.currentTime = currentTime / 1000
126
- }
127
- await this.controller.videoAgentScene?.videoAgentMesh.play(vreoUnit.video.url)
128
- this.play()
183
+
184
+ setAppearance(appearance: Appearance) {
185
+ this.controller.setAppearance(appearance)
129
186
  }
130
- this.controller.run((type, keyframe) => this.emit(type, keyframe))
131
- return true
132
- }
133
-
134
- get paused() {
135
- return !this.controller.playing
136
- }
137
-
138
- play() {
139
- if (this.controller.playing) return true
140
- this.controller.setPlaying(true)
141
- return true
142
- }
143
-
144
- pause() {
145
- this.controller.setPlaying(false)
146
- }
147
-
148
- show() {
149
- this.controller.setVisible(true)
150
- }
151
-
152
- hide() {
153
- this.controller.setVisible(false)
154
- }
155
-
156
- dispose() {
157
- this.pause()
158
- this.controller.dispose()
159
-
160
- if (this.configs.containter) {
161
- ReactDOM.unmountComponentAtNode(this.configs.containter as Element)
187
+
188
+ pause() {
189
+ this.controller.setPlaying(false)
190
+ }
191
+
192
+ show() {
193
+ this.controller.setVisible(true)
194
+ }
195
+
196
+ hide() {
197
+ this.controller.setVisible(false)
198
+ }
199
+
200
+ getCurrentTime() {
201
+ return this.controller.currentTime
202
+ }
203
+
204
+ dispose() {
205
+ this.pause()
206
+ this.controller.dispose()
207
+
208
+ if (this.configs.container) {
209
+ ReactDOM.unmountComponentAtNode(this.configs.container as Element)
210
+ }
162
211
  }
163
- }
164
212
  }
165
213
 
166
214
  console.log(`
@@ -172,6 +220,6 @@ console.log(`
172
220
  ┃┃┃┗┓┃┃━┫┃┗┛┗┓┃┗┓┣━━┃┃┃━┫┃┃━┫
173
221
  ┗┛┗━┛┗━━┛┗━━━┛┗━┛┗━━┛┗━━┛┗━━┛
174
222
  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
175
- ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
223
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
176
224
 
177
225
  `)
@@ -0,0 +1,23 @@
1
+ import React from 'react'
2
+ import { addResizeListener, hasResizeListener } from '../../../shared-utils/addResizeListener'
3
+ import { useController } from '../../hooks'
4
+
5
+ export function ResizeObserver() {
6
+ const ref = React.useRef<HTMLDivElement>(null)
7
+ const controller = useController()
8
+
9
+ React.useEffect(() => {
10
+ if (!ref.current) return
11
+ const resizeElement = controller.container.parentElement
12
+ if (hasResizeListener(resizeElement)) return
13
+ const dispose = addResizeListener(resizeElement, (width, height) => {
14
+ controller.setContainerSize(width, height)
15
+ })
16
+
17
+ return () => {
18
+ dispose()
19
+ }
20
+ }, [])
21
+
22
+ return <div className="resizeObserver-element" ref={ref}></div>
23
+ }
@@ -1,6 +1,8 @@
1
1
  import * as THREE from 'three'
2
2
  import { Preloader } from '../../../shared-utils/Preloader'
3
3
  import { makeObservable, observable, runInAction } from 'mobx'
4
+ import AudioLike from '../../../shared-utils/AduioLike'
5
+ import { getMediaType } from '../../../shared-utils/getMediaInfo'
4
6
 
5
7
  const vertexShader = `
6
8
  varying vec2 vUv;
@@ -63,7 +65,20 @@ const cacheInstance: {
63
65
  } = {}
64
66
 
65
67
  export interface VideoAgentMeshOptions {
68
+ /**
69
+ * 自定义视频实例。
70
+ */
66
71
  videoInstance?: HTMLVideoElement
72
+ /**
73
+ * 自定义音频实例。
74
+ */
75
+ audioInstance?: HTMLAudioElement
76
+ /**
77
+ * 是否开启音频预载能力。**仅对`.mp3`有效**。
78
+ *
79
+ * @description 开启预载能力后会通过 `Fetch/XHR` 方式将音频文件下载转成二进制 [`Blob`](https://developer.mozilla.org/zh-CN/docs/Web/API/Blob) 提供给多媒体实例。这样的好处是 **能够保障音视频播放过程中不卡顿**。 但在部分 iOS 系统中会存在兼容问题——播 25s 之后会静音(`muted` 为 `false`,但是没有声音)。
80
+ */
81
+ preload?: boolean
67
82
  }
68
83
 
69
84
  /**
@@ -72,17 +87,24 @@ export interface VideoAgentMeshOptions {
72
87
  export class VideoAgentMesh extends THREE.Mesh {
73
88
  options: VideoAgentMeshOptions
74
89
  videoUrl?: string
75
- freeze: boolean
90
+ freeze: boolean
76
91
  paused: boolean
77
92
  audioInstance: HTMLAudioElement
93
+ audioLikeInstance: AudioLike
78
94
  $removeEventListener: () => void
79
95
 
80
- get videoInstance(): HTMLAudioElement {
81
- if (this.videoUrl?.endsWith('mp3')) {
96
+ get mediaInstance(): HTMLAudioElement | HTMLVideoElement | AudioLike {
97
+ if (!this.videoUrl) {
98
+ return this.audioLikeInstance
99
+ }
100
+
101
+ if (getMediaType(this.videoUrl) === 'audio') {
82
102
  return this.audioInstance
83
103
  }
104
+
84
105
  const uniforms = (this.material as THREE.ShaderMaterial).uniforms
85
106
  const videoInstance = uniforms.map.value.image as HTMLVideoElement
107
+
86
108
  return videoInstance
87
109
  }
88
110
 
@@ -99,7 +121,9 @@ export class VideoAgentMesh extends THREE.Mesh {
99
121
  height: number,
100
122
  widthSegments: number,
101
123
  heightSegments: number,
102
- options: VideoAgentMeshOptions = {},
124
+ options: VideoAgentMeshOptions = {
125
+ preload: true
126
+ },
103
127
  ) {
104
128
  if (!options.videoInstance) {
105
129
  if (cacheInstance.videoInstance) {
@@ -107,6 +131,7 @@ export class VideoAgentMesh extends THREE.Mesh {
107
131
  } else {
108
132
  const videoInstance = document.createElement('video')
109
133
  videoInstance.style.opacity = '0'
134
+ videoInstance.style.pointerEvents = 'none'
110
135
  videoInstance.style.display = 'none'
111
136
  document.body.append(videoInstance)
112
137
  options.videoInstance = videoInstance
@@ -114,7 +139,6 @@ export class VideoAgentMesh extends THREE.Mesh {
114
139
  videoInstance.playsInline = true
115
140
  videoInstance.controls = false
116
141
  }
117
-
118
142
  }
119
143
 
120
144
  const geometry = new THREE.PlaneGeometry(width, height, widthSegments, heightSegments)
@@ -134,89 +158,122 @@ export class VideoAgentMesh extends THREE.Mesh {
134
158
  this.freeze = false
135
159
  this.paused = true
136
160
 
137
- {
161
+ if (!options.audioInstance) {
138
162
  if (cacheInstance.audioInstance) {
139
163
  this.audioInstance = cacheInstance.audioInstance
140
-
141
164
  } else {
142
165
  const audioInstance = document.createElement('audio')
143
166
  audioInstance.crossOrigin = ''
144
167
  // videoInstance.muted = true
145
168
  audioInstance.muted = false
146
- audioInstance.setAttribute('playsinline', 'true')
147
- audioInstance.setAttribute('webkit-playsinline', 'true')
148
- audioInstance.setAttribute('autoplay', 'false')
149
- audioInstance.setAttribute('style', 'display: none;')
169
+ audioInstance.autoplay = false
170
+ audioInstance.style.display = 'none'
150
171
  document.body.appendChild(audioInstance)
151
172
  this.audioInstance = audioInstance
152
173
  cacheInstance.audioInstance = audioInstance
153
174
  }
154
-
175
+ } else {
176
+ this.audioInstance = options.audioInstance
155
177
  }
156
178
 
179
+ this.audioLikeInstance = new AudioLike()
180
+
157
181
  makeObservable(this, { paused: observable })
158
182
 
159
183
  const updatePaused = (paused: boolean) => runInAction(() => (this.paused = paused))
160
184
  const onPause = () => updatePaused(true)
161
185
  const onPlay = () => updatePaused(false)
186
+ const onEnded = () => console.log('vreo: video 播放结束')
162
187
 
163
- this.videoInstance.addEventListener('pause', onPause)
164
- this.videoInstance.addEventListener('play', onPlay)
188
+ this.audioInstance.addEventListener('pause', onPause)
189
+ this.audioInstance.addEventListener('play', onPlay)
190
+ this.options.videoInstance?.addEventListener('pause', onPause)
191
+ this.options.videoInstance?.addEventListener('play', onPlay)
192
+ this.audioLikeInstance.addEventListener('pause', onPause)
193
+ this.audioLikeInstance.addEventListener('play', onPlay)
194
+ this.options.videoInstance?.addEventListener('ended', onEnded)
165
195
 
166
196
  this.$removeEventListener = () => {
167
- this.videoInstance.removeEventListener('pause', onPause)
168
- this.videoInstance.removeEventListener('play', onPlay)
197
+ this.audioInstance.removeEventListener('pause', onPause)
198
+ this.audioInstance.removeEventListener('play', onPlay)
199
+ this.options.videoInstance?.removeEventListener('pause', onPause)
200
+ this.options.videoInstance?.removeEventListener('play', onPlay)
201
+ this.audioLikeInstance.removeEventListener('pause', onPause)
202
+ this.audioLikeInstance.removeEventListener('play', onPlay)
203
+ this.options.videoInstance?.removeEventListener('ended', onEnded)
169
204
  }
170
205
  }
171
206
 
172
-
173
207
  private async update(videoUrl: string) {
208
+
174
209
  if (this.videoUrl === videoUrl) {
175
210
  return
176
211
  }
177
212
 
178
213
  this.videoUrl = videoUrl
214
+
215
+ // // 兼容非视频场景
216
+ // if (this.mediaInstance instanceof HTMLAudioElement) {
217
+ // this.mediaInstance.style.display = 'none'
218
+ // } else if (this.mediaInstance instanceof HTMLVideoElement) {
219
+ // this.mediaInstance.style.display = 'block'
220
+ // }
221
+
179
222
  this.freeze = true
180
- await this.videoInstance.pause()
223
+ await this.mediaInstance.pause()
181
224
 
182
225
  const uniforms = (this.material as THREE.ShaderMaterial).uniforms
183
- this.videoInstance.muted = true
184
- this.videoInstance.src = await URL.createObjectURL((await Preloader.blob(this.videoUrl)) as unknown as Blob)
185
- this.videoInstance.setAttribute('data-src', this.videoUrl)
226
+ this.mediaInstance.muted = true
227
+
228
+ this.mediaInstance.src = (this.options.preload || this.options.preload === undefined || getMediaType(this.videoUrl) === 'video') ?
229
+ await URL.createObjectURL((await Preloader.blob(this.videoUrl)) as unknown as Blob) : this.videoUrl
230
+ this.mediaInstance.setAttribute('data-src', this.videoUrl)
186
231
 
187
232
  const onStart = () => {
188
- if (this.videoInstance.currentTime === 0) return
233
+ if (this.mediaInstance.currentTime === 0) return
189
234
  this.freeze = false
190
- this.videoInstance.muted = false
191
- uniforms.enable.value = this.videoUrl?.endsWith('.mp4') ? 1 : 0
192
- this.videoInstance.removeEventListener('timeupdate', onStart, false)
235
+ this.mediaInstance.muted = false
236
+ uniforms.enable.value = getMediaType(this.videoUrl) ? 1 : 0
237
+ this.mediaInstance.removeEventListener('timeupdate', onStart, false)
238
+
193
239
  }
194
240
 
195
- this.videoInstance.addEventListener('timeupdate', onStart, false)
241
+ this.mediaInstance.addEventListener('timeupdate', onStart, false)
196
242
  }
197
243
 
198
- async play(videoUrl = '') {
244
+ async play(videoUrl = '', currentTime = 0, duration?: number) {
199
245
  videoUrl = videoUrl || ''
246
+
247
+ if (duration && !videoUrl) {
248
+ if (this.currentTime) {
249
+ this.mediaInstance.currentTime = currentTime
250
+ }
251
+ (this.mediaInstance as AudioLike).duration = duration
252
+ this.videoUrl = ''
253
+ this.mediaInstance.play()
254
+ return true
255
+ }
256
+
200
257
  if (!videoUrl) {
201
- if (this.videoUrl) await this.videoInstance.play()
258
+ if (this.videoUrl) await this.mediaInstance.play()
202
259
  else console.warn('警告:视频资源未初始化。')
203
260
  return true
204
261
  }
205
262
 
206
263
  if (videoUrl === this.videoUrl) {
207
- this.videoInstance.currentTime = 0
208
- await this.videoInstance.play()
264
+ this.mediaInstance.currentTime = currentTime
265
+ await this.mediaInstance.play()
209
266
  return true
210
267
  }
211
268
 
212
269
  await this.update(videoUrl)
213
270
 
214
- this.videoInstance.pause()
271
+ this.mediaInstance.pause()
215
272
 
216
273
  return await new Promise((resolve) =>
217
274
  setTimeout(async () => {
218
- this.videoInstance.currentTime = 0
219
- await this.videoInstance.play()
275
+ this.mediaInstance.currentTime = currentTime
276
+ await this.mediaInstance.play()
220
277
  resolve(true)
221
278
  }, 20),
222
279
  )
@@ -226,7 +283,7 @@ export class VideoAgentMesh extends THREE.Mesh {
226
283
  * 转成毫秒,保障精准度
227
284
  */
228
285
  get currentTime() {
229
- return this.videoInstance.currentTime * 1000
286
+ return this.mediaInstance.currentTime * 1000
230
287
  }
231
288
 
232
289
  dispose() {