@realsee/vreo 0.1.2 → 0.1.6

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 (51) hide show
  1. package/docs/assets/search.js +1 -1
  2. package/docs/classes/Player.Player-1.html +6 -6
  3. package/docs/enums/Player.InfoPanelTypeEnum.html +1 -1
  4. package/docs/enums/Player.PanoEffectEnum.html +1 -1
  5. package/docs/enums/Player.PanoTagEnum.html +1 -1
  6. package/docs/enums/Player.VreoKeyframeEnum.html +13 -11
  7. package/docs/enums/fivePlugins.CameraMovementEffect.html +1 -1
  8. package/docs/enums/fivePlugins.Rotation.html +1 -1
  9. package/docs/interfaces/Player.PlayerConfigs.html +1 -1
  10. package/docs/interfaces/Player.Vertex.html +1 -1
  11. package/docs/interfaces/Player.VreoKeyframe.html +1 -1
  12. package/docs/interfaces/Player.VreoUnit.html +1 -1
  13. package/docs/interfaces/Player.VreoVideo.html +1 -1
  14. package/docs/interfaces/fivePlugins.CameraMovementOptsCallback.html +1 -1
  15. package/docs/interfaces/fivePlugins.CameraMovementPluginExportType.html +2 -2
  16. package/docs/interfaces/fivePlugins.ModelTVVideoPluginData.html +1 -1
  17. package/docs/interfaces/fivePlugins.ModelTVVideoPluginExportType.html +1 -1
  18. package/docs/interfaces/fivePlugins.ModelTVVideoPluginParameterType.html +1 -1
  19. package/docs/interfaces/react.VreoActionCallbacks.html +6 -6
  20. package/docs/modules/Player.html +14 -12
  21. package/docs/modules/fivePlugins.html +2 -2
  22. package/docs/modules/react.html +1 -1
  23. package/lib/Player/App.js +4 -0
  24. package/lib/Player/Controller.d.ts +1 -0
  25. package/lib/Player/Controller.js +11 -4
  26. package/lib/Player/index.d.ts +1 -0
  27. package/lib/Player/index.js +50 -31
  28. package/lib/Player/modules/VideoAgent/VideoAgentMesh.d.ts +2 -0
  29. package/lib/Player/modules/VideoAgent/VideoAgentMesh.js +62 -22
  30. package/lib/Player/modules/VideoAgent/VideoAgentScene.d.ts +2 -0
  31. package/lib/Player/modules/VideoAgent/VideoAgentScene.js +7 -0
  32. package/lib/Player/modules/VideoAgent/index.js +6 -0
  33. package/lib/Player/modules/keyframes/Prompter/index.js +2 -1
  34. package/lib/fivePlugins/CameraMovementPlugin/index.js +5 -4
  35. package/lib/fivePlugins/CameraMovementPlugin/typings.d.ts +1 -0
  36. package/lib/react/index.js +12 -4
  37. package/lib/typings/VreoUnit.d.ts +7 -1
  38. package/lib/typings/VreoUnit.js +1 -0
  39. package/package.json +6 -2
  40. package/resources/Player/App.tsx +3 -0
  41. package/resources/Player/Controller.ts +5 -5
  42. package/resources/Player/index.tsx +36 -23
  43. package/resources/Player/modules/VideoAgent/VideoAgentMesh.ts +63 -20
  44. package/resources/Player/modules/VideoAgent/VideoAgentScene.ts +6 -0
  45. package/resources/Player/modules/VideoAgent/index.tsx +5 -0
  46. package/resources/Player/modules/keyframes/Prompter/index.tsx +1 -0
  47. package/resources/fivePlugins/CameraMovementPlugin/index.ts +8 -3
  48. package/resources/fivePlugins/CameraMovementPlugin/typings.ts +1 -0
  49. package/resources/react/index.tsx +11 -4
  50. package/resources/typings/VreoUnit.ts +7 -0
  51. package/stylesheets/default.css +9 -1
