@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 CHANGED
@@ -1692,10 +1692,28 @@ via `label`. When collapsible, `defaultExpanded` defaults to `true`.
1692
1692
 
1693
1693
  ### Appearance and CSS hooks
1694
1694
 
1695
- Pass `appearance` for common chrome tokens without writing CSS. The active-item
1696
- ring sits above the thumbnail and is controlled by `selectionColor` /
1697
- `selectionWidth` (`selectionWidth: 0` hides it). Scene titles default to start
1698
- alignment; set `labelAlign: "center"` or `"end"` to change that.
1695
+ Pass `appearance` for common chrome tokens without writing CSS. The border sits
1696
+ inside the thumbnail: `selectionColor` defaults to white for the current scene;
1697
+ `focusColor` defaults to `"#7dd3fc"` for mouse hover and keyboard focus, taking
1698
+ priority over selection. All three states share `selectionWidth` (default 2px).
1699
+ `selectionWidth: 0` hides these borders while retaining the independent keyboard
1700
+ focus outline. Scene titles default to start alignment; set `labelAlign: "center"`
1701
+ or `"end"` to change that.
1702
+
1703
+ Set `showVisited` (default `false`) to show a dark circular check icon in the
1704
+ thumbnail's top-right corner, with no visible text. `visitedLabel` (default
1705
+ `"Visited"`) customizes only the accessible description. A scene is recorded when
1706
+ `Scenes` / `ProjectScenes` first reports it paintable, including the initial and
1707
+ current scene; clicks, preloads and failed loads do not count. Both menu types in
1708
+ the same `PanoViewer` share this session history, including visits made before
1709
+ turning the option on. Collapsing, hiding or remounting a menu and entering/exiting
1710
+ VR retain the history. Refreshing or remounting the viewer clears it. Standalone
1711
+ menus without a scene-ready source do not generate visits; `activeId` alone does
1712
+ not record one.
1713
+
1714
+ ```tsx
1715
+ <PanoSceneMenu items={menuItems} activeId={activeSceneId} onSelect={setActiveSceneId} showVisited />
1716
+ ```
1699
1717
 
1700
1718
  ```tsx
1701
1719
  <PanoSceneMenu
@@ -1732,7 +1750,8 @@ use `className` / `style` against these stable classes:
1732
1750
 
1733
1751
  The root also exposes `data-placement`, `data-align`, `data-orientation`
1734
1752
  (`horizontal` | `vertical`), `data-expanded`, and `data-collapsible`. The active
1735
- item has `data-active="true"`.
1753
+ item has `data-active="true"`; a visited item has `data-visited="true"` when
1754
+ `showVisited` is enabled. The badge uses `pano-scene-menu-visited`.
1736
1755
 
1737
1756
  When `collapsible` is set, `toggle` replaces the edge-tab icon or restyles that
1738
1757
  button. Omit `toggle` to keep the default chevron. Pass an element (`<Icon />`)
@@ -1999,6 +2018,9 @@ These are props on `Gyro` itself. `PanoEvents` `onGyro*` callbacks subscribe to
1999
2018
 
2000
2019
  ## WebVR
2001
2020
 
2021
+ VR settings use independent v2 storage for simulated desktop and MobileVR; legacy v1 values are ignored. Reset affects the active fallback mode, or both outside VR. The neutral `none` preset uses 80° vertical FOV, 60 mm virtual eye spacing and centered optics regardless of physical screen size. Only MobileVR auto screen size queries DPDB (5-second timeout); ambiguous matches require manual calibration. Phone detection never selects a lens preset. Explicit props take precedence and their controls are read-only in Setup.
2022
+
2023
+
2002
2024
  `WebVR` adds immersive viewing to the nearest `PanoViewer`. It prefers an
2003
2025
  `immersive-vr` WebXR session on supported headsets. On phones without WebXR it
2004
2026
  uses a stereoscopic split-screen view with device orientation, and on desktop
@@ -2052,8 +2074,12 @@ center reticle shows a crosshair; looking at an interactive hotspot draws a
2052
2074
  clockwise ring that fills over `cursorDwellMs` (default 1500), then the hotspot
2053
2075
  click handler runs. Set `cursorDwellMs={0}` to keep the reticle without
2054
2076
  auto-click. Simulated desktop VR (`fake` mode) requests pointer lock by default
2055
- (`mousePointerLock`) so the cursor hides and mouse movement looks around; click
2056
- the view again if the browser releases the lock. Fallback sessions keep the
2077
+ (`mousePointerLock`) so the cursor hides and mouse movement looks around. Right-click
2078
+ to release the mouse without leaving VR; left-click the view to lock it again.
2079
+ While unlocked, the reticle is hidden and gaze actions stop; relocking starts
2080
+ a fresh dwell. Desktop crosshair and ring sizes are 60% of the headset size.
2081
+ Browser Escape behavior is preserved. If locking is denied or unsupported,
2082
+ VR stays available with a normal cursor; a later left-click can retry. Fallback sessions keep the
2057
2083
  screen awake (`wakelock`) with the Screen Wake Lock API, or a looping hidden
2058
2084
  video on browsers that lack it.
2059
2085
 
@@ -2066,13 +2092,13 @@ interactive through the default WebXR controller rays.
2066
2092
 
2067
2093
  ### Props
2068
2094
 
2069
- - `chrome` — default `true` mounts session Exit / Setup UI (no Enter VR button). An appearance object may set `background` (default `rgba(16, 24, 32, 0.78)`), `color` (`#ffffff`), `accent` (`#7dd3fc`), `borderRadius` (`6`), `fontSize` (`14`), and optional `setupTitle`. Session buttons fade after ~3s idle and return on pointer / wheel / keyboard interaction with the viewer.
2095
+ - `chrome` — default `true` mounts session Exit / Setup UI (no Enter VR button). An appearance object may set `background` (default `rgba(16, 24, 32, 0.78)`), `color` (`#ffffff`), `accent` (`#7dd3fc`), `borderRadius` (`6`), `fontSize` (`14`), and optional `setupTitle`. Session buttons fade after ~3s idle while locked. Simulated VR keeps them visible while the mouse is unlocked; hovering, keyboard focus, and an open Setup panel also keep them visible.
2070
2096
  - `mobileVr` — default `true`.
