@ericchen1990/pano-view 4.0.1 → 5.0.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/README.md +94 -15
- package/README.zh-CN.md +71 -8
- package/dist/data.cjs +4 -0
- package/dist/data.cjs.map +1 -1
- package/dist/data.d.mts +5 -0
- package/dist/data.d.ts +5 -0
- package/dist/data.mjs +4 -0
- package/dist/data.mjs.map +1 -1
- package/dist/index.cjs +6494 -2650
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.mts +319 -120
- package/dist/index.d.ts +319 -120
- package/dist/index.mjs +6576 -2688
- package/dist/index.mjs.map +1 -1
- package/dist/styles.css +47 -3
- package/dist/ui/styles.css +3 -0
- package/dist/ui.cjs +31 -11
- package/dist/ui.cjs.map +1 -1
- package/dist/ui.d.mts +4 -3
- package/dist/ui.d.ts +4 -3
- package/dist/ui.mjs +33 -13
- package/dist/ui.mjs.map +1 -1
- package/docs/dialogs.md +74 -2
- package/docs/dialogs.zh-CN.md +80 -2
- package/package.json +3 -2
package/docs/dialogs.zh-CN.md
CHANGED
|
@@ -122,7 +122,7 @@ export function Tour({ project }: { project: LoadedPanoProject }) {
|
|
|
122
122
|
| `entrance` | `guide` | iframe | Courtyard guide |
|
|
123
123
|
| `hall` | `story` | article | Hall story |
|
|
124
124
|
|
|
125
|
-
下面是完整源码,包含所有内容组件、映射、选中状态和点击处理。`/
|
|
125
|
+
下面是完整源码,包含所有内容组件、映射、选中状态和点击处理。`/immersive-tour` 综合案例在此映射基础上加入模型与 VR;`/overlays` 使用更小的单场景示例比较三种弹窗接入方式。保存为 `multi-hotspot-example.tsx`,以 `project` 传入已加载的项目即可;在仓库外使用时,将 `/fixtures/...` 替换为自己的资源地址。
|
|
126
126
|
|
|
127
127
|
```tsx
|
|
128
128
|
import { useEffect, useRef, useState, type ReactNode } from "react";
|
|
@@ -280,7 +280,7 @@ export function MultiHotspotTour({ project }: { project: LoadedPanoProject }) {
|
|
|
280
280
|
5. 内容容器的 key 同时包含两个 ID。通过代码直接切换选中热点时,旧内容的表单/画册状态会重置并执行清理,包括暂停原视频;外层弹窗和焦点范围继续复用。
|
|
281
281
|
6. 关闭弹窗或切换场景会清空 `selected`,卸载内容并释放模态锁。切换全屏不改变选中热点的 key,因此不会重置内容。
|
|
282
282
|
|
|
283
|
-
在 `/
|
|
283
|
+
在 `/immersive-tour` 中分别打开 **Courtyard story** 和 **Garden details**,比较两个图文热点,再尝试视频、画册和指南。点击 **Go to next room** 进入 hall 后,同样的 `story` ID 会打开内容不同的 **Hall story**。viewer 下的映射表与可复制代码会展示实际对应关系。
|
|
284
284
|
|
|
285
285
|
`body` 接受任意 React 节点:原生媒体、业务组件、图文内容,或请求 API 数据的组件。加载中/错误状态在内容组件中处理,自定义播放器和订阅在 cleanup 中释放。如果内容来自 JSON,应将已校验的内容类型映射到 React 渲染器,不要将 React 节点写进 JSON。iframe 的权限按嵌入来源配置,第三方响应头可能禁止嵌入;视频自动播放需明确开启,并受浏览器策略限制。
|
|
286
286
|
|
|
@@ -614,3 +614,81 @@ export function DialogHelp() {
|
|
|
614
614
|
| hook 提示缺少 provider | 把自定义 DOM 组件放在 `PanoOverlay` 内;不要在 `PanoViewer` 的父组件调用 Overlay hook。 |
|
|
615
615
|
|
|
616
616
|
接入时应确认普通模式与真实元素全屏、viewer 部分滚出屏幕、键盘开关与焦点恢复、长内容、二级 Portal、媒体清理及目标 Safari/移动设备的表现。最大化浏览器窗口不能替代 Fullscreen API 验证。
|
|
617
|
+
|
|
618
|
+
## VR 空间弹窗与凝视操作
|
|
619
|
+
|
|
620
|
+
`PanoDialog`、组合式 `PanoDialogRoot/Content` 和 `ProjectPopupDialog` 会自动使用同一份内容切换为空间面板。原弹窗右上角的关闭按钮继续可用;面板底部只提供内容操作,不增加关闭或退出 VR 按钮。VR 中热点 Tooltip 使用固定于热点的三维精灵,与场景共用双眼投影和镜片校正,保留显示并位于弹窗下方。手机和模拟 VR 的弹窗通过每眼独立平面图层合成,不经过全景镜片畸变;最后统一应用镜片可见区域遮罩,弹窗不会越过遮罩。
|
|
621
|
+
|
|
622
|
+
第三方弹窗可使用主入口的 `usePanoDialogSurface({ open, onOpenChange })`,将 `contentRef` 连接到内容节点,使用返回的 `portalContainer`、`visibleRect` 和 `presentation`。将返回的 `onInteractOutside` 传给弹窗内容:空间模式和操作 viewer 外的宿主控件(包括进入 VR)时会阻止自动关闭,普通模式仍支持 viewer 内的遮罩点击关闭。空间模式将内容放到 viewer 内的离屏测量区域(保留 DOM 实例),不要对内容调用 `display:none` 或将 portal 移到 `body`。保留可凝视点击的关闭按钮。完整接入参见 Playground 的第三方弹窗示例。
|
|
623
|
+
|
|
624
|
+
宿主模型使用 `usePanoSpatialControls(elementRef, adapter)` 接入一次。`PanoSpatialControlAdapter` 支持 `rotateBy(yawDegrees, pitchDegrees)`、`zoomBy(factor)`、`resetView()`、`canExecute(action)`、`subscribeFrame(invalidate)` 和同一 WebGL 上下文的 `texture`。动作名为 `left/right/up/down/in/out/reset`。仅显示已提供能力;宿主负责限制观察角度、缩放范围及复位状态。独立 Canvas 应在渲染结束后提供可读取的画面;纹理必须属于当前 viewer 的渲染上下文。不会重新加载模型或改变项目 JSON。
|
|
625
|
+
|
|
626
|
+
VR 中,视线移到弹窗外时可凝视场景热点。短暂经过热点或看向空白处会保持弹窗;在热点上完成现有 `cursorDwellMs` 后,先从上到下关闭当前弹窗栈,再触发一次目标热点操作,新操作打开的弹窗会保留,并按当前凝视方向重新定位。即使宿主复用同一个弹窗组件,也会先完成旧弹窗的关闭再执行目标操作。凝视弹窗内容时,弹窗优先于其后的热点。仅进入或退出 VR 不会关闭已打开的弹窗。
|
|
627
|
+
|
|
628
|
+
连续操作在凝视计时完成后开始,移开即停;滚动默认每隔 700 毫秒移动一小步;旋转每秒 30 度、缩放每秒 1.5 倍。关闭、复位、图集切换为单次操作。明确禁用 `cursor` 或将 `cursorDwellMs` 设为零仍会禁用自动凝视。文字输入和虚拟键盘不在支持范围。跨域 iframe、不可读取的 Canvas 或被浏览器阻止的媒体需要退出 VR 后查看或授权;凝视不能替代浏览器要求的可信用户手势。
|
|
629
|
+
|
|
630
|
+
空间窗口默认距视点约 2 米,外框按宽不超过 32°、高不超过 40°的视角缩放,避免长内容撑满双眼视野。关闭按钮视觉尺寸在普通模式和 VR 中均为 36px;VR 使用至少 56px 的不可见凝视命中范围。原生捕获会完整检测 `content="drawable"` 和绘制 API;旧实验 API 使用兼容捕获。可通过内容节点的 `data-pano-spatial-state`(`loading`、`ready`、`error`)观察捕获状态。
|
|
631
|
+
|
|
632
|
+
`PanoOverlay` / `renderOverlay` 内的屏幕平面 UI 在 VR 中隐藏且不可聚焦,退出后恢复。Portal 容器保持挂载和可测量,以便已注册的弹窗继续渲染为空间面板。内置 VR 会话控制仍可使用。
|
|
633
|
+
|
|
634
|
+
## VR 图标控件与滚动
|
|
635
|
+
|
|
636
|
+
这些新增控件只在 VR 模式显示;退出后隐藏且不可聚焦。普通模式继续使用视频原生控件、鼠标模型交互和原生滚动。无需改变项目 JSON。
|
|
637
|
+
|
|
638
|
+
主入口导出 `PanoMediaControls`、`PanoModelControls`、`PanoScrollControls`、`PanoControlGroup`、`PanoControlButton`、`PanoSpatialControls`、`usePanoMediaController`,以及 `PanoMediaController`、`PanoControlAction` 类型。导入 `@ericchen1990/pano-view/styles.css`。
|
|
639
|
+
|
|
640
|
+
```tsx
|
|
641
|
+
const mediaRef = useRef<HTMLVideoElement>(null);
|
|
642
|
+
const controller = usePanoMediaController(mediaRef);
|
|
643
|
+
// 在 PanoDialog 内;同一个 video 保留原生控件供普通模式使用。
|
|
644
|
+
<video ref={mediaRef} src="/movie.mp4" controls playsInline />
|
|
645
|
+
<PanoMediaControls controller={controller} />
|
|
646
|
+
```
|
|
647
|
+
|
|
648
|
+
场景内按钮组放在 `PanoOverlay` 内,使用 `PanoSpatialControls` 将 DOM 内容呈现为空间面板:
|
|
649
|
+
|
|
650
|
+
```tsx
|
|
651
|
+
<PanoOverlay>
|
|
652
|
+
<PanoSpatialControls position={{ yaw: 55, pitch: -14 }} width={28} distance={10}>
|
|
653
|
+
<PanoMediaControls controller={controller} />
|
|
654
|
+
</PanoSpatialControls>
|
|
655
|
+
</PanoOverlay>
|
|
656
|
+
```
|
|
657
|
+
|
|
658
|
+
`position` 使用与热点相同的 yaw/pitch 度数;`width` 是角宽(默认 28°),`distance` 默认 2。省略 position 时,面板出现在进入 VR 时的视线下方并保持位置。面板应位于内容旁边,避免遮挡视频。360° 视频可通过 `usePanoViewer()` 的 `getVideo` / `subscribeVideo` 获取 controller;订阅后用同一组件。`VideoHotspot` 的 `ref` 返回 controller,配合 `onPlaybackStateChange` 同步宿主的 `playing` 状态。关闭、切源和卸载仍遵循媒体本身的生命周期。
|
|
659
|
+
|
|
660
|
+
`PanoModelControls` 接收已有 `adapter`,支持 `rotateSpeed`(默认每秒 30°)、`zoomSpeed`(默认每秒 1.5 倍);不会加载模型或接管宿主视角状态。宿主通过 `canExecute` 限制角度和缩放范围。
|
|
661
|
+
|
|
662
|
+
默认 VR 弹窗自动提供半透明滚动图标,使用双箭头,贴在可滚动区域的上下边缘,并可与模型操作同时存在。仅显示仍可滚动的方向;没有溢出时隐藏。默认不透明度为 0.3,凝视或悬停时提升至 0.9。默认弹窗绑定正文;嵌套内容可自动选择最近滚动区域。第三方弹窗可向 `usePanoDialogSurface({ open, onOpenChange, scrollTargetRef })` 传入滚动容器 ref;组合弹窗可传给 `PanoDialogRoot`。显式绑定优先于自动选择。也可自行添加 `<PanoScrollControls targetRef={scrollRef} speed={0.2} />`,组件会将按钮叠加到目标滚动区域边缘。跨域 iframe 的内部页面无法访问,仍需退出 VR 操作。
|
|
663
|
+
|
|
664
|
+
所有操作组支持 `actions` 数组:内置动作 ID 与自定义动作可混合排序。媒体 ID 为 `play/pause/rewind/forward/vol-/vol+/mute/unmute`,模型 ID 为 `left/right/up/down/in/out/reset`,滚动 ID 为 `up/down/left/right`。播放与静音 ID 随状态变化;筛选时同时包含两种状态。
|
|
665
|
+
|
|
666
|
+
```tsx
|
|
667
|
+
<PanoMediaControls controller={controller} actions={["rewind", "play", "pause", "forward"]}>
|
|
668
|
+
{actions => <div className="my-layout">{actions.map(action =>
|
|
669
|
+
<PanoControlButton key={action.id === "pause" ? "play" : action.id} action={action} />
|
|
670
|
+
)}</div>}
|
|
671
|
+
</PanoMediaControls>
|
|
672
|
+
```
|
|
673
|
+
|
|
674
|
+
自定义动作包含 `id`、无障碍 `label`、可选 React `icon`、`disabled` / `enabled()`、单次 `activate()`、连续 `update(seconds)` 和停止 `end()`;可选 `repeatIntervalMs` 将连续动作改为间隔执行。使用 `PanoControlButton` 保留凝视绑定;切换播放/静音图标时保持按钮 key 稳定,防止重新计时。`PanoControlGroup` 可直接渲染自定义动作数组。
|
|
675
|
+
|
|
676
|
+
可通过 `className`、`style` 和 `--pano-control-background/color/border/radius/size/gap/padding/focus` CSS 变量定制显式按钮组。自动视频按钮组也支持这些变量;其他自动操作区支持 background 变量。完整布局或图标替换需显式按钮组。同一目标的显式媒体、模型或滚动组会替代自动操作组。
|
|
677
|
+
|
|
678
|
+
自动接管的每个弹窗视频会在视频下方显示独立的圆形按钮控制栏,顺序为后退、播放/暂停、前进、静音/取消静音,以及平台支持时的减音量、加音量。控制栏在布局中独立占位,不遮挡画面或后续正文,随视频一起滚动并遵守所在滚动容器的裁剪。媒体按钮默认大小为 48px,浅蓝色线框图标为 24px,间距和内边距均为 8px;窄区域中按钮可换行。这些默认值同时适用于自动视频控制栏和显式 `PanoMediaControls`(包括音频),仍可使用现有控件 CSS 变量覆盖按钮大小、间距和内边距。退出 VR 时移除自动控件占位,原视频节点和普通模式原生控件保持不变。播放前优先显示封面;没有封面时临时提示 `preload="auto"` 尝试获取首帧,不播放、不跳转、不重新加载,也不创建第二个视频元素。没有可读取画面时显示明确的视频占位。封面需要同源或允许 CORS;不可读取的媒体不会污染面板纹理。关闭弹窗或退出 VR 时释放预览资源并恢复临时预载设置。
|
|
679
|
+
|
|
680
|
+
凝视完成 `cursorDwellMs` 后,播放、静音、复位执行一次;跳转、音量和模型持续执行;滚动每隔 700 毫秒执行一小步,移开即停。默认跳转每秒 10 秒(`seekSpeed`),音量每秒 0.25(`volumeSpeed`),滚动每秒 0.2 个可见区域(`speed`),每步默认移动 0.14 个可见区域。`PanoScrollControls.repeatIntervalMs` 可调整重复间隔;间隔计时不受组件重新渲染影响,移开会清零,重新凝视需重新完成 dwell。关闭、退出 VR、禁用和失焦会停止操作。凝视不能替代浏览器要求的可信用户手势;音量不可调整的平台(包括 iOS)隐藏音量加减按钮,仍可静音;支持调节的平台在音量上下限禁用对应按钮。
|
|
681
|
+
|
|
682
|
+
通过 `--pano-scroll-opacity`、`--pano-scroll-active-opacity`、`--pano-scroll-background` 和 `--pano-scroll-color` 定制滚动按钮。
|
|
683
|
+
|
|
684
|
+
在 `/overlays` 比较弹窗接入方式,在 `/spatial-controls` 分别查看场景视频、360° 视频、模型及滚动控件,在 `/immersive-tour` 验证完整组合。`/video` 聚焦普通 360° 视频播放、画质与字幕。
|
|
685
|
+
|
|
686
|
+
### VR 输入方式与控件适配
|
|
687
|
+
|
|
688
|
+
`WebVR.interactionMode` 默认为 `"auto"`:手机分屏与桌面模拟沿用凝视;WebXR 使用射线配合扳机、捏合或系统选择事件,不执行停留点击。可设为 `"gaze"` 或 `"pointer"` 固定策略。`getVRState()` / `subscribeVR()` 的可选 `input` 快照提供当前 `mode`(`gaze/pointer/paused/null`)与 `source`(`head/controller/hand/system/null`);原有呈现模式不变。
|
|
689
|
+
|
|
690
|
+
手柄指向模型后按住扳机拖动可旋转模型,上下推摇杆可缩放;指向正文时摇杆滚动最近的可滚动区域,显式滚动目标优先。模型缩放优先于外层滚动。摇杆死区为 0.2,滚动最大每秒一屏,缩放默认每秒 1.5 倍。只有具备可用替代能力时,默认模型与滚动控件才隐藏对应方向按钮。复位、媒体、关闭和场景导航保留;显式 `actions` 和自定义渲染布局不会被自动过滤。
|
|
691
|
+
|
|
692
|
+
使用过的手柄断开或丢失跟踪后暂停操作,不自动恢复凝视。默认 WebXR 会话工具栏提供退出和主动启用凝视的入口;等待输入或暂停时,只有启用凝视入口接受头部停留选择。宿主关闭 `cursor` 或自动凝视、固定输入策略、关闭 `chrome` 时,界面尊重宿主设置。恢复手柄的第一次明确选择只切换输入,不点击内容。系统瞬时输入不会因暂时消失而触发凝视回退。基础手势保留方向按钮;不提供双手缩放、近距离触碰或移动面板。
|
|
693
|
+
|
|
694
|
+
Immersive Web Emulator 接管画布时,模拟预览会铺满浏览器内容区域,使用与模拟器相同的显示尺寸及宽高比,并随窗口变化更新。不会调用浏览器 Fullscreen API。退出后恢复原 viewer 的画布样式;真实 WebXR 头显、手机 VR 和桌面非 XR 模式不受此适配影响。
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ericchen1990/pano-view",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "5.0.0",
|
|
4
4
|
"description": "React components for panoramic viewing experiences.",
|
|
5
5
|
"author": "Eric Chen",
|
|
6
6
|
"license": "MIT",
|
|
@@ -64,7 +64,8 @@
|
|
|
64
64
|
"dependencies": {
|
|
65
65
|
"@radix-ui/react-dialog": "^1.3.1",
|
|
66
66
|
"@types/three": "^0.185.0",
|
|
67
|
-
"howler": "^2.2.4"
|
|
67
|
+
"howler": "^2.2.4",
|
|
68
|
+
"modern-screenshot": "4.7.0"
|
|
68
69
|
},
|
|
69
70
|
"peerDependencies": {
|
|
70
71
|
"@react-three/drei": "^10.0.0",
|