@@ -47,6 +47,8 @@ var fragmentShaderTpl = function fragmentShaderTpl() {
47
47
  return "\nuniform int enable;\nuniform sampler2D map;\nvarying vec2 vUv;\nvarying vec2 vColorUv;\nvarying vec2 vMaskUv;\n\nvec3 hsv2rgb(vec3 c) {\n const vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0);\n vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www);\n return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y);\n}\n\nvec3 rgb2hsv(vec3 c) {\n const vec4 K = vec4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0);\n vec4 p = mix(vec4(c.bg, K.wz), vec4(c.gb, K.xy), step(c.b, c.g));\n vec4 q = mix(vec4(p.xyw, c.r), vec4(c.r, p.yzx), step(p.x, c.r));\n\n float d = q.x - min(q.w, q.y);\n return vec3(abs(q.z + (q.w - q.y) / (6.0 * d + 0.001)), d / (q.x + 0.001), q.x);\n}\n\nvoid main(void) {\n vec3 color = texture2D(map, vUv).xyz;\n float amask = 1.0;\n\n float ref = color.r;\n if (ref < color.b) ref = color.b;\n amask = color.g - ref + 0.1;\n amask = smoothstep(0.1, 0.3, amask);\n amask = 1.0 - amask;\n\n vec3 target = rgb2hsv(vec3(0.0 / 255.0, 255.0 / 255.0, 0.0 / 255.0));\n vec3 hsv = rgb2hsv(color);\n float distance = abs(hsv.x - target.x);\n if (distance < 0.15) {\n hsv.y = 0.0;\n }\n color = hsv2rgb(hsv);\n\n gl_FragColor = vec4(color.rgb, amask * float(enable));\n}\n";
48
48
  };
49
49
 
50
+ var cacheInstance = {};
51
+
50
52
  /**
51
53
  * 视频经纪人MESH
52
54
  */