2071
2097
  - `desktopSupport` — default `false` (independent of the same-named `Gyro` prop).
2072
2098
  - `fakeSupport` — default `true` for mouse-driven desktop simulation.
2073
2099
  - `screensize` — `"auto"` (factory / omitted storage default) or inches; numbers are clamped to **3–20** with one decimal place.
2074
2100
  - `databaseUrl` — Cardboard DPDB URL for `"auto"` screensize. Default `https://storage.googleapis.com/cardboard-dpdb/dpdb.json`. Pass `""` or `null` to disable the online lookup.
2075
- - `profile` — omitted values use stored settings; factory default is `"cardboard-v1"`. Also accepts `"none"`, `"cardboard-v2"`, `"gear-vr"`, `"daydream"`, or a custom `{ fov, ipdMm, k1, k2, screenToLensMm?, trayDistanceMm?, label?, id? }`. `trayDistanceMm` is millimetres from the headset tray bottom to the lens center; `0` / omitted keeps the vertical optical axis at screen center.
2101
+ - `profile` — omitted values use stored settings; factory default is `"none"`. Also accepts `"none"`, `"cardboard-v2"`, `"gear-vr"`, `"daydream"`, or a custom `{ fov, ipdMm, k1, k2, screenToLensMm?, trayDistanceMm?, label?, id? }`. `trayDistanceMm` is millimetres from the headset tray bottom to the lens center; `0` / omitted keeps the vertical optical axis at screen center.
2076
2102
  - `absolute` — default `false`. Compass-oriented MobileVR heading (no effect in WebXR).
2077
2103
  - `north` — default `0`. Panorama yaw that represents North when `absolute` is enabled.
2078
2104
  - `friction` — default `0` (range `0–0.99`). MobileVR gyro smoothing; keep `0` for normal viewing.
@@ -2470,6 +2496,8 @@ on screen. Clicking the hotspot does not toggle it. Optional
2470
2496
  clicked. Hover and click triggers need `interactive` and `pointerEvents`
2471
2497
  other than `"none"`.
2472
2498
 
2499
+ `tooltipMode` defaults to `"billboard"` and supports `"surface"`. `tooltipScaleMode` defaults to `"inherit"`, following the hotspot’s effective zoom behavior. Floating tooltips also support `"fov"` and `"fixed"`; surface tooltips always scale with FOV. Styling is measured at the hotspot’s `referenceFov` (default `75`), while the gap remains in screen pixels. Polygon and polyline geometry always follows the sphere, without floating, distance or scale-mode controls; their tooltips remain independently configurable and default to floating with FOV scaling. Omitted settings in existing projects use these new defaults too.
2500
+
2473
2501
  `tooltipPlacement` is `"top"` (default), `"bottom"`, `"left"`, or `"right"`.
2474
2502
  These are screen directions: the bubble sits outside the hotspot's projected
2475
2503
  screen bounds on that edge, so rotated graphics such as arrows are not covered.
@@ -2645,7 +2673,7 @@ Point hotspots use `mode` to configure orientation and placement together:
2645
2673
  decrease it to move the hotspot nearer.
2646
2674
  The built-in panorama shell is at roughly `1000` units; distance is
2647
2675
  automatically capped so the hotspot's corners cannot cross it.
2648
- - `scaleMode="fov"` (default) lets a hotspot grow when the user zooms in.
2676
+ - Floating hotspots default to `scaleMode="fixed"`; choose `"fov"` to scale with zoom. Surface hotspots always use `"fov"`, even when `"fixed"` was saved.
2649
2677
  `scaleMode="fixed"` compensates for FOV changes and keeps its screen size
2650
2678
  close to the size at `referenceFov` (default `75`).
2651
2679
  - `rotation` rotates the hotspot around its own normal in degrees; positive
@@ -2787,11 +2815,10 @@ handler, `onEnded`, or playback error.
2787
2815
  ## VideoHotspot
2788
2816
 
2789
2817
  `VideoHotspot` uses an `HTMLVideoElement` and `VideoTexture`. Its `playing`
2790
- prop is likewise controlled: toggle it from your click handler or application
2791
- state. On source change or unmount, the element is paused and its texture is
2818
+ prop is likewise controlled: initialize it to `false` for manual playback, then toggle it from your click handler or application state. On source change or unmount, the element is paused and its texture is
2792
2819
  released. `playsInline` defaults to `true` and uses the same inline-playback
2793
2820
  attributes as `PanoVideo`. The element is mounted inside the viewer, hidden,
2794
- so mobile browsers keep it in the page. `muted` defaults to `true`, `loop` to
2821
+ so mobile browsers keep it in the page. `muted` defaults to `false`, `loop` to
2795
2822
  `false`, `preload` to `"metadata"` (also `"none"` | `"auto"`), and `volume` to
2796
2823
  `1` (0–1). Omitted size defaults to `16×9`. Cross-origin media may set
2797
2824
  `crossOrigin` (`""` | `"anonymous"` | `"use-credentials"`).
@@ -2809,7 +2836,6 @@ import { VideoHotspot } from "@ericchen1990/pano-view";
2809
2836
  src="/hotspots/room.webm"
