@realsee/vreo 0.2.0-alpha.9 → 1.0.0-alpha.2
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/CHANGELOG.md +30 -0
- package/README.md +16 -5
- package/docs/assets/highlight.css +1 -1
- package/docs/assets/main.js +2 -2
- package/docs/assets/search.js +1 -1
- package/docs/assets/style.css +28 -2
- package/docs/classes/Player.AudioLike.html +52 -0
- package/docs/classes/Player.Controller.html +13 -13
- package/docs/classes/Player.Player-1.html +11 -11
- package/docs/classes/Player.VideoAgentMesh.html +53 -53
- package/docs/classes/Player.VideoAgentScene.html +1 -1
- package/docs/demo/assets/AppReact.d0ee843a.js +1 -0
- package/docs/demo/assets/default.63925584.js +3984 -0
- package/docs/demo/assets/default.f3f3ca8c.css +1 -0
- package/docs/demo/assets/dynamic.76506c2c.js +1 -0
- package/docs/demo/assets/examples.3d8323db.js +25 -0
- package/docs/demo/assets/examples.dac4a6fe.css +1 -0
- package/docs/demo/assets/index.7e31122c.js +1 -0
- package/docs/demo/assets/index.9331038f.css +1 -0
- package/docs/demo/assets/main.79b11b9d.js +1 -0
- package/docs/demo/assets/react.2ee8565f.css +1 -0
- package/docs/demo/assets/react.c1c07c14.js +25 -0
- package/docs/demo/assets/znRoyv06SZQeqA7m.0057e8ae.js +1 -0
- package/docs/demo/examples.html +17 -0
- package/docs/demo/index-react-dynamic.html +20 -0
- package/docs/demo/index-react.html +20 -0
- package/docs/demo/index.html +19 -0
- package/docs/enums/Player.InfoPanelStyleEnum.html +1 -1
- package/docs/enums/Player.InfoPanelTypeEnum.html +2 -2
- package/docs/enums/Player.PanoEffectEnum.html +2 -2
- package/docs/enums/Player.PanoTagEnum.html +2 -2
- package/docs/enums/Player.PanoTagStyleEnum.html +7 -0
- package/docs/enums/Player.VreoKeyframeEnum.html +12 -12
- package/docs/enums/fivePlugins.CameraMovementEffect.html +1 -1
- package/docs/enums/fivePlugins.Rotation.html +1 -1
- package/docs/index.html +13 -6
- package/docs/interfaces/Player.CustomVreoKeyframeProps.html +1 -1
- package/docs/interfaces/Player.PlayerConfigs.html +4 -3
- package/docs/interfaces/Player.Vertex.html +2 -2
- package/docs/interfaces/Player.VideoAgentMeshOptions.html +8 -1
- package/docs/interfaces/Player.VreoKeyframe.html +2 -2
- package/docs/interfaces/Player.VreoUnit.html +2 -2
- package/docs/interfaces/Player.VreoVideo.html +2 -2
- package/docs/interfaces/fivePlugins.CSS3DRenderPluginExportType.html +4 -0
- package/docs/interfaces/fivePlugins.CameraMovementOptsCallback.html +1 -1
- package/docs/interfaces/fivePlugins.CameraMovementPluginExportType.html +2 -2
- package/docs/interfaces/fivePlugins.CameraMovementPluginParameterType.html +1 -1
- package/docs/interfaces/fivePlugins.ModelTVVideoPluginData.html +1 -1
- package/docs/interfaces/fivePlugins.ModelTVVideoPluginExportType.html +1 -1
- package/docs/interfaces/fivePlugins.ModelTVVideoPluginParameterType.html +1 -1
- package/docs/interfaces/fivePlugins.Vector3Position.html +3 -0
- package/docs/interfaces/react.VreoActionCallbacks.html +15 -7
- package/docs/interfaces/react.VreoProviderProps.html +1 -1
- package/docs/modules/Player.html +21 -15
- package/docs/modules/custom.html +1 -1
- package/docs/modules/fivePlugins.html +5 -2
- package/docs/modules/react.html +1 -1
- package/docs/modules.html +1 -1
- package/lib/PlayController/index.js +4 -5
- package/lib/Player/App.js +9 -7
- package/lib/Player/Controller.d.ts +29 -8
- package/lib/Player/Controller.js +193 -27
- package/lib/Player/custom/SpatialScenePanel/index.js +5 -4
- package/lib/Player/hooks.js +2 -2
- package/lib/Player/index.d.ts +5 -3
- package/lib/Player/index.js +97 -53
- package/lib/Player/modules/Drawer/index.js +1 -1
- package/lib/Player/modules/PopUp/index.js +1 -1
- package/lib/Player/modules/ResizeObserver/index.d.ts +2 -0
- package/lib/Player/modules/ResizeObserver/index.js +37 -0
- package/lib/Player/modules/VideoAgent/VideoAgentMesh.d.ts +16 -2
- package/lib/Player/modules/VideoAgent/VideoAgentMesh.js +132 -59
- package/lib/Player/modules/VideoAgent/VideoAgentScene.d.ts +3 -6
- package/lib/Player/modules/VideoAgent/VideoAgentScene.js +31 -36
- package/lib/Player/modules/VideoAgent/index.js +53 -12
- package/lib/Player/modules/Wave/index.d.ts +5 -0
- package/lib/Player/modules/Wave/index.js +51 -0
- package/lib/Player/modules/keyframes/BgMusic/index.js +1 -1
- package/lib/Player/modules/keyframes/CameraMovement/index.js +1 -1
- package/lib/Player/modules/keyframes/InfoPanel/index.js +22 -10
- package/lib/Player/modules/keyframes/ModelVideo/index.js +46 -9
- package/lib/Player/modules/keyframes/PanoEffect/index.js +1 -1
- package/lib/Player/modules/keyframes/PanoEffect/lineAnime.js +1 -1
- package/lib/Player/modules/keyframes/PanoTag/index.d.ts +1 -2
- package/lib/Player/modules/keyframes/PanoTag/index.js +79 -119
- package/lib/Player/modules/keyframes/PanoTextLabel/index.d.ts +3 -0
- package/lib/Player/modules/keyframes/PanoTextLabel/index.js +96 -44
- package/lib/Player/modules/keyframes/Prompter/index.js +10 -23
- package/lib/Player/modules/keyframes/UpdateVRPanorama/index.js +10 -6
- package/lib/Player/modules/keyframes/VideoEffect/index.js +1 -1
- package/lib/Player/typings.d.ts +12 -1
- package/lib/fivePlugins/CSS3DRenderPlugin/centerPoint.d.ts +2 -0
- package/lib/fivePlugins/CSS3DRenderPlugin/centerPoint.js +12 -0
- package/lib/fivePlugins/CSS3DRenderPlugin/createResizeObserver.d.ts +7 -0
- package/lib/fivePlugins/CSS3DRenderPlugin/createResizeObserver.js +32 -0
- package/lib/fivePlugins/CSS3DRenderPlugin/evenNumber.d.ts +3 -0
- package/lib/fivePlugins/CSS3DRenderPlugin/evenNumber.js +11 -0
- package/lib/fivePlugins/CSS3DRenderPlugin/index.d.ts +56 -0
- package/lib/fivePlugins/CSS3DRenderPlugin/index.js +275 -0
- package/lib/fivePlugins/CSS3DRenderPlugin/transformPositionToVector3.d.ts +7 -0
- package/lib/fivePlugins/CSS3DRenderPlugin/transformPositionToVector3.js +18 -0
- package/lib/fivePlugins/CameraMovementPlugin/index.js +13 -12
- package/lib/fivePlugins/ModelTVVideoPlugin/index.js +1 -1
- package/lib/react/index.d.ts +8 -4
- package/lib/react/index.js +3 -3
- package/lib/shared-utils/AduioLike.d.ts +37 -0
- package/lib/shared-utils/AduioLike.js +166 -0
- package/lib/shared-utils/addResizeListener.d.ts +4 -0
- package/lib/shared-utils/addResizeListener.js +51 -0
- package/lib/shared-utils/animationFrame/BetterTween.js +7 -3
- package/lib/shared-utils/animationFrame/tween.js +1 -1
- package/lib/shared-utils/createTransMatrix.d.ts +1 -0
- package/lib/shared-utils/createTransMatrix.js +43 -0
- package/lib/shared-utils/getFileExpandedName.d.ts +1 -0
- package/lib/shared-utils/getFileExpandedName.js +13 -0
- package/lib/shared-utils/getMediaInfo.d.ts +9 -0
- package/lib/shared-utils/getMediaInfo.js +75 -0
- package/lib/shared-utils/setElementDataset.d.ts +1 -0
- package/lib/shared-utils/setElementDataset.js +24 -0
- package/lib/typings/VreoUnit.d.ts +37 -2
- package/lib/typings/VreoUnit.js +13 -1
- package/package.json +10 -10
- package/resources/PlayController/index.ts +0 -13
- package/resources/Player/App.tsx +5 -4
- package/resources/Player/Controller.ts +277 -146
- package/resources/Player/custom/SpatialScenePanel/index.tsx +5 -5
- package/resources/Player/hooks.ts +3 -5
- package/resources/Player/index.tsx +189 -141
- package/resources/Player/modules/ResizeObserver/index.tsx +23 -0
- package/resources/Player/modules/VideoAgent/VideoAgentMesh.ts +87 -31
- package/resources/Player/modules/VideoAgent/VideoAgentScene.ts +27 -24
- package/resources/Player/modules/VideoAgent/index.tsx +28 -12
- package/resources/Player/modules/Wave/index.tsx +18 -0
- package/resources/Player/modules/keyframes/InfoPanel/index.tsx +26 -11
- package/resources/Player/modules/keyframes/ModelVideo/index.tsx +36 -3
- package/resources/Player/modules/keyframes/PanoTag/index.tsx +53 -97
- package/resources/Player/modules/keyframes/PanoTextLabel/index.tsx +86 -34
- package/resources/Player/modules/keyframes/Prompter/index.tsx +6 -16
- package/resources/Player/modules/keyframes/UpdateVRPanorama/index.tsx +6 -2
- package/resources/Player/typings.ts +15 -1
- package/resources/fivePlugins/CSS3DRenderPlugin/centerPoint.ts +5 -0
- package/resources/fivePlugins/CSS3DRenderPlugin/createResizeObserver.ts +17 -0
- package/resources/fivePlugins/CSS3DRenderPlugin/evenNumber.ts +4 -0
- package/resources/fivePlugins/CSS3DRenderPlugin/index.ts +254 -0
- package/resources/fivePlugins/CSS3DRenderPlugin/transformPositionToVector3.ts +5 -0
- package/resources/fivePlugins/CameraMovementPlugin/index.ts +19 -12
- package/resources/react/index.tsx +7 -3
- package/resources/shared-utils/AduioLike.ts +109 -0
- package/resources/shared-utils/addResizeListener.ts +46 -0
- package/resources/shared-utils/createTransMatrix.ts +12 -0
- package/resources/shared-utils/getFileExpandedName.ts +6 -0
- package/resources/shared-utils/getMediaInfo.ts +52 -0
- package/resources/shared-utils/setElementDataset.ts +18 -0
- package/resources/typings/VreoUnit.ts +41 -3
- package/stylesheets/custom/SpatialScenePanel.css +2 -1
- package/stylesheets/default.css +348 -84
|
@@ -9,16 +9,19 @@ export function UpdateVRPanorama() {
|
|
|
9
9
|
const deafultWorkRef = React.useRef<string[] | null>(null)
|
|
10
10
|
const updateWorkRef = React.useRef<Work | null>(null)
|
|
11
11
|
|
|
12
|
+
// 恢复默认 VR
|
|
12
13
|
const restoreCallback = React.useCallback(() => {
|
|
14
|
+
// 先清理掉 之前的 VR 数据备份
|
|
15
|
+
updateWorkRef.current = null
|
|
13
16
|
|
|
14
17
|
if (deafultWorkRef.current) {
|
|
15
18
|
const work = parseWork(deafultWorkRef.current)
|
|
16
19
|
five.load(work)
|
|
17
20
|
}
|
|
18
21
|
|
|
19
|
-
updateWorkRef.current = null
|
|
20
22
|
}, [])
|
|
21
23
|
|
|
24
|
+
// 暂停时: 恢复默认 VR
|
|
22
25
|
const pausedCallback = React.useCallback(() => {
|
|
23
26
|
if (deafultWorkRef.current) {
|
|
24
27
|
const work = parseWork(deafultWorkRef.current)
|
|
@@ -27,6 +30,7 @@ export function UpdateVRPanorama() {
|
|
|
27
30
|
}
|
|
28
31
|
}, [])
|
|
29
32
|
|
|
33
|
+
// 播放时:如果暂停前有更新 VR 需要还原
|
|
30
34
|
const playingCallback = React.useCallback(() => {
|
|
31
35
|
if (!updateWorkRef.current) {
|
|
32
36
|
return
|
|
@@ -89,7 +93,7 @@ export function UpdateVRPanorama() {
|
|
|
89
93
|
controller.off('paused', pausedCallback)
|
|
90
94
|
controller.off('playing', playingCallback)
|
|
91
95
|
}
|
|
92
|
-
}, [
|
|
96
|
+
}, [])
|
|
93
97
|
|
|
94
98
|
return <></>
|
|
95
99
|
}
|
|
@@ -5,7 +5,11 @@ import { VreoKeyframeConfigMap, VreoKeyframeEvent } from '../typings/VreoUnit'
|
|
|
5
5
|
import { VideoAgentMeshOptions } from './modules/VideoAgent/VideoAgentMesh';
|
|
6
6
|
|
|
7
7
|
export interface PlayerConfigs {
|
|
8
|
-
|
|
8
|
+
/**
|
|
9
|
+
* @deprecated rename to container
|
|
10
|
+
*/
|
|
11
|
+
containter?: Element
|
|
12
|
+
container?: Element
|
|
9
13
|
keyframeMap: VreoKeyframeConfigMap
|
|
10
14
|
/**
|
|
11
15
|
* 微信端自动播放功能实现需要提前初始化 Video 实例。
|
|
@@ -25,6 +29,16 @@ export interface PlayerConfigs {
|
|
|
25
29
|
customKeyframes?: React.FC<CustomVreoKeyframeProps>[]
|
|
26
30
|
imageOptions?: { size: number }
|
|
27
31
|
autoPreload?: boolean
|
|
32
|
+
appSize?: AppSize
|
|
33
|
+
appearance?: Appearance
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export type AppSize = 'S' | 'M' | 'L' | 'XL'
|
|
37
|
+
|
|
38
|
+
export type WaveAppearance = 'single' | 'double' | 'solid' | 'swap' | 'expand'
|
|
39
|
+
|
|
40
|
+
export interface Appearance {
|
|
41
|
+
waveStyle?: 'wave' | 'solid'
|
|
28
42
|
}
|
|
29
43
|
|
|
30
44
|
export type VreoSubscribe = Pick<Subscribe<VreoKeyframeEvent>, 'on' | 'once' | 'off'>
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @description: 简陋的 ResizeObserver polyfill
|
|
3
|
+
*/
|
|
4
|
+
export default function createResizeObserver(func: () => any, element?: HTMLElement) {
|
|
5
|
+
if (!element || typeof ResizeObserver === 'undefined') {
|
|
6
|
+
return {
|
|
7
|
+
observe: () => window.addEventListener('resize', func),
|
|
8
|
+
unobserve: () => window.removeEventListener('resize', func),
|
|
9
|
+
}
|
|
10
|
+
} else {
|
|
11
|
+
const observer = new ResizeObserver(func)
|
|
12
|
+
return {
|
|
13
|
+
observe: () => observer.observe(element),
|
|
14
|
+
unobserve: () => observer.unobserve(element),
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
}
|
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
import { FivePlugin } from '@realsee/five'
|
|
2
|
+
import * as THREE from 'three'
|
|
3
|
+
import { Scene, Vector3 } from 'three'
|
|
4
|
+
import { CSS3DObject, CSS3DRenderer } from 'three/examples/jsm/renderers/CSS3DRenderer'
|
|
5
|
+
import evenNumber from './evenNumber'
|
|
6
|
+
import centerPoint from './centerPoint'
|
|
7
|
+
import transformPositionToVector3 from './transformPositionToVector3'
|
|
8
|
+
import createResizeObserver from './createResizeObserver'
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* 三维向量
|
|
12
|
+
*/
|
|
13
|
+
export interface Vector3Position {
|
|
14
|
+
x: number
|
|
15
|
+
y: number
|
|
16
|
+
z: number
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export type CSS3DRenderPluginParameterType = undefined
|
|
20
|
+
export interface CSS3DRenderPluginExportType {
|
|
21
|
+
/**
|
|
22
|
+
* @description: 根据传入的四个点位创建一个3d dom容器,可以通过ReactDom.render()的方式将react组件放到容器中
|
|
23
|
+
* @param {Five} five
|
|
24
|
+
* @param {Vector3[] | Vector3Position[]} points: 矩形四个点坐标
|
|
25
|
+
* @param {number} config.ratio: 1px对应多少米,默认为 0.002,即1px对应2mm
|
|
26
|
+
* @param {number} config.dpr: dpr, 默认为1
|
|
27
|
+
* @param {HTMLElement} config.container: HTMLElement
|
|
28
|
+
* @param {number} config.autoRender: 默认为true,如果需要手动render则传false
|
|
29
|
+
* @param {'front' | 'behind'} config.mode: 两种模式
|
|
30
|
+
* @param {HTMLElement} config.behindFiveContainer: 如果config.mode为'behind',需要传入容器,并确保此容器位five下方,且中间没有其他dom元素遮挡
|
|
31
|
+
* @return {{ container: HTMLElement; dispose: () => void }}: container: dom容器, dispose: 清理函数
|
|
32
|
+
*/
|
|
33
|
+
create3DDomContainer: (
|
|
34
|
+
points: Vector3[] | Vector3Position[],
|
|
35
|
+
config?: { ratio?: number; dpr?: number; autoRender?: boolean; container?: HTMLElement; [key: string]: any } & (
|
|
36
|
+
| { mode?: 'front' }
|
|
37
|
+
| { mode: 'behind'; behindFiveContainer: HTMLElement }
|
|
38
|
+
),
|
|
39
|
+
) => { container: HTMLElement; dispose: () => void; css3DObject: CSS3DObject; render?: () => void } | void
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* @description: 销毁所有的渲染内容
|
|
43
|
+
* @return {void}
|
|
44
|
+
*/
|
|
45
|
+
disposeAll: () => void
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
type Create3DDomContainerInterface = CSS3DRenderPluginExportType['create3DDomContainer']
|
|
49
|
+
type Create3DDomContainerConfigInterface = NonNullable<Parameters<Create3DDomContainerInterface>[1]>
|
|
50
|
+
interface CSS3DRenderPluginState {
|
|
51
|
+
disposeCallbacks: (() => boolean)[]
|
|
52
|
+
frontMode?: {
|
|
53
|
+
scene: Scene
|
|
54
|
+
css3DRenderer: CSS3DRenderer
|
|
55
|
+
}
|
|
56
|
+
behindMode?: {
|
|
57
|
+
scene: Scene
|
|
58
|
+
css3DRenderer: CSS3DRenderer
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* **CSS3DRenderPlugin** 提供矩形区域(三维空间四个坐标点),你可以在此矩形区域中渲染 DOM 内容。
|
|
64
|
+
* 实现原理参考[CSS3DRenderer](https://threejs.org/docs/index.html?q=CSS3D#examples/en/renderers/CSS3DRenderer)。
|
|
65
|
+
*
|
|
66
|
+
* @returns
|
|
67
|
+
*/
|
|
68
|
+
export const CSS3DRenderPlugin: FivePlugin<CSS3DRenderPluginParameterType, CSS3DRenderPluginExportType> = (five) => {
|
|
69
|
+
const state: CSS3DRenderPluginState = { disposeCallbacks: [] }
|
|
70
|
+
const create3DDomContainer = (
|
|
71
|
+
...params: Parameters<Create3DDomContainerInterface>
|
|
72
|
+
): ReturnType<Create3DDomContainerInterface> => {
|
|
73
|
+
const points = params[0].map((point) => (point instanceof Vector3 ? point : transformPositionToVector3(point)))
|
|
74
|
+
const config = params[1]
|
|
75
|
+
if (points?.length < 4) return console.error('points must be equal or greater than than 4')
|
|
76
|
+
if (!five?.model?.loaded) return console.error('five.model.loaded is: ', five?.model?.loaded)
|
|
77
|
+
const fiveElement = five.getElement()
|
|
78
|
+
if (!fiveElement) return console.error('five.getElement() is ' + fiveElement)
|
|
79
|
+
|
|
80
|
+
const disposers: any[] = []
|
|
81
|
+
const ratio = config?.ratio || 0.00216
|
|
82
|
+
if (ratio <= 0.00215) console.warn('if you need click css3DElement on safari, ratio must be greater than 0.00215')
|
|
83
|
+
const dpr = config?.dpr || 1
|
|
84
|
+
const mode = config?.mode || 'front'
|
|
85
|
+
const autoRender = config?.autoRender ?? true
|
|
86
|
+
const behindFiveContainer = config?.behindFiveContainer || fiveElement.parentElement || document.body
|
|
87
|
+
const container = config?.container || document.createElement('div')
|
|
88
|
+
container.classList.add('__Vreo-plugin--CSS3DRenderPlugin')
|
|
89
|
+
|
|
90
|
+
// 获取css3DObject, 如果mode为behind的话,一起获取mesh
|
|
91
|
+
const { css3DObject, mesh } = createObject(points, { ratio, dpr, container, mode })
|
|
92
|
+
|
|
93
|
+
// ======= INIT START =======
|
|
94
|
+
let resizeObserver:
|
|
95
|
+
| {
|
|
96
|
+
observe?: () => void
|
|
97
|
+
unobserve?: () => void
|
|
98
|
+
}
|
|
99
|
+
| undefined
|
|
100
|
+
const frontModeUnInited = mode === 'front' && !state.frontMode
|
|
101
|
+
const behindModeUnInited = mode === 'behind' && !state.behindMode
|
|
102
|
+
const inited = !(frontModeUnInited || behindModeUnInited)
|
|
103
|
+
let stopRenderFlag = false
|
|
104
|
+
let requestAnimationFrameId: number | null = null
|
|
105
|
+
|
|
106
|
+
if (behindModeUnInited) {
|
|
107
|
+
state.behindMode = { scene: new Scene(), css3DRenderer: new CSS3DRenderer() }
|
|
108
|
+
}
|
|
109
|
+
if (frontModeUnInited) {
|
|
110
|
+
state.frontMode = { scene: new Scene(), css3DRenderer: new CSS3DRenderer() }
|
|
111
|
+
}
|
|
112
|
+
const { scene, css3DRenderer } = mode === 'behind' ? state.behindMode! : state.frontMode!
|
|
113
|
+
|
|
114
|
+
const render = (() => {
|
|
115
|
+
if (!inited) {
|
|
116
|
+
const css3DRendererSetSize = () => {
|
|
117
|
+
// 这里evenNumber策略是遇到奇数会加1,不知道为啥会触发滚动条显示,导致fiveElement变小,fiveElement变小又触发了这里的resize,宽高变小,滚动条消失,然后又触发resize。。。无限循环
|
|
118
|
+
// 为了规避上面这种情况,evenNumber设置了一个参数,遇到奇数可选择减一
|
|
119
|
+
const rendererWidth = evenNumber(fiveElement.clientWidth, { smaller: true })
|
|
120
|
+
const rendererHeight = evenNumber(fiveElement.clientHeight, { smaller: true })
|
|
121
|
+
css3DRenderer.setSize(rendererWidth, rendererHeight)
|
|
122
|
+
}
|
|
123
|
+
css3DRendererSetSize()
|
|
124
|
+
resizeObserver = createResizeObserver(css3DRendererSetSize, fiveElement)
|
|
125
|
+
css3DRenderer.domElement.style.position = 'absolute'
|
|
126
|
+
css3DRenderer.domElement.style.top = '0'
|
|
127
|
+
css3DRenderer.domElement.style.userSelect = 'none'
|
|
128
|
+
css3DRenderer.domElement.style.pointerEvents = 'none'
|
|
129
|
+
|
|
130
|
+
const renderEveryFrame = () => {
|
|
131
|
+
if (stopRenderFlag) return
|
|
132
|
+
requestAnimationFrameId = requestAnimationFrame(renderEveryFrame)
|
|
133
|
+
css3DRenderer.render(scene!, five.camera)
|
|
134
|
+
}
|
|
135
|
+
return () => {
|
|
136
|
+
scene.add(css3DObject)
|
|
137
|
+
resizeObserver?.observe?.()
|
|
138
|
+
renderEveryFrame()
|
|
139
|
+
if (mode === 'behind' && mesh) {
|
|
140
|
+
five.scene.add(mesh)
|
|
141
|
+
disposers.push(() => mesh && five.scene.remove(mesh))
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
} else {
|
|
145
|
+
return () => {
|
|
146
|
+
scene.add(css3DObject)
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
})()
|
|
150
|
+
if (frontModeUnInited) {
|
|
151
|
+
const wrapper = fiveElement.parentElement || document.body
|
|
152
|
+
wrapper.appendChild(css3DRenderer.domElement)
|
|
153
|
+
}
|
|
154
|
+
if (behindModeUnInited) {
|
|
155
|
+
const wrapper = behindFiveContainer
|
|
156
|
+
wrapper.appendChild(css3DRenderer.domElement)
|
|
157
|
+
}
|
|
158
|
+
// ======= INIT END =======
|
|
159
|
+
|
|
160
|
+
const dispose = () => {
|
|
161
|
+
stopRenderFlag = true
|
|
162
|
+
disposers.forEach((d) => d?.())
|
|
163
|
+
scene.remove(css3DObject)
|
|
164
|
+
if (typeof requestAnimationFrameId === 'number') cancelAnimationFrame(requestAnimationFrameId)
|
|
165
|
+
if (scene.children.length === 0) {
|
|
166
|
+
css3DRenderer.domElement.remove()
|
|
167
|
+
if (mode === 'front') state.frontMode = undefined
|
|
168
|
+
if (mode === 'behind') state.behindMode = undefined
|
|
169
|
+
if (!state.behindMode && !state.frontMode) {
|
|
170
|
+
resizeObserver?.unobserve?.()
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
return true
|
|
174
|
+
}
|
|
175
|
+
const idx = state.disposeCallbacks.findIndex((item) => item === dispose)
|
|
176
|
+
if (idx !== -1) {
|
|
177
|
+
state.disposeCallbacks.splice(idx, 1)
|
|
178
|
+
} else {
|
|
179
|
+
state.disposeCallbacks.push(dispose)
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
if (autoRender) render()
|
|
183
|
+
return { container, dispose, css3DObject, render: autoRender ? undefined : render }
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
const createObject = (
|
|
187
|
+
points: Vector3[],
|
|
188
|
+
config: Required<Pick<Create3DDomContainerConfigInterface, 'ratio' | 'dpr' | 'container'>> &
|
|
189
|
+
Pick<Create3DDomContainerConfigInterface, 'mode'>,
|
|
190
|
+
) => {
|
|
191
|
+
const { ratio, dpr, container: element, mode } = config
|
|
192
|
+
const planeWidth = points[0].distanceTo(points[1])
|
|
193
|
+
const planeHeight = points[1].distanceTo(points[2])
|
|
194
|
+
const domWidthPx = evenNumber((planeWidth / ratio) * dpr)
|
|
195
|
+
const domHeightPx = evenNumber((planeHeight / ratio) * dpr)
|
|
196
|
+
|
|
197
|
+
const css3DObject = new CSS3DObject(element)
|
|
198
|
+
css3DObject.scale.set(ratio, ratio, ratio)
|
|
199
|
+
element.style.width = domWidthPx + 'px'
|
|
200
|
+
element.style.height = domHeightPx + 'px'
|
|
201
|
+
element.style.pointerEvents = 'none'
|
|
202
|
+
|
|
203
|
+
const centerPosition = centerPoint(points[0], points[2])
|
|
204
|
+
|
|
205
|
+
const vector01 = points[1].clone().sub(points[0]) // 点0 -> 点1 的向量
|
|
206
|
+
const vector12 = points[2].clone().sub(points[1]) // 点1 -> 点2 的向量
|
|
207
|
+
|
|
208
|
+
// 旋转
|
|
209
|
+
const rotateXAngle = new Vector3(0, 1, 0).angleTo(new Vector3(0, vector12.y, vector12.z))
|
|
210
|
+
const rotateYAngle = new Vector3(1, 0, 0).angleTo(new Vector3(vector01.x, 0, vector01.z))
|
|
211
|
+
const rotateZAngle = vector01.angleTo(new Vector3(vector01.x, 0, vector01.z))
|
|
212
|
+
/**
|
|
213
|
+
* [0,1,0] => [0,0,1] 为rolate Worldx正方向
|
|
214
|
+
* [0,0,1] => [0,1,0] 为rolate Worldy正方向
|
|
215
|
+
* [0,1,1] => [-1,0,0] 为rolate Worldz正方向
|
|
216
|
+
*/
|
|
217
|
+
const rotateX = (vector12.z > 0 ? -1 : 1) * rotateXAngle
|
|
218
|
+
const rotateY = (vector01.z < 0 ? 1 : -1) * rotateYAngle
|
|
219
|
+
const rotateZ = ((vector01.x > 0 && vector01.y < 0) || (vector01.x < 0 && vector01.y > 0) ? -1 : 1) * rotateZAngle
|
|
220
|
+
|
|
221
|
+
css3DObject.rotateOnWorldAxis(new Vector3(1, 0, 0), rotateX) // x
|
|
222
|
+
css3DObject.rotateOnWorldAxis(new Vector3(0, 1, 0), rotateY) // y
|
|
223
|
+
css3DObject.rotateOnWorldAxis(new Vector3(0, 0, 1), rotateZ) // z
|
|
224
|
+
|
|
225
|
+
css3DObject.position.set(centerPosition.x, centerPosition.y, centerPosition.z)
|
|
226
|
+
|
|
227
|
+
let mesh: THREE.Mesh<THREE.PlaneGeometry, THREE.MeshBasicMaterial> | undefined
|
|
228
|
+
if (mode === 'behind') {
|
|
229
|
+
const material = new THREE.MeshBasicMaterial({
|
|
230
|
+
opacity: 0,
|
|
231
|
+
transparent: false,
|
|
232
|
+
side: THREE.DoubleSide,
|
|
233
|
+
})
|
|
234
|
+
const geometry = new THREE.PlaneGeometry(domWidthPx, domHeightPx)
|
|
235
|
+
mesh = new THREE.Mesh(geometry, material)
|
|
236
|
+
mesh.name = 'CSS3DRenderPlugin-mesh'
|
|
237
|
+
mesh.position.copy(css3DObject.position)
|
|
238
|
+
mesh.rotation.copy(css3DObject.rotation)
|
|
239
|
+
mesh.scale.copy(css3DObject.scale)
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
return { css3DObject, mesh }
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
return {
|
|
246
|
+
create3DDomContainer,
|
|
247
|
+
disposeAll: () => {
|
|
248
|
+
state.disposeCallbacks.forEach((d) => d?.())
|
|
249
|
+
state.disposeCallbacks = []
|
|
250
|
+
},
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
export default CSS3DRenderPlugin
|
|
@@ -14,29 +14,31 @@ import {
|
|
|
14
14
|
|
|
15
15
|
import { Easing, tweenProgress } from '../../shared-utils/animationFrame/tween'
|
|
16
16
|
|
|
17
|
-
const
|
|
17
|
+
const PI = Math.PI
|
|
18
|
+
const PI_2 = PI * 2
|
|
18
19
|
|
|
19
20
|
function formatLatitude(rad: number) {
|
|
20
21
|
return rad % PI_2
|
|
21
22
|
}
|
|
22
23
|
function formatLongitude(rad: number) {
|
|
23
|
-
return
|
|
24
|
+
return rad % PI_2
|
|
24
25
|
}
|
|
25
26
|
|
|
26
27
|
const getLongitudeParams = (from: number, to: number, rotation: Rotation): { from: number; to: number } => {
|
|
27
|
-
const PI = Math.PI
|
|
28
|
-
const PI_2 = PI * 2
|
|
29
28
|
from = formatLongitude(from)
|
|
30
29
|
to = formatLongitude(to)
|
|
30
|
+
|
|
31
31
|
// 逆时针旋转,初始值必须是较大的值
|
|
32
|
-
if (rotation === Rotation.Anticlockwise && from
|
|
32
|
+
if (rotation === Rotation.Anticlockwise && from > to) to += PI_2
|
|
33
|
+
|
|
33
34
|
// 顺时针旋转,结束值必须是较大值
|
|
34
|
-
if (rotation === Rotation.Clockwise && to
|
|
35
|
+
else if (rotation === Rotation.Clockwise && to > from) from += PI_2
|
|
36
|
+
|
|
35
37
|
// Loop 旋转,找锐角旋转
|
|
36
38
|
// 如果 to 比 from 大 180°,逆时针转
|
|
37
39
|
// 如果 from 比 to 大 180°,顺时针转
|
|
38
|
-
if (rotation
|
|
39
|
-
if (rotation
|
|
40
|
+
else if (rotation !== Rotation.Anticlockwise && rotation !== Rotation.Clockwise && to - from > PI) return getLongitudeParams(from, to, Rotation.Clockwise)
|
|
41
|
+
else if (rotation !== Rotation.Anticlockwise && rotation !== Rotation.Clockwise && from - to > PI) return getLongitudeParams(from, to, Rotation.Anticlockwise)
|
|
40
42
|
return { from, to }
|
|
41
43
|
}
|
|
42
44
|
|
|
@@ -50,6 +52,7 @@ function progressNumber(from: number, to: number, pst: number) {
|
|
|
50
52
|
export const CameraMovementPlugin: FivePlugin<CameraMovementPluginParameterType, CameraMovementPluginExportType> = (
|
|
51
53
|
five: Five,
|
|
52
54
|
) => {
|
|
55
|
+
|
|
53
56
|
const move = (args: Partial<MoveArgs>, duration: number, opts: MoveOpts = { preload: true }) => {
|
|
54
57
|
return new Promise<boolean>(async (resolve, reject) => {
|
|
55
58
|
|
|
@@ -177,7 +180,7 @@ export const CameraMovementPlugin: FivePlugin<CameraMovementPluginParameterType,
|
|
|
177
180
|
return await new Promise<boolean>((resolve, reject) => {
|
|
178
181
|
const onUpdate = ({ progress }: { progress: number }) => {
|
|
179
182
|
const state: Record<string, number> = {}
|
|
180
|
-
state.longitude = progressNumber(from.longitude, to.longitude, progress)
|
|
183
|
+
state.longitude = progressNumber(from.longitude, to.longitude, progress) % PI_2
|
|
181
184
|
state.latitude = progressNumber(from.latitude, to.latitude, progress)
|
|
182
185
|
state.fov = progressNumber(from.fov, to.fov, progress)
|
|
183
186
|
five.setState(state, true)
|
|
@@ -191,9 +194,13 @@ export const CameraMovementPlugin: FivePlugin<CameraMovementPluginParameterType,
|
|
|
191
194
|
resolve(false)
|
|
192
195
|
}
|
|
193
196
|
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
+
let animeDuration = duration
|
|
198
|
+
|
|
199
|
+
if (args.rotation === Rotation.Loop) {
|
|
200
|
+
animeDuration = args.rotateSpeed
|
|
201
|
+
? Math.ceil((Math.abs(to.longitude - from.longitude) / args.rotateSpeed) * 1000)
|
|
202
|
+
: duration
|
|
203
|
+
}
|
|
197
204
|
|
|
198
205
|
type _Tween = TWEEN.Tween<{ progress: number }> & { onDestroy: (p: any) => _Tween; destroy: () => void }
|
|
199
206
|
|
|
@@ -45,12 +45,16 @@ function useVreoInstance() {
|
|
|
45
45
|
export interface VreoActionCallbacks {
|
|
46
46
|
/**
|
|
47
47
|
* 载入剧本数据。
|
|
48
|
+
* @param vreoUnit 剧本数据
|
|
49
|
+
* @param currentTime 开始时间戳
|
|
50
|
+
* @param preload 是否开启预载
|
|
51
|
+
* @param force 是否强制载入
|
|
48
52
|
*/
|
|
49
|
-
load: (vreoUnit: VreoUnit, currentTime?: number) => Promise<boolean>
|
|
53
|
+
load: (vreoUnit: VreoUnit, currentTime?: number, preload?: boolean, force?: boolean) => Promise<boolean>
|
|
50
54
|
/**
|
|
51
55
|
* 播放。
|
|
52
56
|
*/
|
|
53
|
-
play: () => void
|
|
57
|
+
play: (currentTime?: number) => void
|
|
54
58
|
/**
|
|
55
59
|
* 暂停。
|
|
56
60
|
*/
|
|
@@ -77,7 +81,7 @@ export function useVreoAction() {
|
|
|
77
81
|
[player]
|
|
78
82
|
)
|
|
79
83
|
|
|
80
|
-
const play = React.useCallback(() => player.play(), [player])
|
|
84
|
+
const play = React.useCallback((currentTime?: number) => player.play(currentTime || 0), [player])
|
|
81
85
|
const pause = React.useCallback(() => player.pause(), [player])
|
|
82
86
|
const show = React.useCallback(() => player.show(), [player])
|
|
83
87
|
const hide = React.useCallback(() => player.hide(), [player])
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import { Subscribe } from '@realsee/five'
|
|
2
|
+
import { requestAnimationFrameInterval } from './animationFrame'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* 模拟 `<Audio>` 事件。
|
|
6
|
+
*/
|
|
7
|
+
export type AudioLikeEvent = {
|
|
8
|
+
// 播放
|
|
9
|
+
play: () => void
|
|
10
|
+
// 语音状态 播放 -> 暂停
|
|
11
|
+
pause: () => void
|
|
12
|
+
// 语音时间刷新
|
|
13
|
+
timeupdate: () => void
|
|
14
|
+
// 播放
|
|
15
|
+
ended: () => void
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* 模拟 `<Audio>` 功能:没有音频,但执行逻辑跟 `<Audio>` 相似。
|
|
21
|
+
*/
|
|
22
|
+
export class AudioLike extends Subscribe<AudioLikeEvent> {
|
|
23
|
+
private $timestamp: number | null = null
|
|
24
|
+
private $currentTime = 0
|
|
25
|
+
private $duration = 0
|
|
26
|
+
private stopInterval?: () => void
|
|
27
|
+
muted = false
|
|
28
|
+
src = ''
|
|
29
|
+
|
|
30
|
+
setAttribute(name: string, value: string) {
|
|
31
|
+
// nothing todo
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
constructor({ duration }: { duration?: number } = {}) {
|
|
35
|
+
super()
|
|
36
|
+
if (duration) {
|
|
37
|
+
this.$duration = duration
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
play() {
|
|
42
|
+
if (this.$currentTime === this.$duration) {
|
|
43
|
+
this.$currentTime = 0
|
|
44
|
+
}
|
|
45
|
+
if (this.$timestamp === null) {
|
|
46
|
+
this.$timestamp = performance.now() - this.$currentTime
|
|
47
|
+
}
|
|
48
|
+
this.stopInterval = requestAnimationFrameInterval(() => this.requestAnimationFrameLoop())
|
|
49
|
+
this.emit('play')
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
pause() {
|
|
53
|
+
if (this.stopInterval) {
|
|
54
|
+
this.stopInterval()
|
|
55
|
+
this.emit('pause')
|
|
56
|
+
this.stopInterval = undefined
|
|
57
|
+
}
|
|
58
|
+
this.$timestamp = null
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
requestAnimationFrameLoop() {
|
|
62
|
+
if (this.$timestamp === null) return
|
|
63
|
+
const now = performance.now()
|
|
64
|
+
this.$currentTime = now - this.$timestamp
|
|
65
|
+
this.emit('timeupdate')
|
|
66
|
+
if (this.$currentTime >= this.$duration - 10) {
|
|
67
|
+
this.$currentTime = this.$duration
|
|
68
|
+
this.pause()
|
|
69
|
+
this.emit('ended')
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
get currentTime() {
|
|
74
|
+
return this.$currentTime / 1000
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
set currentTime(time: number) {
|
|
78
|
+
this.$currentTime = time
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
get duration() {
|
|
82
|
+
return this.$duration
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
set duration(duration: number) {
|
|
86
|
+
this.pause()
|
|
87
|
+
this.$duration = duration
|
|
88
|
+
this.$timestamp = null
|
|
89
|
+
this.$currentTime = 0
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
get ended() {
|
|
93
|
+
return this.$currentTime === this.$duration
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
get paused() {
|
|
97
|
+
return !this.stopInterval
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
addEventListener(evtName: keyof AudioLikeEvent, callback: () => void, useCapture = false) {
|
|
101
|
+
this.on(evtName, callback)
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
removeEventListener(evtName: keyof AudioLikeEvent, callback: () => void, useCapture = false) {
|
|
105
|
+
this.off(evtName, callback)
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export default AudioLike
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
type ResizeHandler = (width: number, height: number) => void
|
|
2
|
+
|
|
3
|
+
const resizeObserverCache = new Map<Element, number>()
|
|
4
|
+
|
|
5
|
+
export function addResizeListener(element: Element | undefined | null, resizeHandler: ResizeHandler, immediate = true) {
|
|
6
|
+
if (!element) return () => {}
|
|
7
|
+
|
|
8
|
+
if (immediate) resizeHandler(element.clientWidth, element.clientHeight)
|
|
9
|
+
|
|
10
|
+
if (typeof ResizeObserver !== 'undefined' && ResizeObserver) {
|
|
11
|
+
const resizeObserver = new ResizeObserver(() => resizeHandler(element.clientWidth, element.clientHeight))
|
|
12
|
+
|
|
13
|
+
resizeObserver.observe(element)
|
|
14
|
+
resizeObserverCache.set(element, resizeObserverCache.get(element) || 0 + 1)
|
|
15
|
+
|
|
16
|
+
return () => {
|
|
17
|
+
if (element) resizeObserver.unobserve(element)
|
|
18
|
+
if (resizeObserverCache.get(element) === 1) {
|
|
19
|
+
resizeObserverCache.delete(element)
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
} else {
|
|
23
|
+
const iframe = document.createElement('iframe')
|
|
24
|
+
iframe.style.cssText = 'position:absolute;top:0;left:0;width:100%;height:100%;pointer-events:none;opacity:0;visibility:hidden;margin:0;border:0;padding:0;z-index:-1;'
|
|
25
|
+
iframe.classList.add('resize-sensor-iframe')
|
|
26
|
+
element.appendChild(iframe)
|
|
27
|
+
if (!iframe.contentWindow) return () => {}
|
|
28
|
+
|
|
29
|
+
iframe.contentWindow.addEventListener('resize', () => {
|
|
30
|
+
resizeHandler(iframe.clientWidth, iframe.clientHeight)
|
|
31
|
+
})
|
|
32
|
+
resizeObserverCache.set(element, resizeObserverCache.get(element) || 0 + 1)
|
|
33
|
+
|
|
34
|
+
return () => {
|
|
35
|
+
element.removeChild(iframe)
|
|
36
|
+
if (resizeObserverCache.get(element) === 1) {
|
|
37
|
+
resizeObserverCache.delete(element)
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export function hasResizeListener(element?: Element | null) {
|
|
44
|
+
if (!element) return false
|
|
45
|
+
return resizeObserverCache.get(element) || 0 > 0
|
|
46
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import * as THREE from 'three'
|
|
2
|
+
|
|
3
|
+
export function createTransMatrix(arr1: number[]) {
|
|
4
|
+
const matrix4 = new THREE.Matrix4()
|
|
5
|
+
matrix4.fromArray(arr1)
|
|
6
|
+
|
|
7
|
+
return (arr2: number[]) => {
|
|
8
|
+
const vector3 = new THREE.Vector3(...arr2)
|
|
9
|
+
vector3.applyMatrix4(matrix4)
|
|
10
|
+
return vector3.toArray()
|
|
11
|
+
}
|
|
12
|
+
}
|