@@ -71,13 +73,18 @@ var VideoAgentMesh = /*#__PURE__*/function (_THREE$Mesh) {
71
73
  _classCallCheck(this, VideoAgentMesh);
72
74
 
73
75
  if (!options.videoInstance) {
74
- var videoInstance = document.createElement('video');
75
- videoInstance.style.opacity = '0';
76
- videoInstance.style.display = 'none';
77
- document.body.append(videoInstance);
78
- options.videoInstance = videoInstance;
79
- videoInstance.playsInline = true;
80
- videoInstance.controls = false;
76
+ if (cacheInstance.videoInstance) {
77
+ options.videoInstance = cacheInstance.videoInstance;
78
+ } else {
79
+ var videoInstance = document.createElement('video');
80
+ videoInstance.style.opacity = '0';
81
+ videoInstance.style.display = 'none';
82
+ document.body.append(videoInstance);
83
+ options.videoInstance = videoInstance;
84
+ cacheInstance.videoInstance = videoInstance;
85
+ videoInstance.playsInline = true;
86
+ videoInstance.controls = false;
87
+ }
81
88
  }
82
89
 
83
90
  var geometry = new THREE.PlaneGeometry(width, height, widthSegments, heightSegments);
@@ -100,16 +107,21 @@ var VideoAgentMesh = /*#__PURE__*/function (_THREE$Mesh) {
100
107
  _this.freeze = false;
101
108
  _this.paused = true;
102
109
  {
103
- var audioInstance = document.createElement('audio');
104
- audioInstance.crossOrigin = ''; // videoInstance.muted = true
105
-
106
- audioInstance.muted = false;
107
- audioInstance.setAttribute('playsinline', 'true');
108
- audioInstance.setAttribute('webkit-playsinline', 'true');
109
- audioInstance.setAttribute('autoplay', 'false');
110
- audioInstance.setAttribute('style', 'display: none;');
111
- document.body.appendChild(audioInstance);
112
- _this.audioInstance = audioInstance;
110
+ if (cacheInstance.audioInstance) {
111
+ _this.audioInstance = cacheInstance.audioInstance;
112
+ } else {
113
+ var audioInstance = document.createElement('audio');
114
+ audioInstance.crossOrigin = ''; // videoInstance.muted = true
115
+
116
+ audioInstance.muted = false;
117
+ audioInstance.setAttribute('playsinline', 'true');
118
+ audioInstance.setAttribute('webkit-playsinline', 'true');
119
+ audioInstance.setAttribute('autoplay', 'false');
120
+ audioInstance.setAttribute('style', 'display: none;');
121
+ document.body.appendChild(audioInstance);
122
+ _this.audioInstance = audioInstance;
123
+ cacheInstance.audioInstance = audioInstance;
124
+ }
113
125
  }
114
126
  (0, _mobx.makeObservable)(_assertThisInitialized(_this), {
115
127
  paused: _mobx.observable
@@ -121,13 +133,23 @@ var VideoAgentMesh = /*#__PURE__*/function (_THREE$Mesh) {
121
133
  });
122
134
  };
123
135
 
124
- _this.videoInstance.addEventListener('pause', function () {
136
+ var onPause = function onPause() {
125
137
  return updatePaused(true);
126
- });
138
+ };
127
139
 
128
- _this.videoInstance.addEventListener('play', function () {
140
+ var onPlay = function onPlay() {
129
141
  return updatePaused(false);
130
- });
142
+ };
143
+
144
+ _this.videoInstance.addEventListener('pause', onPause);
145
+
146
+ _this.videoInstance.addEventListener('play', onPlay);
147
+
148
+ _this.$removeEventListener = function () {
149
+ _this.videoInstance.removeEventListener('pause', onPause);
150
+
151
+ _this.videoInstance.removeEventListener('play', onPlay);
152
+ };
131
153
 
132
154
  return _this;
133
155
  }
@@ -186,10 +208,12 @@ var VideoAgentMesh = /*#__PURE__*/function (_THREE$Mesh) {
186
208
  this.videoInstance.setAttribute('data-src', this.videoUrl);
187
209
 
188
210
  onStart = function onStart() {
211
+ var _this2$videoUrl;
212
+
189
213
  if (_this2.videoInstance.currentTime === 0) return;
190
214
  _this2.freeze = false;
191
215
  _this2.videoInstance.muted = false;
192
- uniforms.enable.value = 1;
216
+ uniforms.enable.value = (_this2$videoUrl = _this2.videoUrl) !== null && _this2$videoUrl !== void 0 && _this2$videoUrl.endsWith('.mp4') ? 1 : 0;
193
217
 
194
218
  _this2.videoInstance.removeEventListener('timeupdate', onStart, false);
195
219
  };
@@ -316,6 +340,22 @@ var VideoAgentMesh = /*#__PURE__*/function (_THREE$Mesh) {
316
340
  get: function get() {
317
341
  return this.videoInstance.currentTime * 1000;
318
342
  }
343
+ }, {
344
+ key: "dispose",
345
+ value: function dispose() {
346
+ // 销毁事件监听
347
+ this.$removeEventListener();
348
+
349
+ if (cacheInstance.audioInstance) {
350
+ document.body.removeChild(cacheInstance.audioInstance);
351
+ cacheInstance.audioInstance = undefined;
352
+ }
353
+
354
+ if (cacheInstance.videoInstance) {
355
+ document.body.removeChild(cacheInstance.videoInstance);
356
+ cacheInstance.videoInstance = undefined;
357
+ }
358
+ }
319
359
  }]);
320
360
 
321
361
  return VideoAgentMesh;
@@ -5,6 +5,8 @@ export declare class VideoAgentScene {
5
5
  scene: THREE.Scene;
6
6
  camera: THREE.OrthographicCamera;
7
7
  renderer: THREE.WebGLRenderer;
8
+ container: HTMLElement;
8
9
  constructor(container: HTMLElement, needRender?: boolean);
9
10
  run: () => void;
11
+ dispose: () => void;
10
12
  }
@@ -43,6 +43,13 @@ var VideoAgentScene = function VideoAgentScene(container) {
43
43
  requestAnimationFrame(_this.run);
44
44
  });
45
45
 
46
+ _defineProperty(this, "dispose", function () {
47
+ var _this$videoAgentMesh;
48
+
49
+ (_this$videoAgentMesh = _this.videoAgentMesh) === null || _this$videoAgentMesh === void 0 ? void 0 : _this$videoAgentMesh.dispose();
50
+ });
51
+
52
+ this.container = container;
46
53
  this.camera.position.set(0, 0, 10);
47
54
  this.camera.lookAt(0, 0, 0);