2810
2837
  poster="/hotspots/room-poster.webp"
2811
2838
  playing={isClipPlaying}
2812
- muted
2813
2839
  volume={0.8}
2814
2840
  onClick={() => setClipPlaying((playing) => !playing)}
2815
2841
  onEnded={() => setClipPlaying(false)}
@@ -2818,7 +2844,7 @@ import { VideoHotspot } from "@ericchen1990/pano-view";
2818
2844
  />;
2819
2845
  ```
2820
2846
 
2821
- Browsers can reject unmuted or otherwise non-gesture playback. In that case
2847
+ Loading or entering VR does not start playback. Readiness events and seeking preserve controller-set mute and volume; changing the corresponding props or source applies the new settings. Pass `muted` explicitly when you want silent playback. Browsers can reject unmuted or otherwise non-gesture playback. In that case
2822
2848
  `onPlaybackStateChange` receives `"blocked"` and `onPlaybackError` receives
2823
2849
  the browser error; retain control of `playing` in the host and offer an
2824
2850
  explicit user action.
@@ -3178,3 +3204,56 @@ Legacy `action` remains a click fallback; `events.click` wins. Editing or cleari
3178
3204
  `ProjectScenes.onHotspotEvent(event)` receives `trigger`, `sceneId`, `hotspot`, `interaction` and `preventDefault()`. On clicks it runs before the existing `onHotspotClick`; either callback can cancel the action. `onCustomAction(event)` additionally receives `action` and may return a Promise. Dispatch by the stable `action.actionId`, validate business parameters and throw for unknown actions. Missing handlers, synchronous exceptions and rejected Promises reach `onHotspotError` with `trigger`. Requests, authentication, cancellation, duplicate prevention and response ordering are host responsibilities; events dispatch independently without automatic retries.
3179
3205
 
3180
3206
  Hover runs once per logical hotspot boundary, skips touch and keyboard focus, and respects visibility and pointer interaction settings. Scene changes, hiding and unmounting clear hover state without executing leave actions. Existing tooltip behavior is independent. Custom renderers must forward `context.onHoverChange(hovered, interaction)` as well as `context.onClick`; `context.renderDefault()` wires both automatically.
3207
+
3208
+ ## VR control groups
3209
+
3210
+ Customizable VR-only icon groups support media, models and scrolling. See the [VR controls guide](./docs/dialogs.md#vr-icon-controls-and-scrolling) for APIs and examples.
3211
+
3212
+ ## WebVRSceneMenu
3213
+
3214
+ Mount beside `WebVR` and `Scenes` inside `PanoViewer`. This native spatial menu appears in WebXR, MobileVR and desktop simulated VR. Previous/next buttons surround a circular scene-menu button below the horizon. Opening it reveals a curved thumbnail grid with pagination, gaze/pointer controls and accessible keyboard buttons. The active scene has a border and check mark; reopening locates its page.
3215
+
3216
+ ```tsx
3217
+ <WebVRSceneMenu
3218
+ items={[
3219
+ { id: "hall", thumbnailUrl: "/hall.webp", title: "Hall" },
3220
+ { id: "garden", thumbnailUrl: "/garden.webp", title: "Garden" },
3221
+ ]}
3222
+ activeId={activeSceneId}
3223
+ onSelect={setActiveSceneId}
3224
+ columns={4}
3225
+ rows={3}
3226
+ appearance={{ labelPlacement: "center", selectionColor: "#ffffff" }}
3227
+ />
3228
+ ```
3229
+
3230
+ Items reuse `PanoSceneMenuItem`. Titles default to centered image overlays, wrap to two lines and truncate with an ellipsis. Empty titles fall back to IDs; accessible names retain the full title. Thumbnail sources must permit cross-origin image access. Cards remain selectable when images fail.
3231
+
3232
+ | Prop | Default / range |
3233
+ | --- | --- |
3234
+ | `layout` | `"compact"` (default, original curved grid), `"flat"` (flat wall), or `"arc"` (independent flat cards around the viewer) |
3235
+ | `displayMode` | `"paged"` (default) or `"all"`; all mode grows rows, ignores `rows`, and hides paging controls |
3236
+ | `arcAngle` | 180°; clamped to 60–360°, used only by arc layout |
3237
+ | `columns`, `rows` | compact/flat: 4 columns; arc: automatic (8 at 180°, 16 at 360°). Column ranges: compact 2–4, flat/arc 2–24. Paged rows: 3, range 1–3 |
3238
+ | `loop` | `true`; wraps scene navigation, never pagination |
3239
+ | `closeOnSelect` | `true`; use `false` for continuous browsing |
3240
+ | `follow` | `true`; navigation follows horizontal heading, the open grid remains anchored |
3241
+ | `navigationPitch` | −25 degrees; −40 to −15 |
3242
+ | `distance`, `scale` | 2 meters, 1; ranges 1.5–3, 0.75–1.25 |
3243
+ | `fadeDurationMs` | 200; 0–600ms, respects reduced motion |
3244
+ | `showTitles` | `true` |
3245
+ | `showVisited`, `visitedLabel` | `false`, `"Visited"`; icon-only visit badge and its accessible description |
3246
+
3247
+ Dwell timing inherits `WebVR.cursorDwellMs`. `activeId` owns selection; `onSelect` requests navigation from the host, which retains scene loading/error handling. Dialogs and VR Setup temporarily hide the menu. Empty lists render nothing; a single scene disables previous/next navigation.
3248
+
3249
+ The bounded `appearance` interface includes `controlColor` (white), `controlBackground` (`transparent`), `controlOpacity` (0.75, range 0–1), `selectionColor` (white), `focusColor` (`#7dd3fc`), `selectionWidth` (4, range 0–8), `borderRadius` (8, range 0–48), `labelColor` (white), `labelBackground` (`transparent`), `fontSize` (28, range 16–48), and `labelPlacement` (`"center"` or `"bottom"`). Scene outlines meet the thumbnail edge from the outside. Selected, gaze and keyboard-focus borders share `selectionWidth`; zero hides them while retaining a separate keyboard-focus indicator. The optional visited check icon is independent of selection and remains visible at zero border width. The current scene uses `selectionColor`; gaze and keyboard focus use `focusColor`, taking priority on the current scene. Sizes use reference-canvas pixels and follow the overall scale. Exports include `WebVRSceneMenuProps`, `WebVRSceneMenuAppearance`, and `DEFAULT_WEBVR_SCENE_MENU_APPEARANCE`.
3250
+
3251
+ `showVisited` and `visitedLabel` follow the same session and scene-ready rules as `PanoSceneMenu` above. The badge remains visible when `showTitles={false}` or a thumbnail fails to load. Dialogs, VR Setup, pagination and VR re-entry preserve the shared viewer history.
3252
+
3253
+ The `/webvr` example teaches VR entry, exit, capability events and gaze activation. `/vr-scene-menu` includes behavior, layout and appearance controls, editable scene titles, a 25-scene pagination fixture, reset, and a regular scene menu for checking shared visit history across VR entry/exit. `/spatial-controls` teaches media, model and scrolling targets; `/immersive-tour` combines them. Settings live in page state only.
3254
+
3255
+ The floating navigation is a compact button group with equal-height icons, transparent backgrounds and subtle shadows for contrast against the panorama.
3256
+
3257
+ Flat and arc layouts use bounded texture atlases. Compact/flat default to four columns; arc defaults to one column per 22.5° (8 for a semicircle, 16 for a full circle). Compact supports 2–4 columns; new layouts support 2–24. Paged rows default to 3, clamped to 1–3. All mode extends the wall without shrinking cards, so large lists require more looking around. Opening centers the menu on the current heading; it then follows translation but not head rotation. New layouts keep close/paging controls in a separate forward control strip. The `/vr-scene-menu` playground exposes layout, display mode, and arc angle; leave columns blank for automatic defaults.
3258
+
3259
+ VR scene titles default to a transparent background with a subtle dark text shadow. Menu textures use 2× raster resolution, mipmaps and anisotropic filtering while layout and hit areas remain unchanged.
package/README.zh-CN.md CHANGED
@@ -1540,7 +1540,13 @@ export function Tour() {
1540
1540
 
1541
1541
  ### 外观与样式钩子
1542
1542
 
1543
- 常用外观可用 `appearance`,不必手写 CSS。当前项边框叠在缩略图上方,可用 `selectionColor` / `selectionWidth` 调节(`selectionWidth: 0` 隐藏);场景名默认左对齐,设 `labelAlign: "center"` 或 `"end"` 可改对齐。
1543
+ 常用外观可用 `appearance`,不必手写 CSS。场景边框位于缩略图内侧:当前场景使用 `selectionColor`(默认白色),鼠标悬停和键盘焦点使用 `focusColor`(默认 `"#7dd3fc"`),交互颜色优先于选中颜色。三种状态共用 `selectionWidth`(默认 2px);设为 `0` 隐藏这些边框,但保留独立的键盘焦点提示。场景名默认左对齐,设 `labelAlign: "center"` 或 `"end"` 可改对齐。
1544
+
1545
+ 设置 `showVisited`(默认 `false`)后,已浏览场景的缩略图右上角显示深色圆底的勾选图标,不增加可见文字。`visitedLabel`(默认 `"Visited"`)只用于无障碍描述。`Scenes` / `ProjectScenes` 在场景首次可显示时记录,包括首个场景和当前场景;点击、预加载及加载失败不计入。同一 `PanoViewer` 内的普通和 VR 菜单共享本次会话记录,也包含启用开关前浏览过的场景。收起、隐藏、重新挂载菜单或进出 VR 均保留记录;刷新或重新挂载查看器后清空。独立菜单没有场景就绪来源时不会产生记录,单独改变 `activeId` 不会标记已浏览。
1546
+
1547
+ ```tsx
1548
+ <PanoSceneMenu items={menuItems} activeId={activeSceneId} onSelect={setActiveSceneId} showVisited visitedLabel="已浏览" />
1549
+ ```
1544
1550
 
1545
1551
  ```tsx
1546
1552
  <PanoSceneMenu
@@ -1573,7 +1579,7 @@ export function Tour() {
1573
1579
  - `pano-scene-menu-item` / `pano-scene-menu-thumb` / `pano-scene-menu-label`
1574
1580
  - `pano-scene-menu-control` / `pano-scene-menu-arrow` / `pano-scene-menu-toggle`
1575
1581
 
1576
- 根节点还带 `data-placement`、`data-align`、`data-orientation`(`horizontal` | `vertical`)、`data-expanded`、`data-collapsible`;当前项带 `data-active="true"`。
1582
+ 根节点还带 `data-placement`、`data-align`、`data-orientation`(`horizontal` | `vertical`)、`data-expanded`、`data-collapsible`;当前项带 `data-active="true"`。启用 `showVisited` 时,已浏览项带 `data-visited="true"`,图标类名为 `pano-scene-menu-visited`。
1577
1583
 
1578
1584
  `collapsible` 时,边缘拉手可用 `toggle` 换图标或改样式。省略 `toggle` 时仍是默认箭头。`icon` 传元素(`<Icon />`),不要传组件类型。图标要随收起状态变化时传函数,参数为 `{ collapsed }`(菜单展开时 `collapsed` 为 `false`)。`className` 追加到按钮上。`style` 覆盖背景、边框、颜色和尺寸;位置和 `transform` 由贴边布局负责,不要在 `style` 里改。默认箭头使用 `currentColor`,只改 `style.color` 就会变色。
1579
1585
 
@@ -1793,6 +1799,9 @@ export function GyroExample() {
1793
1799
 
1794
1800
  ## WebVR
1795
1801
 
1802
+ VR 设置按桌面模拟与手机分屏分别保存到 v2 存储,不继承旧版 v1。Reset 仅重置当前回退模式,未进入 VR 时重置两种模式。默认 `none` 使用每眼垂直 FOV 80°、虚拟眼距 60 mm 和居中光轴,不根据屏幕尺寸校准。只有手机自动尺寸查询 DPDB(5 秒超时),冲突结果需要手动校准;识别手机不会自动选择镜头。显式 props 优先,相关设置项只读。桌面解锁期间顶部按钮常显;锁定后空闲 3 秒淡出,悬停、键盘焦点或设置面板打开时保持显示。
1803
+
1804
+
1796
1805
  `WebVR` 为最近的 `PanoViewer` 增加沉浸式观看。支持的头戴设备优先使用 `immersive-vr` WebXR 会话。无 WebXR 的手机使用设备方向的立体分屏;桌面可提供鼠标驱动的模拟 VR 预览。进入 VR 由宿主通过查看器句柄触发;内置 chrome 仅在会话中显示退出 VR 与 MobileVR 设置(空闲后淡出)。
1797
1806
 
1798
1807
  ```tsx
@@ -1822,7 +1831,7 @@ export function WebVRExample() {
1822
1831
  }
1823
1832
  ```
1824
1833
 
1825
- 句柄暴露 `enterVR()`、`exitVR()`、`toggleVR()`、`isAvailable()`、`isEnabled()`、`getMode()`、`getState()`、`subscribe()`、`requestPermission()` 与 `resetSettings()`。模式为 `"webxr"`、`"mobilevr"`、`"fake"`。`profile` 接受 `"none"`、`"cardboard-v1"`、`"cardboard-v2"`、`"gear-vr"`、`"daydream"`,或自定义 `{ fov, ipdMm, k1, k2, screenToLensMm?, trayDistanceMm? }` 镜头配置。在已知物理屏幕尺寸时,MobileVR 会用瞳距与可选的托盘到镜片高度(`trayDistanceMm`;Cardboard V1 为 `35`)放置每只眼睛的光轴;`0` 或省略则保持垂直方向居中。当 `screensize` 为 `"auto"` 时,MobileVR 会加载 Cardboard Device Parameter Database(`databaseUrl`,默认 Google 公开 DPDB)估算对角线英寸;传 `""` 或 `null` 可跳过请求。无法识别时触发 `onUnknownDevice`,并继续使用镜头预设 FOV(尺寸未知前不做托盘偏移)。MobileVR 设置值会版本化并保存在当前源的 `localStorage` 中;`resetSettings()` 会清除该存储并恢复 props 或内置默认(Setup 面板也有 Reset)。VR 中中心准星显示十字;注视可交互热点时绘制顺时针填充环,经 `cursorDwellMs`(默认 1500)后触发热点点击。设 `cursorDwellMs={0}` 可保留准星但不自动点击。模拟桌面 VR(`fake` 模式)默认请求指针锁定(`mousePointerLock`),隐藏光标并用鼠标环视;若浏览器释放锁定需再次点击视图。回退会话通过 Screen Wake Lock API 或在不支持时的循环隐藏视频保持屏幕常亮(`wakelock`)。
1834
+ 句柄暴露 `enterVR()`、`exitVR()`、`toggleVR()`、`isAvailable()`、`isEnabled()`、`getMode()`、`getState()`、`subscribe()`、`requestPermission()` 与 `resetSettings()`。模式为 `"webxr"`、`"mobilevr"`、`"fake"`。`profile` 接受 `"none"`、`"cardboard-v1"`、`"cardboard-v2"`、`"gear-vr"`、`"daydream"`,或自定义 `{ fov, ipdMm, k1, k2, screenToLensMm?, trayDistanceMm? }` 镜头配置。在已知物理屏幕尺寸时,MobileVR 会用瞳距与可选的托盘到镜片高度(`trayDistanceMm`;Cardboard V1 为 `35`)放置每只眼睛的光轴;`0` 或省略则保持垂直方向居中。当 `screensize` 为 `"auto"` 时,MobileVR 会加载 Cardboard Device Parameter Database(`databaseUrl`,默认 Google 公开 DPDB)估算对角线英寸;传 `""` 或 `null` 可跳过请求。无法识别时触发 `onUnknownDevice`,并继续使用镜头预设 FOV(尺寸未知前不做托盘偏移)。MobileVR 设置值会版本化并保存在当前源的 `localStorage` 中;`resetSettings()` 会清除该存储并恢复 props 或内置默认(Setup 面板也有 Reset)。VR 中中心准星显示十字;注视可交互热点时绘制顺时针填充环,经 `cursorDwellMs`(默认 1500)后触发热点点击。设 `cursorDwellMs={0}` 可保留准星但不自动点击。模拟桌面 VR(`fake` 模式)默认请求指针锁定(`mousePointerLock`),隐藏光标并用鼠标环视;右键释放鼠标但保留 VR,左键点击画面重新锁定(该次点击只恢复锁定)。解锁时隐藏准星并停止注视动作,重新锁定后重新计时。桌面十字及进度环为头显尺寸的 60%。保留浏览器原有 Esc 行为;锁定被拒绝或不支持时保留普通鼠标,可再次左键重试。回退会话通过 Screen Wake Lock API 或在不支持时的循环隐藏视频保持屏幕常亮(`wakelock`)。
1826
1835
 
1827
1836
  WebXR 需要 HTTPS 与用户手势。移动端传感器访问也需要 HTTPS,iOS 可能需显式权限提示。嵌入查看器需要适当的 `xr-spatial-tracking`、`gyroscope`、`accelerometer` 权限。iframe 热点、tooltip、视频控件等 DOM 叠加层在沉浸式头戴会话中不可见;基于 mesh 的热点仍可通过默认 WebXR 控制器射线交互。
1828
1837
 
@@ -1834,7 +1843,7 @@ WebXR 需要 HTTPS 与用户手势。移动端传感器访问也需要 HTTPS,i
1834
1843
  - `fakeSupport` — 默认 `true`,允许鼠标驱动的桌面模拟 VR。
1835
1844
  - `screensize` — `"auto"`(出厂 / 省略时的存储默认)或英寸数;数字会被夹到 **3–20** 并保留一位小数。
1836
1845
  - `databaseUrl` — `"auto"` 屏幕尺寸时使用的 Cardboard DPDB URL。默认 `https://storage.googleapis.com/cardboard-dpdb/dpdb.json`。传 `""` 或 `null` 可关闭在线查询。
1837
- - `profile` — 省略时用已存设置,出厂为 `"cardboard-v1"`。也可传 `"none"`、`"cardboard-v2"`、`"gear-vr"`、`"daydream"`,或自定义 `{ fov, ipdMm, k1, k2, screenToLensMm?, trayDistanceMm?, label?, id? }`。`trayDistanceMm` 为头显托盘底边到镜片中心的毫米距离;`0` / 省略时垂直光轴保持屏幕中心。
1846
+ - `profile` — 省略时用已存设置,出厂为 `"none"`。也可传 `"none"`、`"cardboard-v2"`、`"gear-vr"`、`"daydream"`,或自定义 `{ fov, ipdMm, k1, k2, screenToLensMm?, trayDistanceMm?, label?, id? }`。`trayDistanceMm` 为头显托盘底边到镜片中心的毫米距离;`0` / 省略时垂直光轴保持屏幕中心。
1838
1847
  - `absolute` — 默认 `false`。MobileVR 使用罗盘绝对朝向(对 WebXR 无效)。
1839
1848
  - `north` — 默认 `0`。启用 `absolute` 时,全景中代表北方的 yaw。
1840
1849
  - `friction` — 默认 `0`(范围 `0–0.99`)。MobileVR 陀螺仪平滑;正常观看保持 `0`。
@@ -2164,6 +2173,8 @@ import {
2164
2173
 
2165
2174
  `tooltipPlacement` 为 `"top"`(默认)、`"bottom"`、`"left"`、`"right"`。均为屏幕方向:气泡位于热点投影边界该侧之外,避免遮挡旋转后的图形(如箭头)。`tooltipOffset` 为屏幕空间间距(CSS 像素,默认 `12` / `DEFAULT_HOTSPOT_TOOLTIP_OFFSET`)。
2166
2175
 
2176
+ `tooltipMode` 默认 `"billboard"`(悬浮),可设为 `"surface"`(贴合球面)。`tooltipScaleMode` 默认 `"inherit"`,跟随热点实际缩放规则;悬浮提示可独立设为 `"fov"` 或 `"fixed"`,贴合提示始终随 FOV 缩放。气泡字号和样式以热点 `referenceFov`(默认 `75`)为基准,间距继续使用屏幕像素。多边形、折线本体始终贴合球面,不支持悬浮、距离或缩放模式;其提示默认悬浮并随 FOV 缩放,也支持独立配置。新旧项目省略配置均采用这些默认值。
2177
+
2167
2178
  可选 `tooltipAppearance` 覆盖气泡绘制。未设字段保留库默认主题(`DEFAULT_HOTSPOT_TOOLTIP_APPEARANCE`):背景、文字色、边框、圆角、阴影、内边距与字号。
2168
2179
 
2169
2180
  ```tsx
@@ -2300,7 +2311,7 @@ import { GraphicHotspot } from "@ericchen1990/pano-view";
2300
2311
 
2301
2312
  - `mode="surface"` 将平面附着于局部全景表面。
2302
2313
  - `mode="billboard"` 使平面始终朝向相机并浮于壳层前方。`distance` 是热点到相机的距离(世界单位,默认 `10`),减小该值可将热点移近相机。内置全景壳层约在 `1000` 单位;距离会自动限制在允许范围内,避免热点角点穿过壳层。
2303
- - `scaleMode="fov"`(默认)在用户放大时热点变大。`scaleMode="fixed"` 补偿 FOV 变化,屏幕尺寸接近 `referenceFov`(默认 `75`)时的大小。
2314
+ - 悬浮热点默认 `scaleMode="fixed"`,保持固定屏幕大小;可用 `scaleMode="fov"` 随视角缩放。贴合热点始终使用 `fov`,显式的 `fixed` 不生效。`scaleMode="fixed"` 补偿 FOV 变化,屏幕尺寸接近 `referenceFov`(默认 `75`)时的大小。
2304
2315
  - `rotation` 绕自身法线旋转(度);正值顺时针,负值逆时针。`scale` 为角向 width/height 的整体正倍率,默认 `1`。
2305
2316
 
2306
2317
  ```tsx
@@ -2405,7 +2416,7 @@ import { SequenceHotspot } from "@ericchen1990/pano-view";
2405
2416
 
2406
2417
  ## VideoHotspot
2407
2418
 
2408
- `VideoHotspot` 使用 `HTMLVideoElement` 与 `VideoTexture`。其 `playing` prop 同样受控:在点击处理或应用状态中切换。源变更或卸载时元素暂停并释放纹理。`playsInline` 默认 `true`,并使用与 `PanoVideo` 相同的内联播放属性。元素会隐藏挂在查看器内,以便移动端浏览器把它留在页面中。`muted` 默认 `true`,`loop` 默认 `false`,`preload` 默认 `"metadata"`(另可 `"none"` | `"auto"`),`volume` 默认 `1`(0–1)。省略尺寸时默认 `16×9`。非同源资源可设 `crossOrigin`(`""` | `"anonymous"` | `"use-credentials"`)。
2419
+ `VideoHotspot` 使用 `HTMLVideoElement` 与 `VideoTexture`。其 `playing` prop 同样受控:手动播放时初始化为 `false`,在点击处理或应用状态中切换。源变更或卸载时元素暂停并释放纹理。`playsInline` 默认 `true`,并使用与 `PanoVideo` 相同的内联播放属性。元素会隐藏挂在查看器内,以便移动端浏览器把它留在页面中。`muted` 默认 `false`,`loop` 默认 `false`,`preload` 默认 `"metadata"`(另可 `"none"` | `"auto"`),`volume` 默认 `1`(0–1)。省略尺寸时默认 `16×9`。非同源资源可设 `crossOrigin`(`""` | `"anonymous"` | `"use-credentials"`)。
2409
2420
 
2410
2421
  ```tsx
2411
2422
  import { VideoHotspot } from "@ericchen1990/pano-view";
@@ -2420,7 +2431,6 @@ import { VideoHotspot } from "@ericchen1990/pano-view";
2420
2431
  src="/hotspots/room.webm"
2421
2432
  poster="/hotspots/room-poster.webp"
2422
2433
  playing={isClipPlaying}
2423
- muted
2424
2434
  volume={0.8}
2425
2435
  onClick={() => setClipPlaying((playing) => !playing)}
2426
2436
  onEnded={() => setClipPlaying(false)}
@@ -2429,7 +2439,7 @@ import { VideoHotspot } from "@ericchen1990/pano-view";
2429
2439
  />;
2430
2440
  ```
2431
2441
 
2432
- 浏览器可能拒绝非静音或非手势播放。此时 `onPlaybackStateChange` 收到 `"blocked"`,`onPlaybackError` 收到浏览器错误;宿主仍控制 `playing` 并提供显式用户操作。
2442
+ 加载或进入 VR 不会自动开始播放。就绪事件和跳转不会重置通过 controller 设置的静音与音量;修改对应 prop 或切换源时才应用新的配置。需要静音播放时显式传入 `muted`。浏览器可能拒绝非静音或非手势播放。此时 `onPlaybackStateChange` 收到 `"blocked"`,`onPlaybackError` 收到浏览器错误;宿主仍控制 `playing` 并提供显式用户操作。
2433
2443
 
2434
2444
  视频通过 `onError` 报告媒体与 poster 失败,参数含 `source: "poster" | "video"`。与 `SequenceHotspot` 相同,不会自行改变 `playing`,以保证源变更、卸载与 React StrictMode 生命周期可预测。
2435
2445
 
@@ -2709,3 +2719,56 @@ viewLimits: {
2709
2719
  `ProjectScenes.onHotspotEvent(event)` 提供 `trigger`、`sceneId`、`hotspot`、`interaction` 和 `preventDefault()`。点击时先调用它,再调用原有 `onHotspotClick`,任一回调均可取消动作。`onCustomAction(event)` 额外提供 `action`,支持返回 Promise。宿主按稳定的 `action.actionId` 分发,校验业务参数,对未知动作抛出错误。缺少执行器、同步异常和 Promise 拒绝进入附带 `trigger` 的 `onHotspotError`。请求、鉴权、取消、防重复和迟到响应由宿主负责;各事件独立分发,不自动重试。
2710
2720
 
2711
2721
  悬停按整个热点的边界触发一次,跳过触屏和键盘焦点,并遵循可见性、指针交互设置。切换场景、隐藏和卸载仅清理状态,不执行离开动作。原有 tooltip 行为独立保留。自定义渲染器需同时转发 `context.onHoverChange(hovered, interaction)` 和 `context.onClick`;调用 `context.renderDefault()` 会自动接线。
2722
+
2723
+ ## VR 操作按钮组
2724
+
2725
+ 视频、模型和滚动支持可定制图标按钮组,仅在 VR 显示。接口、凝视行为与完整示例见[VR 控件指南](./docs/dialogs.zh-CN.md#vr-图标控件与滚动)。
2726
+
2727
+ ## WebVRSceneMenu
2728
+
2729
+ 在 `PanoViewer` 内与 `WebVR`、`Scenes` 同级挂载。仅在 WebXR、手机 VR 或桌面模拟 VR 中显示:前下方的三按钮导航可切换场景并打开弧形缩略图网格,支持凝视、指针和键盘操作。当前场景带选中框和勾选标记;再次打开时定位到当前场景所在页。
2730
+
2731
+ ```tsx
2732
+ <WebVRSceneMenu
2733
+ items={[
2734
+ { id: "hall", thumbnailUrl: "/hall.webp", title: "大厅" },
2735
+ { id: "garden", thumbnailUrl: "/garden.webp", title: "花园" },
2736
+ ]}
2737
+ activeId={activeSceneId}
2738
+ onSelect={setActiveSceneId}
2739
+ columns={4}
2740
+ rows={3}
2741
+ appearance={{ labelPlacement: "center", selectionColor: "#ffffff" }}
2742
+ />
2743
+ ```
2744
+
2745
+ `items` 复用 `PanoSceneMenuItem`。标题默认居中叠字,最多两行,超长省略;空标题回退到 ID,完整标题保留为无障碍名称。缩略图需允许跨域图片访问,加载失败时仍可通过标题选择。
2746
+
2747
+ | 参数 | 默认值/范围 |
2748
+ | --- | --- |
2749
+ | `layout` | `"compact"`(默认,原有弧形网格)、`"flat"`(平面墙)、`"arc"`(独立平卡环绕画廊) |
2750
+ | `displayMode` | `"paged"`(默认)或 `"all"`;全部模式自动增长行数,忽略 `rows`,隐藏翻页与页码 |
2751
+ | `arcAngle` | 180°;范围 60–360°,仅环绕布局使用 |
2752
+ | `columns`、`rows` | compact/flat 默认 4 列;arc 自动按每 22.5° 一列(半环 8 列,整圈 16 列);compact 列范围 2–4,新布局 2–24。分页默认 3 行,范围 1–3 |
2753
+ | `loop` | `true`,场景首尾循环;分页始终在边界停止 |
2754
+ | `closeOnSelect` | `true`;设为 `false` 可连续浏览 |
2755
+ | `follow` | `true`;仅导航跟随水平朝向,展开网格固定在空间中 |
2756
+ | `navigationPitch` | −25°;−40°至 −15° |
2757
+ | `distance`、`scale` | 2 米、1;范围 1.5–3、0.75–1.25 |
2758
+ | `fadeDurationMs` | 200;0–600ms,尊重减少动态效果设置 |
2759
+ | `showTitles` | `true` |
2760
+ | `showVisited`、`visitedLabel` | `false`、`"Visited"`;已浏览图标及其无障碍描述 |
2761
+
2762
+ 凝视时间沿用 `WebVR.cursorDwellMs`。`activeId` 是选中状态来源,`onSelect` 请求宿主切换场景;场景加载和错误处理仍由宿主负责。弹窗或 VR Setup 打开时暂时隐藏此组件。无场景时隐藏,单场景时禁用左右导航。
2763
+
2764
+ `appearance` 是有限的空间样式接口:`controlColor`(白色)、`controlBackground`(`transparent`)、`controlOpacity`(0.75,范围 0–1)、`selectionColor`(白色)、`focusColor`(`#7dd3fc`)、`selectionWidth`(4,范围 0–8)、`borderRadius`(8,范围 0–48)、`labelColor`(白色)、`labelBackground`(`transparent`)、`fontSize`(28,范围 16–48)、`labelPlacement`(`"center"` 或 `"bottom"`)。场景边框从外侧紧贴缩略图边缘。选中、凝视和键盘焦点共用 `selectionWidth`;设为零时隐藏这些边框,但保留独立的键盘焦点提示。可选的已浏览勾选图标与选中状态独立,边框宽度为零时仍显示。当前场景使用 `selectionColor`;凝视和键盘焦点使用 `focusColor`,焦点落在当前场景上时优先显示焦点颜色。数值尺寸使用参考画布像素并随整体缩放。导出 `WebVRSceneMenuProps`、`WebVRSceneMenuAppearance` 和 `DEFAULT_WEBVR_SCENE_MENU_APPEARANCE`。
2765
+
2766
+ `showVisited` 和 `visitedLabel` 沿用上述 `PanoSceneMenu` 的会话及场景就绪规则。隐藏标题(`showTitles={false}`)或缩略图加载失败时,已浏览图标仍显示;弹窗、VR Setup、翻页和重新进入 VR 均保留查看器共享记录。
2767
+
2768
+ `/webvr` 示例讲解 VR 进入、退出、能力事件与凝视激活。`/vr-scene-menu` 提供行为、布局、外观设置及当前场景标题编辑、25 场景分页演示、恢复默认,以及用于验证进出 VR 后共享浏览记录的普通菜单;`/spatial-controls` 分别讲解媒体、模型及滚动控件,`/immersive-tour` 展示完整组合。设置仅保存在当前页面状态中。
2769
+
2770
+ 悬浮导航作为紧凑的整体按钮组呈现,默认仅显示等高图标,以轻微阴影增强场景中的辨识度。
2771
+
2772
+ 平面墙与环绕画廊使用有界分块图集。全部模式保留卡片尺寸并扩展墙面;大列表可能需要更多转头浏览。打开时以当前水平朝向为中心,展开后不跟随转头,但随用户平移。新布局的关闭和翻页按钮始终位于正前方独立控制区。`/vr-scene-menu` 支持切换布局、全部/分页和环绕角度;列数留空使用自动值。
2773
+
2774
+ VR 场景标题默认不带背景,并带有轻微黑色文字阴影。菜单纹理以 2 倍分辨率绘制,使用 mipmap 与各向异性过滤;布局和命中区域保持不变。
package/dist/data.cjs CHANGED
@@ -464,6 +464,8 @@ var common = [
464
464
  "tooltip",
465
465
  "tooltipTrigger",
466
466
  "tooltipPlacement",
467
+ "tooltipMode",
468
+ "tooltipScaleMode",
467
469
  "tooltipOffset",
468
470
  "tooltipAppearance"
469
471
  ];
@@ -605,6 +607,8 @@ var enums = {
605
607
  pointerEvents: ["auto", "none"],
606
608
  tooltipTrigger: ["always", "hover", "click"],
607
609
  tooltipPlacement: ["top", "bottom", "left", "right"],
610
+ tooltipMode: ["surface", "billboard"],
611
+ tooltipScaleMode: ["inherit", "fov", "fixed"],
608
612
  mode: ["surface", "billboard"],
609
613
  scaleMode: ["fov", "fixed"],
610
614
  frameDirection: ["horizontal", "vertical"],