48
55
  var state = {
@@ -30,6 +30,12 @@ function VideoAgent(props) {
30
30
 
31
31
  var videoAgentScene = new _VideoAgentScene.VideoAgentScene(ref.current);
32
32
  controller.videoAgentScene = videoAgentScene;
33
+ return function () {
34
+ var _controller$videoAgen;
35
+
36
+ controller.dispose();
37
+ (_controller$videoAgen = controller.videoAgentScene) === null || _controller$videoAgen === void 0 ? void 0 : _controller$videoAgen.dispose();
38
+ };
33
39
  }, []);
34
40
  return /*#__PURE__*/React.createElement("div", {
35
41
  className: "VideoAgent",
@@ -84,7 +84,8 @@ function Prompter() {
84
84
  }, [controller]);
85
85
  return /*#__PURE__*/React.createElement("div", {
86
86
  className: (0, _classnames["default"])('vreo-prompter', {
87
- 'vreo-prompter--hidden': hidden
87
+ 'vreo-prompter--hidden': hidden,
88
+ 'vreo-prompter--audio': controller.isAudio
88
89
  })
89
90
  }, text ?
90
91
  /*#__PURE__*/
@@ -204,18 +204,19 @@ var CameraMovementPlugin = function CameraMovementPlugin(five) {
204
204
 
205
205
  if (opts.asyncStartCallback) {
206
206
  opts.asyncStartCallback();
207
- }
207
+ } // 先切换模型再出旋转效果
208
+
208
209
 
209
- if (!(five.currentMode !== _five.Five.Mode.Panorama)) {
210
+ if (!(args.mode && five.currentMode !== args.mode)) {
210
211
  _context2.next = 5;
211
212
  break;
212
213
  }
213
214
 
214
215
  _context2.next = 5;
215
- return five.changeMode(_five.Five.Mode.Panorama);
216
+ return five.changeMode(args.mode);
216
217
 
217
218
  case 5:
218
- if (!(args.panoIndex !== undefined && args.panoIndex !== five.panoIndex)) {
219
+ if (!(args.mode === _five.Five.Mode.Panorama && five.currentMode === _five.Five.Mode.Panorama && args.panoIndex !== undefined && args.panoIndex !== five.panoIndex)) {
219
220
  _context2.next = 11;
220
221
  break;
221
222
  }
@@ -38,6 +38,7 @@ export declare type MoveOpts = {
38
38
  preload?: boolean;
39
39
  } & CameraMovementOptsCallback;
40
40
  export declare type RotateArgs = {
41
+ mode?: Mode;
41
42
  panoIndex: number;
42
43
  loop: boolean;
43
44
  rotateSpeed: number;
@@ -42,10 +42,14 @@ var VreoProvider = function VreoProvider(props) {
42
42
  player = _React$useState2[0],
43
43
  setPlayer = _React$useState2[1];
44
44
 
45
+ var playerRef = React.useRef(null);
45
46
  React.useEffect(function () {
46
- setPlayer(new _Player.Player(five));
47
+ playerRef.current = new _Player.Player(five);
48
+ setPlayer(playerRef.current);
47
49
  return function () {
48
- player === null || player === void 0 ? void 0 : player.dispose();
50
+ var _playerRef$current;
51
+
52
+ (_playerRef$current = playerRef.current) === null || _playerRef$current === void 0 ? void 0 : _playerRef$current.dispose();
49
53
  };
50
54
  }, []);
51
55
  if (!player) return /*#__PURE__*/React.createElement(React.Fragment, null);
@@ -108,9 +112,13 @@ function useVreoEventCallback(name, callback, deps) {
108
112
  dependencyList = dependencyList.concat(deps);
109
113
  }
110
114
 
111
- React.useLayoutEffect(function () {
115
+ React.useEffect(function () {
112
116
  // @ts-ignore
113
- return player.on(name, callback);
117
+ player.on(name, callback);
118
+ return function () {
119
+ // @ts-ignore
120
+ player.off(name, callback);
121
+ };
114
122
  }, dependencyList);
115
123
  }
116
124
 
@@ -38,7 +38,11 @@ export declare enum VreoKeyframeEnum {
38
38
  /**
39
39
  * 结束
40
40
  */
41
- Exit = "Exit"
41
+ Exit = "Exit",
42
+ /**
43
+ * 自定义剧本帧
44
+ */
45
+ Custom = "Custom"
42
46
  }
43
47
  /**
44
48
  * 支持自定义剧本关键帧配置项
@@ -202,3 +206,5 @@ export declare type VideoEffectData = {
202
206
  direction?: Vertex;
203
207
  vector?: Pick<Pose, 'longitude' | 'latitude'>;
204
208
  };
209
+ /** 自定义序列帧 */
210
+ export declare type CustomData = Record<string, any>;
@@ -22,6 +22,7 @@ exports.VreoKeyframeEnum = VreoKeyframeEnum;
22
22
  VreoKeyframeEnum["InfoPanel"] = "InfoPanel";
23
23
  VreoKeyframeEnum["VideoEffect"] = "VideoEffect";
24
24
  VreoKeyframeEnum["Exit"] = "Exit";
25
+ VreoKeyframeEnum["Custom"] = "Custom";
25
26
  })(VreoKeyframeEnum || (exports.VreoKeyframeEnum = VreoKeyframeEnum = {}));
26
27
 
27
28
  /**
package/package.json CHANGED
@@ -1,7 +1,11 @@
1
1
  {
2
2
  "name": "@realsee/vreo",
3
- "version": "0.1.2",
3
+ "version": "0.1.6",
4
4
  "description": "Vreo (VR Video 缩写) 是基于如视三维渲染引擎 Five 和 用户界面构建库 React 实现的如视 3D 空间剧本播放器。",
5
+ "keywords": [
6
+ "realsee",
7
+ "vreo"
8
+ ],
5
9
  "main": "lib/index.js",
6
10
  "author": "BEIKE REALSEE TECHNOLOGY (HK) LIMITED",
7
11
  "license": "MIT",
@@ -31,7 +35,7 @@
31
35
  "@babel/preset-env": "^7.16.4",
32
36
  "@babel/preset-react": "^7.16.0",
33
37
  "@babel/preset-typescript": "^7.16.0",
34
- "@realsee/five": "^5.0.0-alpha.57",
38
+ "@realsee/five": "^5.0.0-alpha.80",
35
39
  "@types/offscreencanvas": "^2019.6.4",
36
40
  "@types/react": "^17.0.37",
37
41
  "@types/react-dom": "^17.0.11",
@@ -24,6 +24,9 @@ const AppView = observer(({ controller }: { controller: Controller }) => {
24
24
  <div className="vreo-panel-inner">
25
25
  <VideoAgent
26
26
  onClick={() => {
27
+ if (controller.isAudio) {
28
+ return
29
+ }
27
30
  if (controller.playing) {
28
31
  controller.setPlaying(false)
29
32
  } else {
@@ -1,5 +1,5 @@
1
1
  import { Five, Subscribe } from '@realsee/five'
2
- import { action, makeObservable, observable, reaction } from 'mobx'
2
+ import { action, computed, makeObservable, observable, reaction } from 'mobx'
3
3
  import * as React from 'react'
4
4
  import { requestAnimationFrameInterval } from '../shared-utils/animationFrame'
5
5
  import { VreoKeyframe, VreoKeyframeEnum, VreoKeyframeEvent, VreoUnit } from '../typings/VreoUnit'
@@ -20,6 +20,10 @@ export class Controller extends Subscribe<VreoKeyframeEvent> {
20
20
 
21
21
  visible = false
22
22
 
23
+ get isAudio() {
24
+ return !this.videoAgentScene?.videoAgentMesh.videoUrl?.endsWith('.mp4')
25
+ }
26
+
23
27
  drawerConfig: {
24
28
  content: string | JSX.Element
25
29
  height?: number | string
@@ -55,7 +59,6 @@ export class Controller extends Subscribe<VreoKeyframeEvent> {
55
59
  setPlaying: action,
56
60
  drawerConfig: observable.ref,
57
61
  openDrawer: action,
58
-
59
62
  })
60
63
 
61
64
  // 监听播放情况:抛出触发时机
@@ -149,6 +152,3 @@ export class Controller extends Subscribe<VreoKeyframeEvent> {
149
152
  }
150
153
 
151
154
  export const ControllerContext = React.createContext<Controller | null>(null)
152
-
153
-
154
-
@@ -9,18 +9,19 @@ import { reaction } from 'mobx'
9
9
  import { Drawer } from './modules/Drawer'
10
10
  import { PlayerConfigs } from './typings'
11
11
 
12
- const controller = new Controller()
13
-
14
12
  export class Player extends Subscribe<VreoKeyframeEvent> {
15
13
  $five: Five
14
+ private controller: Controller
16
15
  configs: Readonly<PlayerConfigs>
17
16
 
18
17
  constructor(five: Five, configs: Partial<PlayerConfigs> = {}) {
19
18
  super()
20
- this.$five = controller.$five = five
19
+ this.controller = new Controller()
20
+ this.$five = this.controller.$five = five
21
21
 
22
22
  if (!configs.containter) {
23
- const containter = document.createElement('div')
23
+ const containter = document.querySelector('#vreo-app') || document.createElement('div')
24
+ ReactDOM.unmountComponentAtNode(containter)
24
25
  containter.setAttribute('id', 'vreo-app')
25
26
  configs.containter = containter
26
27
  document.body.append(containter)
@@ -35,10 +36,10 @@ export class Player extends Subscribe<VreoKeyframeEvent> {
35
36
  )
36
37
  )
37
38
 
38
- controller.configs = this.configs
39
+ this.controller.configs = this.configs
39
40
 
40
41
  ReactDOM.render(
41
- <ControllerContext.Provider value={controller}>
42
+ <ControllerContext.Provider value={this.controller}>
42
43
  <App></App>
43
44
  <Drawer />
44
45
  </ControllerContext.Provider>,
@@ -47,7 +48,7 @@ export class Player extends Subscribe<VreoKeyframeEvent> {
47
48
 
48
49
  // 监听播放情况:抛出触发时机
49
50
  reaction(
50
- () => controller.playing,
51
+ () => this.controller.playing,
51
52
  (playing) => {
52
53
  this.emit(playing ? 'playing' : 'paused')
53
54
  }
@@ -55,49 +56,61 @@ export class Player extends Subscribe<VreoKeyframeEvent> {
55
56
  }
56
57
 
57
58
  async load(vreoUnit: VreoUnit, currentTime = 0) {
58
- if (controller.stopInterval) {
59
- controller.stopInterval()
60
- controller.stopInterval = undefined
59
+ if (!this.controller.visible) {
60
+ this.controller.setVisible(true)
61
+ await new Promise((resolve) => {
62
+ setTimeout(() => resolve(true), 500)
63
+ })
64
+ }
65
+
66
+ if (this.controller.stopInterval) {
67
+ this.controller.stopInterval()
68
+ this.controller.stopInterval = undefined
61
69
  }
62
70
 
63
- controller.vreoUnit = vreoUnit
64
- controller.videoInstance?.pause()
71
+ this.controller.vreoUnit = vreoUnit
72
+ this.controller.videoInstance?.pause()
65
73
 
66
74
  if (vreoUnit.video.url) {
67
- if (controller.videoAgentScene?.videoAgentMesh.videoInstance) {
68
- controller.videoAgentScene.videoAgentMesh.videoInstance.currentTime = currentTime / 1000
75
+ if (this.controller.videoAgentScene?.videoAgentMesh.videoInstance) {
76
+ this.controller.videoAgentScene.videoAgentMesh.videoInstance.currentTime = currentTime / 1000
69
77
  }
70
- await controller.videoAgentScene?.videoAgentMesh.play(vreoUnit.video.url)
78
+ await this.controller.videoAgentScene?.videoAgentMesh.play(vreoUnit.video.url)
71
79
  this.play()
72
80
  }
73
- controller.run((type, keyframe) => this.emit(type, keyframe))
81
+ this.controller.run((type, keyframe) => this.emit(type, keyframe))
74
82
  return true
75
83
  }
76
84
 
77
85
  get paused() {
78
- return !controller.playing
86
+ return !this.controller.playing
79
87
  }
80
88
 
81
89
  play() {
82
- if (controller.playing) return true
83
- controller.setPlaying(true)
90
+ if (this.controller.playing) return true
91
+ this.controller.setPlaying(true)
84
92
  return true
85
93
  }
86
94
 
87
95
  pause() {
88
- controller.setPlaying(false)
96
+ this.controller.setPlaying(false)
89
97
  }
90
98
 
91
99
  show() {
92
- controller.setVisible(true)
100
+ this.controller.setVisible(true)
93
101
  }
94
102
 
95
103
  hide() {
96
- controller.setVisible(false)
104
+ this.controller.setVisible(false)
97
105
  }
98
106
 
99
107
  dispose() {
100
- controller.dispose()
108
+ this.pause()
109
+ this.controller.dispose()
110
+
111
+ if (this.configs.containter) {
112
+ ReactDOM.unmountComponentAtNode(this.configs.containter as Element)
113
+ }
101
114
  }
102
115
  }
103
116
 
@@ -57,6 +57,11 @@ void main(void) {
57
57
  }
58
58
  `
59
59
 
60
+ const cacheInstance: {
61
+ videoInstance?: HTMLVideoElement
62
+ audioInstance?: HTMLAudioElement
63
+ } = {}
64
+
60
65
  export interface VideoAgentMeshOptions {
61
66
  videoInstance?: HTMLVideoElement
62
67
  }
@@ -70,6 +75,7 @@ export class VideoAgentMesh extends THREE.Mesh {
70
75
  freeze: boolean
71
76
  paused: boolean
72
77
  audioInstance: HTMLAudioElement
78
+ $removeEventListener: () => void
73
79
 
74
80
  get videoInstance(): HTMLAudioElement {
75
81
  if (this.videoUrl?.endsWith('mp3')) {
@@ -96,13 +102,19 @@ export class VideoAgentMesh extends THREE.Mesh {
96
102
  options: VideoAgentMeshOptions = {},
97
103
  ) {
98
104
  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
105
+ if (cacheInstance.videoInstance) {
106
+ options.videoInstance = cacheInstance.videoInstance
107
+ } else {
108
+ const videoInstance = document.createElement('video')
109
+ videoInstance.style.opacity = '0'
110
+ videoInstance.style.display = 'none'
111
+ document.body.append(videoInstance)
112
+ options.videoInstance = videoInstance
113
+ cacheInstance.videoInstance = videoInstance
114
+ videoInstance.playsInline = true
115
+ videoInstance.controls = false
116
+ }
117
+
106
118
  }
107
119
 
108
120
  const geometry = new THREE.PlaneGeometry(width, height, widthSegments, heightSegments)
@@ -123,24 +135,41 @@ export class VideoAgentMesh extends THREE.Mesh {
123
135
  this.paused = true
124
136
 
125
137
  {
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
138
+ if (cacheInstance.audioInstance) {
139
+ this.audioInstance = cacheInstance.audioInstance
140
+
141
+ } else {
142
+ const audioInstance = document.createElement('audio')
143
+ audioInstance.crossOrigin = ''
144
+ // videoInstance.muted = true
145
+ 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;')
150
+ document.body.appendChild(audioInstance)
151
+ this.audioInstance = audioInstance
152
+ cacheInstance.audioInstance = audioInstance
153
+ }
154
+
136
155
  }
156
+
137
157
  makeObservable(this, { paused: observable })
138
158
 
139
159
  const updatePaused = (paused: boolean) => runInAction(() => (this.paused = paused))
140
- this.videoInstance.addEventListener('pause', () => updatePaused(true))
141
- this.videoInstance.addEventListener('play', () => updatePaused(false))
160
+ const onPause = () => updatePaused(true)
161
+ const onPlay = () => updatePaused(false)
162
+
163
+ this.videoInstance.addEventListener('pause', onPause)
164
+ this.videoInstance.addEventListener('play', onPlay)
165
+
166
+ this.$removeEventListener = () => {
167
+ this.videoInstance.removeEventListener('pause', onPause)
168
+ this.videoInstance.removeEventListener('play', onPlay)
169
+ }
142
170
  }
143
171
 
172
+
144
173
  private async update(videoUrl: string) {
145
174
  if (this.videoUrl === videoUrl) {
146
175
  return
@@ -159,7 +188,7 @@ export class VideoAgentMesh extends THREE.Mesh {
159
188
  if (this.videoInstance.currentTime === 0) return
160
189
  this.freeze = false
161
190
  this.videoInstance.muted = false
162
- uniforms.enable.value = 1
191
+ uniforms.enable.value = this.videoUrl?.endsWith('.mp4') ? 1 : 0
163
192
  this.videoInstance.removeEventListener('timeupdate', onStart, false)
164
193
  }
165
194
 
@@ -199,4 +228,18 @@ export class VideoAgentMesh extends THREE.Mesh {
199
228
  get currentTime() {
200
229
  return this.videoInstance.currentTime * 1000
201
230
  }
231
+
232
+ dispose() {
233
+ // 销毁事件监听
234
+ this.$removeEventListener()
235
+ if (cacheInstance.audioInstance) {
236
+ document.body.removeChild(cacheInstance.audioInstance)
237
+ cacheInstance.audioInstance = undefined
238
+ }
239
+
240
+ if (cacheInstance.videoInstance) {
241
+ document.body.removeChild(cacheInstance.videoInstance)
242
+ cacheInstance.videoInstance = undefined
243
+ }
244
+ }
202
245
  }
@@ -7,8 +7,10 @@ export class VideoAgentScene {
7
7
  scene = new THREE.Scene()
8
8
  camera = new THREE.OrthographicCamera(-240, 240, 135, -135)
9
9
  renderer = new THREE.WebGLRenderer({ alpha: true })
10
+ container: HTMLElement
10
11
 
11
12
  constructor(container: HTMLElement, needRender = true) {
13
+ this.container = container
12
14
  this.camera.position.set(0, 0, 10)
13
15
  this.camera.lookAt(0, 0, 0)
14
16
  const state = {
@@ -35,4 +37,8 @@ export class VideoAgentScene {
35
37
  }
36
38
  requestAnimationFrame(this.run)
37
39
  }
40
+
41
+ dispose = () => {
42
+ this.videoAgentMesh?.dispose()
43
+ }
38
44
  }
@@ -14,6 +14,11 @@ export function VideoAgent(props: { onClick?: () => void }) {
14
14
  }
15
15
  const videoAgentScene = new VideoAgentScene(ref.current)
16
16
  controller.videoAgentScene = videoAgentScene
17
+
18
+ return () => {
19
+ controller.dispose()
20
+ controller.videoAgentScene?.dispose()
21
+ }
17
22
  }, [])
18
23
 
19
24
  return (
@@ -45,6 +45,7 @@ export function Prompter() {
45
45
  <div
46
46
  className={classNames('vreo-prompter', {
47
47
  'vreo-prompter--hidden': hidden,
48
+ 'vreo-prompter--audio': controller.isAudio,
48
49
  })}
49
50
  >
50
51
  {text ? (
@@ -139,11 +139,16 @@ export const CameraMovementPlugin: FivePlugin<CameraMovementPluginParameterType,
139
139
  opts.asyncStartCallback()
140
140
  }
141
141
 
142
- if (five.currentMode !== Five.Mode.Panorama) {
143
- await five.changeMode(Five.Mode.Panorama)
142
+ // 先切换模型再出旋转效果
143
+ if (args.mode && five.currentMode !== args.mode) {
144
+ await five.changeMode(args.mode)
144
145
  }
145
146
 
146
- if (args.panoIndex !== undefined && args.panoIndex !== five.panoIndex) {
147
+ // 全景状态下,需要点位对齐
148
+ if (args.mode === Five.Mode.Panorama &&
149
+ five.currentMode === Five.Mode.Panorama &&
150
+ args.panoIndex !== undefined &&
151
+ args.panoIndex !== five.panoIndex) {
147
152
  if (opts.preload) await five.preloadPano(args.panoIndex)
148
153
  await new Promise((resolve, reject) => {
149
154
  five.moveToPano(args.panoIndex, {
@@ -46,6 +46,7 @@ export type MoveOpts = {
46
46
  } & CameraMovementOptsCallback
47
47
 
48
48
  export type RotateArgs = {
49
+ mode?: Mode
49
50
  panoIndex: number
50
51
  loop: boolean
51
52
  rotateSpeed: number