@ericchen1990/pano-view 3.7.0 → 3.9.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 +675 -71
- package/README.zh-CN.md +558 -42
- package/dist/data.cjs +239 -7
- package/dist/data.cjs.map +1 -1
- package/dist/data.d.mts +120 -2
- package/dist/data.d.ts +120 -2
- package/dist/data.mjs +239 -7
- package/dist/data.mjs.map +1 -1
- package/dist/index.cjs +2385 -310
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.mts +335 -2
- package/dist/index.d.ts +335 -2
- package/dist/index.mjs +2393 -306
- package/dist/index.mjs.map +1 -1
- package/dist/styles.css +399 -1
- package/dist/ui.cjs +1 -1
- package/dist/ui.cjs.map +1 -1
- package/dist/ui.mjs +1 -1
- package/dist/ui.mjs.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -11,6 +11,7 @@ Try the components in the [live playground](https://pano-view-playground.vercel.
|
|
|
11
11
|
- [pano-converter](#pano-converter)
|
|
12
12
|
- [Data sources and choosing an approach](#data-sources-and-choosing-an-approach)
|
|
13
13
|
- [Static projects and JSON data](#static-projects-and-json-data)
|
|
14
|
+
- [Floor plan maps](#floor-plan-maps)
|
|
14
15
|
- [FAQ and troubleshooting](#faq-and-troubleshooting)
|
|
15
16
|
- [krpano-compatible tile output](#krpano-compatible-tile-output)
|
|
16
17
|
- [Install](#install)
|
|
@@ -22,6 +23,7 @@ Try the components in the [live playground](https://pano-view-playground.vercel.
|
|
|
22
23
|
- [PanoVideo](#panovideo)
|
|
23
24
|
- [Tile](#tile)
|
|
24
25
|
- [Scenes](#scenes)
|
|
26
|
+
- [PanoSceneMenu](#panoscenemenu)
|
|
25
27
|
- [PanoFilter](#panofilter)
|
|
26
28
|
- [AutoRotate](#autorotate)
|
|
27
29
|
- [BackgroundAudio](#backgroundaudio)
|
|
@@ -199,7 +201,9 @@ export function Tour() {
|
|
|
199
201
|
|
|
200
202
|
`ProjectScenes` belongs inside `PanoViewer` and reuses all nine existing hotspot types. A `scene` action navigates on click. Without an action, audio/video/sequence hotspots toggle playback. Media unmounts on leaving a scene and starts from its JSON settings on re-entry; browser autoplay restrictions still apply.
|
|
201
203
|
|
|
202
|
-
Configured `initialView` values apply at `onSceneReady`, defaulting missing fields to yaw 0, pitch 0, fov 75. With no view configured, the current view is retained. Hotspots hide during transitions. Control selection with `activeSceneId` / `onActiveSceneIdChange`. Call `event.preventDefault()` in `onHotspotClick` to cancel the default action. `onHotspotError` receives the
|
|
204
|
+
Configured `initialView` values apply at `onSceneReady`, defaulting missing fields to yaw 0, pitch 0, fov 75. With no view configured, the current view is retained. Hotspots hide during transitions. Control selection with `activeSceneId` / `onActiveSceneIdChange`. When uncontrolled, internal state starts at `project.initialSceneId`; a new `project` reference resets that selection. Call `event.preventDefault()` in `onHotspotClick` to cancel the default action. `onHotspotError` receives `sceneId`, the hotspot, and the original error (media errors may be events containing an `error` field). `renderHotspot(hotspot, context)` returns `undefined` for the default renderer or `null` to hide an item. Context includes `sceneId`, `playing`, `onClick`, `onHoverChange`, `togglePlayback`, `reportError`, and `renderDefault`.
|
|
205
|
+
|
|
206
|
+
Beyond project-specific props, `ProjectScenes` accepts the `Scenes` transition and cache props: `transition`, `onSceneReady`, `transitionWaitForDetailMs`, `onTransitionEnd`, `onTransitionError`, `maxTextureMemoryMb`, `maxConcurrentTileLoads`, and `snapshotMaxPixels` (default `3686400`). It does not accept `scenes`, `defaultActiveSceneId`, or a host `renderHotspots` — the scene list comes from `project`, and hotspots are rendered by the component.
|
|
203
207
|
|
|
204
208
|
### Hotspot actions and shared popup content
|
|
205
209
|
|
|
@@ -243,7 +247,167 @@ export function Tour({ project }: { project: LoadedPanoProject }) {
|
|
|
243
247
|
|
|
244
248
|
`ProjectPopupDialog` accepts `popup` (or `null` to close), `onOpenChange`, optional `closeLabel` and `onMediaError(block)`. It renders text as text, uses native audio/video controls without autoplay, and releases media when content closes or changes. It inherits `PanoDialog` fullscreen, sizing and focus behavior. Replace this component with your own overlay to customize rendering.
|
|
245
249
|
|
|
246
|
-
|
|
250
|
+
### Floor plan maps
|
|
251
|
+
|
|
252
|
+
An optional project-level `map` places scene spots on one floor image. Projects without `map` stay valid, and `schemaVersion` remains 1. `loadPanoProject` returns the map with the floor image and spot icons resolved from the project directory. `parsePanoMap` and `resolvePanoMapAssets` are also exported from `/data` for custom loaders.
|
|
253
|
+
|
|
254
|
+
```json
|
|
255
|
+
"map": {
|
|
256
|
+
"image": "assets/map/floor.webp",
|
|
257
|
+
"interaction": "zoom",
|
|
258
|
+
"maxScale": 4,
|
|
259
|
+
"iconSize": 28,
|
|
260
|
+
"presentation": {
|
|
261
|
+
"mode": "docked",
|
|
262
|
+
"corner": "bottom-left",
|
|
263
|
+
"width": 240,
|
|
264
|
+
"height": 180,
|
|
265
|
+
"offsetX": 16,
|
|
266
|
+
"offsetY": 16
|
|
267
|
+
},
|
|
268
|
+
"controls": { "collapse": true, "zoom": true },
|
|
269
|
+
"sector": {
|
|
270
|
+
"angle": 70,
|
|
271
|
+
"radius": 56,
|
|
272
|
+
"followFov": false,
|
|
273
|
+
"color": "#38bdf8",
|
|
274
|
+
"opacity": 0.55,
|
|
275
|
+
"strokeColor": "#ffffff",
|
|
276
|
+
"strokeWidth": 1.5,
|
|
277
|
+
"strokeOpacity": 0.9
|
|
278
|
+
},
|
|
279
|
+
"spots": [{
|
|
280
|
+
"id": "entrance-spot",
|
|
281
|
+
"sceneId": "entrance",
|
|
282
|
+
"x": 0.31,
|
|
283
|
+
"y": 0.5,
|
|
284
|
+
"heading": 20,
|
|
285
|
+
"icon": "assets/map/pin.webp"
|
|
286
|
+
}]
|
|
287
|
+
}
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
`image` is a project-relative path or an absolute URL. `spots` is required. Spot IDs are unique, and each `sceneId` must name a scene in the project. `x` and `y` are normalized image coordinates in [0, 1], with the origin at the top-left. `heading` is clockwise degrees from image-up when viewer yaw is 0; the drawn sector uses `heading + yaw`. Several spots may share a scene. The sector is drawn on the first spot whose `sceneId` matches the active scene. `icon` is optional; a spot without one uses the built-in marker.
|
|
291
|
+
|
|
292
|
+
`interaction` is `"fixed"` (the default) or `"zoom"`. Fixed mode fits the image in the panel and ignores the wheel, drag-pan, and zoom buttons. Zoom mode allows wheel zoom, and drag-pan once the scale is above the fitted size. Wheel events on the map do not scroll the page. `maxScale` is the maximum multiple of that fitted size, at least 1, default 8. `dragMotion` controls pan inertia and edge bounce while zoomed (both on by default). Set `false` to disable both, or `{ inertia?: boolean; bounce?: boolean }` to toggle either flag. Spot and heading edits stay direct. `controls.zoom` shows the zoom buttons only when interaction is `"zoom"`. `controls.collapse` shows expand and collapse on a docked panel. `iconSize` is the marker edge in CSS pixels, default 28. Icons and the sector keep that screen size while the floor image zooms.
|
|
293
|
+
|
|
294
|
+
`sector` is optional. `angle` is the opening in degrees, greater than 0 and less than 360. `radius` is CSS pixels and does not scale with the image. `followFov` replaces `angle` with the horizontal field of view derived from the viewer FOV and the map panel's aspect ratio. Without `gradient`, the fill is solid `color` (default `#ffffff`) at `opacity` (default 0.55). `gradient.type` is `"radial"` or `"linear"`, with at least two stops. Each stop has `offset` in [0, 1], `color`, and an optional `opacity`. Stop opacities describe only the fade shape. `sector.opacity` scales the whole fill in both solid and gradient mode. `strokeColor`, `strokeWidth`, and `strokeOpacity` draw an outline when the width is greater than 0. The outline width stays in CSS pixels.
|
|
295
|
+
|
|
296
|
+
`presentation.mode` is `"docked"` (the default) or `"dialog"`. Both accept `corner` (`top-left`, `top-right`, `bottom-left`, or `bottom-right`; default `bottom-left`), `offsetX` and `offsetY` (default 16), plus `width` and `height`. A docked panel defaults to 220×160. Dialog mode docks a launcher (48×48 when width and height are omitted) and opens the same map in a viewer-local dialog. Choosing a spot closes that dialog. The chrome is positioned on the full viewer overlay, so scrolling a parent page does not move or resize it.
|
|
297
|
+
|
|
298
|
+
The document stores saved defaults (`image`, `spots`, `sector`, `presentation`, `interaction`, `maxScale`, `dragMotion`, `controls`, `iconSize`). Mount-time props on `PanoMapOverlay` override presentation and zoom for that render without cloning the map. Priority is built-in defaults → map document → `presentationOverride` (same shape as `map.presentation`) → individual props. Spot geometry and sector styling stay in the document. `selectedSpotId` / `onSelectedSpotIdChange` are forwarded to the inner `PanoMap`. Docked collapse state is owned by the overlay; dialog mode and `editorSurface` force collapse controls off, so `collapsed` cannot be driven from outside.
|
|
299
|
+
|
|
300
|
+
#### Mounting `PanoMapOverlay`
|
|
301
|
+
|
|
302
|
+
Mount it from `PanoViewer.renderOverlay` and pass the same `activeSceneId` you pass to `ProjectScenes`. Import `@ericchen1990/pano-view/styles.css` for the built-in `pano-map-*` classes. The overlay reads yaw and FOV from the viewer. It does not change scenes by itself: set the active scene from `onSelectScene`. Pass `view` when the camera state comes from outside the viewer; outside the viewer with no `view`, the surface stays at yaw `0`, pitch `0`, fov `75`. `PanoMap` is the surface alone, for a host-owned frame.
|
|
303
|
+
|
|
304
|
+
`PanoMap` also accepts controlled selection and collapse: `selectedSpotId` / `onSelectedSpotIdChange` (internal selection defaults to `null`); `collapsed` / `onCollapsedChange` (uncontrolled starts expanded); `renderChrome` to draw host controls above the surface. `showZoomControls` / `showCollapseControl` work on `PanoMap` the same way as on the overlay. `className` / `style` attach to the surface root.
|
|
305
|
+
|
|
306
|
+
```tsx
|
|
307
|
+
import { useState } from "react";
|
|
308
|
+
import {
|
|
309
|
+
PanoViewer, ProjectScenes, PanoMapOverlay, type LoadedPanoProject,
|
|
310
|
+
} from "@ericchen1990/pano-view";
|
|
311
|
+
import "@ericchen1990/pano-view/styles.css";
|
|
312
|
+
|
|
313
|
+
export function Tour({ project }: { project: LoadedPanoProject }) {
|
|
314
|
+
const [activeSceneId, setActiveSceneId] = useState(project.initialSceneId);
|
|
315
|
+
const map = project.map;
|
|
316
|
+
return <PanoViewer style={{ height: 600 }} renderOverlay={map ? () => (
|
|
317
|
+
<PanoMapOverlay
|
|
318
|
+
map={map}
|
|
319
|
+
activeSceneId={activeSceneId}
|
|
320
|
+
onSelectScene={setActiveSceneId}
|
|
321
|
+
/>
|
|
322
|
+
) : undefined}>
|
|
323
|
+
<ProjectScenes
|
|
324
|
+
project={project}
|
|
325
|
+
activeSceneId={activeSceneId}
|
|
326
|
+
onActiveSceneIdChange={setActiveSceneId}
|
|
327
|
+
/>
|
|
328
|
+
</PanoViewer>;
|
|
329
|
+
}
|
|
330
|
+
```
|
|
331
|
+
|
|
332
|
+
#### Size, corner, and inset
|
|
333
|
+
|
|
334
|
+
- `presentation` — `"docked"` | `"dialog"` (defaults from the document, otherwise `"docked"`).
|
|
335
|
+
- `corner` — `"top-left"` | `"top-right"` | `"bottom-left"` | `"bottom-right"` (default `"bottom-left"`).
|
|
336
|
+
- `width` / `height` — docked panel size, or the dialog launcher size, in CSS pixels. Docked defaults to 220×160; the dialog launcher defaults to 48×48.
|
|
337
|
+
- `offsetX` / `offsetY` — inset from the chosen corner in CSS pixels (default 16).
|
|
338
|
+
|
|
339
|
+
```tsx
|
|
340
|
+
<PanoMapOverlay
|
|
341
|
+
map={map}
|
|
342
|
+
activeSceneId={activeSceneId}
|
|
343
|
+
onSelectScene={setActiveSceneId}
|
|
344
|
+
presentation="docked"
|
|
345
|
+
corner="bottom-right"
|
|
346
|
+
width={280}
|
|
347
|
+
height={200}
|
|
348
|
+
offsetX={20}
|
|
349
|
+
offsetY={20}
|
|
350
|
+
/>
|
|
351
|
+
```
|
|
352
|
+
|
|
353
|
+
#### Dialog mode
|
|
354
|
+
|
|
355
|
+
Dialog mode docks a launcher button and opens the map in a viewer-local modal. Launcher size uses `width` / `height` (and `corner` / offsets). The dialog body uses a separate pair:
|
|
356
|
+
|
|
357
|
+
- `dialogWidth` / `dialogHeight` — preferred dialog size in CSS pixels (default 720×520). Both are clamped to the viewer with a 16 px margin on each side.
|
|
358
|
+
- `openLabel` / `closeLabel` / `title` — launcher accessible name, close button label, and dialog heading (defaults `"Map"`, `"Close"`, `"Map"`).
|
|
359
|
+
|
|
360
|
+
Choosing a spot closes the dialog.
|
|
361
|
+
|
|
362
|
+
```tsx
|
|
363
|
+
<PanoMapOverlay
|
|
364
|
+
map={map}
|
|
365
|
+
activeSceneId={activeSceneId}
|
|
366
|
+
onSelectScene={setActiveSceneId}
|
|
367
|
+
presentation="dialog"
|
|
368
|
+
corner="bottom-left"
|
|
369
|
+
width={52}
|
|
370
|
+
height={52}
|
|
371
|
+
dialogWidth={640}
|
|
372
|
+
dialogHeight={480}
|
|
373
|
+
openLabel="Floor plan"
|
|
374
|
+
title="Floor plan"
|
|
375
|
+
closeLabel="Close"
|
|
376
|
+
/>
|
|
377
|
+
```
|
|
378
|
+
|
|
379
|
+
#### Zoom and controls
|
|
380
|
+
|
|
381
|
+
These props override the map document for the current mount:
|
|
382
|
+
|
|
383
|
+
- `interaction` — `"fixed"` | `"zoom"`.
|
|
384
|
+
- `maxScale` — maximum multiple of the contain-fit size; values below 1 are treated as 1.
|
|
385
|
+
- `dragMotion` — pan inertia / edge bounce when `interaction` is `"zoom"`. Omit for both on; `false` disables both.
|
|
386
|
+
- `initialScale` — opening zoom relative to the contain-fit size, clamped to `[1, maxScale]`. View state only; not written back to the document. Applies after the image is measured and recenters. Resets only when `initialScale` or `interaction` changes; later wheel and drag keep the user's view. Shrinking `maxScale` only clamps the current scale.
|
|
387
|
+
- `showZoomControls` / `showCollapseControl` — force zoom or collapse chrome on or off.
|
|
388
|
+
- `iconSize` — marker edge in CSS pixels.
|
|
389
|
+
|
|
390
|
+
```tsx
|
|
391
|
+
<PanoMapOverlay
|
|
392
|
+
map={map}
|
|
393
|
+
activeSceneId={activeSceneId}
|
|
394
|
+
onSelectScene={setActiveSceneId}
|
|
395
|
+
interaction="zoom"
|
|
396
|
+
maxScale={4}
|
|
397
|
+
initialScale={1.5}
|
|
398
|
+
showZoomControls
|
|
399
|
+
showCollapseControl
|
|
400
|
+
iconSize={32}
|
|
401
|
+
/>
|
|
402
|
+
```
|
|
403
|
+
|
|
404
|
+
#### Edit mode
|
|
405
|
+
|
|
406
|
+
`mode="edit"` is the authoring surface: with `placementSceneId` set, a click adds a spot whose heading is the negation of the current yaw, so the sector points image-up at that yaw; dragging a spot moves it, and dragging the sector sets `heading`. `onEditChange` reports `{ spots?, selectedSpotId? }`. Visitor playback stays on the default `mode="view"`. `editorSurface` enlarges the panel for authoring; pass `width` / `height` to size that surface, otherwise it scales with the viewer.
|
|
407
|
+
|
|
408
|
+
Run `pnpm dev` and open `/map` for the playground. Authoring the same document is described in the [editor floor-plan section](../editor/README.md#floor-plan).
|
|
409
|
+
|
|
410
|
+
The schema remains version 1 and old projects remain valid. Projects using new actions or a floor-plan map require a viewer that supports them. `parsePanoProject(input, { draft: true })` is for authoring only: it permits empty scene lists, unfinished popup titles/media, and an empty floor-plan image. Loading, saving and package export validate strictly. Host applications may save the same document in a database or map it to tables; the viewer has no database connection.
|
|
247
411
|
|
|
248
412
|
For individual panoramas and existing configs use the pure data entry. Next.js viewer components need a client boundary; server-side data loading uses `/data`:
|
|
249
413
|
|
|
@@ -274,14 +438,14 @@ const project = await loadPanoProject({
|
|
|
274
438
|
|
|
275
439
|
| Reference | Default base |
|
|
276
440
|
| --- | --- |
|
|
277
|
-
| Project `viewerUrl`, hotspot assets, `source.baseUrl` | Project directory |
|
|
441
|
+
| Project `viewerUrl`, hotspot assets, floor-plan image and spot icons, `source.baseUrl` | Project directory |
|
|
278
442
|
| Tiles and preview in external `viewer.json` | That JSON directory |
|
|
279
443
|
| Tiles and preview in inline config | Project directory |
|
|
280
444
|
| Panorama with `source.baseUrl` | Specified directory |
|
|
281
445
|
|
|
282
446
|
URL inputs use the final response directory after redirects. Object inputs require `baseUrl`; it can also override the project directory. Directory trailing slashes are normalized. Domains and cloud prefixes stay outside converter JSON. Browsers accept relative entry URLs; Node.js requires absolute HTTP(S) entry URLs. Full resource URLs remain intact; leading `/` paths resolve against the resource base origin.
|
|
283
447
|
|
|
284
|
-
If uploads rename files or scatter directories, provide synchronous `resolveAssetUrl(path, context)`. Context has `defaultUrl`, `kind` (`tile`, `preview`, `hotspot`), `sceneId` and optional `hotspotId`. Return a final absolute URL or site-root path; it is not joined again. `uploadedUrls` below is application-owned:
|
|
448
|
+
If uploads rename files or scatter directories, provide synchronous `resolveAssetUrl(path, context)`. Context has `defaultUrl`, `kind` (`tile`, `preview`, `hotspot`, `map`), `sceneId` and optional `hotspotId`. Return a final absolute URL or site-root path; it is not joined again. `uploadedUrls` below is application-owned:
|
|
285
449
|
|
|
286
450
|
```ts
|
|
287
451
|
const project = await loadPanoProject("/tour/project.json", {
|
|
@@ -293,13 +457,13 @@ const project = await loadPanoProject("/tour/project.json", {
|
|
|
293
457
|
});
|
|
294
458
|
```
|
|
295
459
|
|
|
296
|
-
Hotspot mapping covers images, SVG image URLs, audio source arrays, video posters, playback icons and tooltip images; text, CSS and SVG path data remain unchanged. Acquire signed URLs ahead of time in your application and return them synchronously.
|
|
460
|
+
Hotspot mapping covers images, SVG image URLs, audio source arrays, video posters, playback icons and tooltip images; text, CSS and SVG path data remain unchanged. Floor-plan images and spot icons use the same callback with `kind: "map"`. The floor image has an empty `sceneId` and no `hotspotId`. A spot icon sets both `sceneId` and `hotspotId` to the spot id. Include `kind` in an upload key when one path is reused as more than one kind. Acquire signed URLs ahead of time in your application and return them synchronously.
|
|
297
461
|
|
|
298
462
|
`requestInit` accepts only `headers`, `credentials` and `signal`, for JSON requests only. They are not forwarded to textures, and JSON query parameters are not inherited by assets. `parsePanoProject(unknown)` validates and copies project data independently. `sceneFromViewerConfig` accepts the unwrapped compact config; `loadSceneFromJson` accepts a versioned `viewer.json`.
|
|
299
463
|
|
|
300
464
|
`PanoDataError` exposes stable codes: `INVALID_OPTIONS`, `FETCH_FAILED`, `HTTP_ERROR`, `INVALID_JSON`, `INVALID_CONFIG`, `UNSUPPORTED_SCHEMA_VERSION`, `UNSUPPORTED_PROJECTION`, `INVALID_PROJECT`, `INVALID_REFERENCE`, `ABORTED`. HTTP errors carry `status`; project errors include `sceneId` and `field` where available. Successful loading means metadata is ready, not that textures have loaded; use `onSceneReady` for render readiness.
|
|
301
465
|
|
|
302
|
-
See the `/project` playground route and `public/fixtures/projects/tour/project.json` for a complete static example.
|
|
466
|
+
See the `/project` playground route and `public/fixtures/projects/tour/project.json` for a complete static example. `/map` shows the floor-plan overlay on that tour.
|
|
303
467
|
|
|
304
468
|
## krpano-compatible tile output
|
|
305
469
|
|
|
@@ -377,7 +541,8 @@ Import the shipped stylesheet once in the host app. The WebGL panorama
|
|
|
377
541
|
**HTML** overlays depend on its prefixed `pano-*` classes for layout.
|
|
378
542
|
|
|
379
543
|
Those overlays include video controls, captions, the default context menu,
|
|
380
|
-
hotspot tooltips, controls for assistive technology,
|
|
544
|
+
hotspot tooltips, controls for assistive technology, WebVR session UI, and the
|
|
545
|
+
floor-plan map.
|
|
381
546
|
|
|
382
547
|
```ts
|
|
383
548
|
import "@ericchen1990/pano-view/styles.css";
|
|
@@ -397,8 +562,9 @@ the stylesheet, the overlays still mount, but they render unstyled.
|
|
|
397
562
|
|
|
398
563
|
Give PanoViewer an explicit size and render one panorama source inside it.
|
|
399
564
|
Import the stylesheet once when you use built-in HTML overlays such as video
|
|
400
|
-
controls, captions, tooltips, the context menu,
|
|
401
|
-
below shows a basic setup; the linked section has the full
|
|
565
|
+
controls, captions, tooltips, the context menu, WebVR UI, or the floor-plan
|
|
566
|
+
map. Each example below shows a basic setup; the linked section has the full
|
|
567
|
+
props.
|
|
402
568
|
|
|
403
569
|
### Equirectangular image
|
|
404
570
|
|
|
@@ -514,8 +680,14 @@ Presets and hotspot rendering are in [Scenes](#scenes).
|
|
|
514
680
|
|
|
515
681
|
- [`PanoEvents`](#panoevents) — viewer-level lifecycle and interaction callbacks
|
|
516
682
|
- [`PanoContextMenu`](#panocontextmenu) — right-click reset / fullscreen menu
|
|
683
|
+
- [`PanoSceneMenu`](#panoscenemenu) — scrollable thumbnail scene picker for `renderOverlay`
|
|
517
684
|
- [`PanoVideoControls`](#panovideo) — playback chrome for `PanoVideo` (mounted by default)
|
|
518
685
|
|
|
686
|
+
### Floor plan
|
|
687
|
+
|
|
688
|
+
- [`PanoMap`](#floor-plan-maps) — floor image, scene spots, and the yaw-linked sector
|
|
689
|
+
- [`PanoMapOverlay`](#floor-plan-maps) — docked panel or dialog launcher for `renderOverlay`
|
|
690
|
+
|
|
519
691
|
### Hotspots
|
|
520
692
|
|
|
521
693
|
- [`ImageHotspot`](#imagehotspot) — image texture at a spherical position
|
|
@@ -539,9 +711,7 @@ Presets and hotspot rendering are in [Scenes](#scenes).
|
|
|
539
711
|
|
|
540
712
|
## DOM overlays and dialogs
|
|
541
713
|
|
|
542
|
-
Use `PanoViewer.renderOverlay` for ordinary DOM UI outside the Three.js tree. `usePanoOverlay()`
|
|
543
|
-
|
|
544
|
-
See the separate **[Dialog and styling guide](./docs/dialogs.md)** for complete examples, CSS variable defaults and placement, custom layouts, third-party integration, nested portals, lifecycle, API tables and troubleshooting. The guide is included in the npm package. Run `pnpm dev` and open `/overlays` for the local examples.
|
|
714
|
+
Use `PanoViewer.renderOverlay` for ordinary DOM UI outside the Three.js tree. Call `usePanoOverlay()` inside the `renderOverlay` subtree to get `portalContainer`, `size` (viewer width/height), `visibleRect`, and `isFullscreen`. `usePanoModal(open)` holds that viewer's modal lock while `open === true` (pauses input and auto-rotate, marks the backdrop inert, closes the context menu); it does not trap focus. `PanoOverlayPortalProvider` takes a required `container` and `children` and only remaps `portalContainer` for descendants; `size` / `visibleRect` still describe the whole viewer. Field meanings, reference counting, and third-party Dialog wiring live in the separate **[Dialog and styling guide](./docs/dialogs.md)**. The optional `/ui` entry provides `PanoDialog` and composable parts. The guide ships in the npm package; run `pnpm dev` and open `/overlays` for local examples.
|
|
545
715
|
|
|
546
716
|
### Default dialog
|
|
547
717
|
|
|
@@ -634,6 +804,21 @@ which use their spherical center. Hotspot ids must be unique within a viewer.
|
|
|
634
804
|
|
|
635
805
|
**Background Audio** (requires `<BackgroundAudio />`) — `getBackgroundAudio` returns the `BackgroundAudioController`, `subscribeBackgroundAudio` subscribes to background-audio controller and playback-state changes, plus convenience aliases `playBackgroundAudio`, `pauseBackgroundAudio`, `toggleBackgroundAudio`, `setBackgroundAudioVolume`, `setBackgroundAudioMuted`, `toggleBackgroundAudioMuted`. `BackgroundAudio` accepts `playing` (controlled) or `defaultPlaying` (uncontrolled).
|
|
636
806
|
|
|
807
|
+
### Props
|
|
808
|
+
|
|
809
|
+
- `initialView` — when omitted, yaw `0`, pitch `0`, fov `75`. Partial objects fill the rest from those defaults.
|
|
810
|
+
- `minFov` / `maxFov` — default `30` / `100`. Used as FOV bounds when `viewLimits.fov` is not set.
|
|
811
|
+
- `viewLimits` — camera-center yaw / pitch / fov ranges; scene-level limits override matching axes. See [Camera-center limits](#camera-center-limits).
|
|
812
|
+
- `dpr` — canvas pixel ratio, default `[1, 2]` (clamped device pixel ratio). A single number is also accepted.
|
|
813
|
+
- `controls` — `false` disables all user input. An object configures shared options and `mouse` / `touch` / `keyboard` channels; see [Mouse, touch, and keyboard controls](#mouse-touch-and-keyboard-controls). Shared defaults: `enabled` `true`, `inertia` `true`, `invert` `false`, `bouncingLimits` `false`, `frictionStop` `0.01`, `rotateSpeed` `0.35`, `zoomSpeed` `0.08`, `rotateDamping` / `zoomDamping` `14` / `16`. Omit `fovSpeed` for no extra cap: mouse wheel clamps each step `deltaY * zoomSpeed` to `±fovSpeed`; keyboard zoom compares degrees-per-second × frame time against `fovSpeed`.
|
|
814
|
+
- `cursors` — `true` / omit uses grab / grabbing / pointer / move; `false` leaves the canvas cursor unchanged; an object overrides those four states.
|
|
815
|
+
- `contextMenu` — `true` / omit mounts Reset view + Fullscreen; `false` skips the default menu; an object can change appearance or `items` / `prepend` / `append`. See [PanoContextMenu](#panocontextmenu).
|
|
816
|
+
- `mediaActivation` — enabled by default when a mounted child intends to play; `false` disables automatic activation.
|
|
817
|
+
- `renderOverlay` — DOM UI outside the Three.js tree and inside fullscreen; the callback receives `portalContainer`, `size`, `visibleRect`, and `isFullscreen`.
|
|
818
|
+
- `onViewChange` — `{ yaw, pitch, fov }` in degrees.
|
|
819
|
+
- `onPanoramaClick` / `onPanoramaDoubleClick` / `onPanoramaPointerMove` — panorama pointer events with `position` (yaw / pitch), `nativeEvent`, `button`, `buttons`, `clientX`, `clientY`, and optional `pointerId`.
|
|
820
|
+
- Other standard `div` props (`className`, `style`, `id`, …) are accepted; conflicting `onChange` and `contextMenu` are omitted from the type.
|
|
821
|
+
|
|
637
822
|
### Mobile media activation
|
|
638
823
|
|
|
639
824
|
By default, when a child has an initial playback intent (`PanoVideo autoPlay`, an active `BackgroundAudio`, `VideoHotspot`, or `AudioHotspot`), the viewer silently unlocks audible playback on the first user gesture when the browser requires one, or immediately when autoplay policy already allows it. Muted video can still preview inline. The same gesture resumes Web Audio and gives the host the final choice of which media becomes audible, so video, music, and positional sound never all start by assumption. Pass `mediaActivation={false}` to retain the 2.x no-automatic-activation behavior.
|
|
@@ -924,7 +1109,7 @@ equirectangular tiles, use [`SphereTile`](#spherical-multiresolution-tiles).
|
|
|
924
1109
|
Provide a small preview to reduce download, decoding, and texture memory costs
|
|
925
1110
|
for the initial display.
|
|
926
1111
|
Relative paths resolve against the directory of `src`; root-absolute and
|
|
927
|
-
`http(s)` URLs are used as-is. For krpano sphere scenes, copy
|
|
1112
|
+
`http(s)`, `blob:`, and `data:` URLs are used as-is. For krpano sphere scenes, copy
|
|
928
1113
|
[`<preview url>`](https://krpano.com/docu/xml/#preview) into `previewUrl`. That
|
|
929
1114
|
preview must be equirectangular, not a cube strip.
|
|
930
1115
|
|
|
@@ -945,6 +1130,17 @@ export function SphereExample() {
|
|
|
945
1130
|
}
|
|
946
1131
|
```
|
|
947
1132
|
|
|
1133
|
+
### Props
|
|
1134
|
+
|
|
1135
|
+
- `src` — required full 2:1 equirectangular image.
|
|
1136
|
+
- `previewUrl` — required low-resolution 2:1 preview while `src` loads.
|
|
1137
|
+
- `yawOffset` — horizontal rotation in degrees, default `0`. Set when the source forward direction differs from viewer yaw `0`.
|
|
1138
|
+
- `visible` — default `true`. `false` keeps the source mounted for preload without drawing it.
|
|
1139
|
+
- `onReady` — once when the first paintable texture (preview or `src`) is ready.
|
|
1140
|
+
- `onLoad` — when the `src` texture finishes loading; argument is a Three.js `Texture`.
|
|
1141
|
+
- `onError` — when `src` fails to load.
|
|
1142
|
+
- `onPreviewError` — when the preview fails; `src` still continues loading.
|
|
1143
|
+
|
|
948
1144
|
## PanoVideo
|
|
949
1145
|
|
|
950
1146
|
`PanoVideo` maps a 2:1 equirectangular video onto the same inward sphere as
|
|
@@ -1025,6 +1221,30 @@ rejected. Remote videos and VTT files that are not same-origin should set
|
|
|
1025
1221
|
`crossOrigin` (usually `"anonymous"`). For mobile delivery, provide an H.264 +
|
|
1026
1222
|
AAC MP4 fallback, poster, CORS, and Range support.
|
|
1027
1223
|
|
|
1224
|
+
### Props
|
|
1225
|
+
|
|
1226
|
+
- `src` — single-file shortcut; mutually exclusive with `variants`.
|
|
1227
|
+
- `poster` — cover image. Variants may also set `variants[].poster`.
|
|
1228
|
+
- `variants` / `defaultVariantId` — multi-quality list; missing `defaultVariantId` falls back to the first item. Switching keeps current time and play/pause.
|
|
1229
|
+
- `tracks` — WebVTT tracks. Each may include `src`, `srcLang`, `label`, `default`, `id` (defaults to `srcLang`), and `kind` (`"subtitles"` or `"captions"`, default `"subtitles"`).
|
|
1230
|
+
- `captions` — `true` / omit shows the default overlay; `false` hides the overlay and language menu; an object merges with defaults. Defaults: `color` `#ffffff`, `background` `rgba(0, 0, 0, 0.55)`, `fontSize` `16`, `fontFamily` Inter / system-ui, `fontWeight` `500`, `textShadow` `0 1px 2px rgba(0, 0, 0, 0.8)`, `padding` `"6px 10px"`, `borderRadius` `6`, `maxWidth` `"72%"`, `bottom` `72`, `lineHeight` `1.35`.
|
|
1231
|
+
- `controls` — `true` / omit mounts the default bar; `false` hides it; an object restyles the default instance. Appearance keys: `background` (default `rgba(16, 16, 16, 0.88)`), `color` (`#f4f4f4`), `accent` (`#75cbd3`), `borderRadius` (`8`), `fontSize` (`12`), `padding` (`"8px 10px"`). Render `PanoVideoControls` as a `PanoViewer` child to replace the bar (same `appearance` prop).
|
|
1232
|
+
- `yawOffset` — horizontal offset in degrees, default `0`.
|
|
1233
|
+
- `loop` — default `false`.
|
|
1234
|
+
- `muted` — default `true`.
|
|
1235
|
+
- `volume` — default `1`, range 0–1.
|
|
1236
|
+
- `autoPlay` — default `false`. When `true`, counts as media-activation intent.
|
|
1237
|
+
- `playsInline` — default `true`.
|
|
1238
|
+
- `preload` — `"none"` | `"metadata"` | `"auto"`, default `"metadata"`.
|
|
1239
|
+
- `crossOrigin` — `""` | `"anonymous"` | `"use-credentials"`; omit to leave unset.
|
|
1240
|
+
- `playbackRates` — speed menu list, default `[0.5, 1, 1.25, 1.5, 2]`.
|
|
1241
|
+
- `visible` — default `true`; `false` preloads without drawing.
|
|
1242
|
+
- `onLoad` — when the video becomes playable.
|
|
1243
|
+
- `onError` — `{ source: "poster" | "video" | "track", error }`.
|
|
1244
|
+
- `onPlaybackStateChange` — `"playing" | "paused" | "ended" | "blocked"`.
|
|
1245
|
+
- `onEnded` — when playback reaches the end without looping.
|
|
1246
|
+
- `onVariantChange` / `onTrackChange` — current variant id, or current track id (`null` when none).
|
|
1247
|
+
|
|
1028
1248
|
## Tile
|
|
1029
1249
|
|
|
1030
1250
|
`Tile` renders six inward-facing cube faces and loads only tiles around the current view. When `urlTemplate` is omitted, paths relative to `baseUrl` use this package default:
|
|
@@ -1136,6 +1356,24 @@ horizontal index and `%00v` produces a three-digit vertical index. The stereo
|
|
|
1136
1356
|
`%t` and frame `%f` placeholders are not applicable to `Tile`, which represents
|
|
1137
1357
|
a single non-stereo cube panorama.
|
|
1138
1358
|
|
|
1359
|
+
### Props
|
|
1360
|
+
|
|
1361
|
+
- `baseUrl` / `multires` / `previewUrl` — required. `multires` may be a string or `{ tileSize, levels }`.
|
|
1362
|
+
- `previewFaceOrder` — default `["l", "f", "r", "b", "u", "d"]`; must list exactly six faces.
|
|
1363
|
+
- `urlTemplate` — package default when omitted; `resolveTileUrl` wins over the template.
|
|
1364
|
+
- `maxTextureMemoryMb` — standalone `Tile` default `128`.
|
|
1365
|
+
- `maxConcurrentLoads` — standalone `Tile` default `8` (do not confuse with `SphereTile` / `Scenes` concurrency of `4`).
|
|
1366
|
+
- `retryCount` — retries after the first failure, default `1`.
|
|
1367
|
+
- `loadMode` — `"full"` (default), `"preview"`, or `"base"`.
|
|
1368
|
+
- `visible` — default `true`; `false` preloads only.
|
|
1369
|
+
- `onReady` — when the preview is paintable (tiles may still report ready if the preview failed).
|
|
1370
|
+
- `onPreviewError` — preview load failure.
|
|
1371
|
+
- `onLoadProgress` — `{ requested, loaded, failed, active, queued }`.
|
|
1372
|
+
- `onTileError` — `{ address, url, error }`.
|
|
1373
|
+
- `onLevelChange` — current cube level, **1-based** (`SphereTile` logical levels are 0-based).
|
|
1374
|
+
|
|
1375
|
+
Inside `Scenes` with `type: "tile"`, the shared texture cache overrides per-scene `maxTextureMemoryMb`, `maxConcurrentLoads`, and `retryCount` (the shared provider fixes retry at `1`). Visibility, `loadMode`, and the callbacks above are owned by `Scenes` and should not be set on the scene object.
|
|
1376
|
+
|
|
1139
1377
|
## Scenes
|
|
1140
1378
|
|
|
1141
1379
|
`Scenes` switches between single-image equirectangular, cube-tile, and
|
|
@@ -1211,6 +1449,234 @@ While a detail wait or transition runs, panorama drag/zoom input is locked and
|
|
|
1211
1449
|
is still being prepared. `maxTextureMemoryMb` and `maxConcurrentTileLoads`
|
|
1212
1450
|
apply to the whole `Scenes` viewer rather than to each tile scene.
|
|
1213
1451
|
|
|
1452
|
+
### Props
|
|
1453
|
+
|
|
1454
|
+
- `scenes` — required. Supports `type: "sphere" | "tile" | "sphereTile"`.
|
|
1455
|
+
- `activeSceneId` — controlled current scene.
|
|
1456
|
+
- `defaultActiveSceneId` — uncontrolled initial scene. When both are omitted, uses `scenes[0].id`.
|
|
1457
|
+
- `onActiveSceneIdChange` — fired by imperative switches in controlled mode.
|
|
1458
|
+
- `transition` — defaults to `"crossfade"` for `1` second. Also accepts `{ preset, duration }`. Preset default seconds: `none` 0; `crossfade` / `whiteFlash` / most slides and opens / `pixelate` / `dissolve` / `clockWipe` / `zoomBlur` 1; `verticalOpen` 0.7; `glitch` 0.8; `hexDissolve` 1.1; the six `gridWipe*` / `shatter` / `swirl` / `ripple` 1.2; `filmBurn` 1.3; `particles` 1.4; `zoom` / `blackout` 2.
|
|
1459
|
+
- `transitionWaitForDetailMs` — default `0`.
|
|
1460
|
+
- `renderHotspots` — rendered only after a transition finishes; hidden while blending.
|
|
1461
|
+
- `onSceneReady` — first paintable scene, or incoming scene after snapshot takeover; not full detail.
|
|
1462
|
+
- `onTransitionEnd` — `{ previousSceneId, sceneId, preset }`.
|
|
1463
|
+
- `onTransitionError` — `{ sceneId, error }`.
|
|
1464
|
+
- `maxTextureMemoryMb` — shared budget, default `128`.
|
|
1465
|
+
- `maxConcurrentTileLoads` — shared concurrency, default `4`.
|
|
1466
|
+
- `snapshotMaxPixels` — max physical-pixel area of the outgoing GPU snapshot, default `3686400` (~2560×1440).
|
|
1467
|
+
|
|
1468
|
+
Scene `viewLimits` are covered in the FAQ; `yawOffset` matches the source component. Per-scene cube cache fields are ignored under the shared cache — see [Tile](#tile).
|
|
1469
|
+
|
|
1470
|
+
## PanoSceneMenu
|
|
1471
|
+
|
|
1472
|
+
`PanoSceneMenu` is a scrollable thumbnail strip for switching scenes. Mount it
|
|
1473
|
+
from `PanoViewer.renderOverlay` so it stays inside the viewer (including
|
|
1474
|
+
fullscreen). Import `@ericchen1990/pano-view/styles.css` for the default layout.
|
|
1475
|
+
|
|
1476
|
+
The menu is host-driven: you pass `items`, `activeId`, and `onSelect`. It does
|
|
1477
|
+
not read `Scenes` state or scene `previewUrl`. Cube `previewUrl` values are
|
|
1478
|
+
often 1×6 preview strips and are not suitable thumbnails — use a dedicated
|
|
1479
|
+
thumbnail URL (for converter output, prefer something like `previews/200.webp`).
|
|
1480
|
+
|
|
1481
|
+
### Basic wiring
|
|
1482
|
+
|
|
1483
|
+
Keep one controlled scene id and share it with `Scenes`:
|
|
1484
|
+
|
|
1485
|
+
```tsx
|
|
1486
|
+
import { useState } from "react";
|
|
1487
|
+
import {
|
|
1488
|
+
PanoSceneMenu,
|
|
1489
|
+
PanoViewer,
|
|
1490
|
+
Scenes,
|
|
1491
|
+
type PanoSceneMenuItem,
|
|
1492
|
+
} from "@ericchen1990/pano-view";
|
|
1493
|
+
import "@ericchen1990/pano-view/styles.css";
|
|
1494
|
+
|
|
1495
|
+
const menuItems: PanoSceneMenuItem[] = [
|
|
1496
|
+
{
|
|
1497
|
+
id: "lobby",
|
|
1498
|
+
title: "Lobby",
|
|
1499
|
+
thumbnailUrl: "/panoramas/lobby/thumb.webp",
|
|
1500
|
+
},
|
|
1501
|
+
{
|
|
1502
|
+
id: "terrace",
|
|
1503
|
+
title: "Terrace",
|
|
1504
|
+
thumbnailUrl: "/panoramas/terrace/previews/200.webp",
|
|
1505
|
+
},
|
|
1506
|
+
];
|
|
1507
|
+
|
|
1508
|
+
export function Tour() {
|
|
1509
|
+
const [activeSceneId, setActiveSceneId] = useState("lobby");
|
|
1510
|
+
|
|
1511
|
+
return (
|
|
1512
|
+
<PanoViewer
|
|
1513
|
+
style={{ height: 560 }}
|
|
1514
|
+
renderOverlay={() => (
|
|
1515
|
+
<PanoSceneMenu
|
|
1516
|
+
activeId={activeSceneId}
|
|
1517
|
+
items={menuItems}
|
|
1518
|
+
onSelect={setActiveSceneId}
|
|
1519
|
+
/>
|
|
1520
|
+
)}
|
|
1521
|
+
>
|
|
1522
|
+
<Scenes
|
|
1523
|
+
scenes={scenes}
|
|
1524
|
+
activeSceneId={activeSceneId}
|
|
1525
|
+
onActiveSceneIdChange={setActiveSceneId}
|
|
1526
|
+
/>
|
|
1527
|
+
</PanoViewer>
|
|
1528
|
+
);
|
|
1529
|
+
}
|
|
1530
|
+
```
|
|
1531
|
+
|
|
1532
|
+
Each item needs `id` and `thumbnailUrl`. Optional `title` is shown on the
|
|
1533
|
+
thumbnail label and as the control tooltip; when omitted, `id` is used. The
|
|
1534
|
+
active item gets a selection ring above the thumbnail and `data-active="true"`.
|
|
1535
|
+
Scene names sit on a dark translucent bar at the bottom of each thumbnail;
|
|
1536
|
+
use `appearance.labelAlign` for `"center"` or `"end"` alignment.
|
|
1537
|
+
|
|
1538
|
+
### Placement, inset, and size
|
|
1539
|
+
|
|
1540
|
+
- `placement` — `"top"` | `"bottom"` | `"left"` | `"right"` (default `"bottom"`).
|
|
1541
|
+
Top/bottom render a horizontal strip; left/right render a vertical strip.
|
|
1542
|
+
- `align` — `"start"` | `"center"` | `"end"` (default `"center"`). When the menu
|
|
1543
|
+
is smaller than the available dock space, aligns it along that edge. Short
|
|
1544
|
+
lists are also aligned inside the strip the same way.
|
|
1545
|
+
- `inset` — gap from the viewer edges (default `12`). A number is pixels; a
|
|
1546
|
+
string keeps units such as `"1rem"`; an object sets `top` / `right` /
|
|
1547
|
+
`bottom` / `left` independently.
|
|
1548
|
+
- `width` / `height` — menu size. A number is pixels; a string is used as CSS as-is,
|
|
1549
|
+
including `%`, `rem`, `calc()`, `min()`, `max()`, and `clamp()`.
|
|
1550
|
+
- `dragMotion` — scroll inertia and edge bounce. Omit for both on (custom pointer
|
|
1551
|
+
scrolling, including desktop drag). Set `false` for native overflow scroll.
|
|
1552
|
+
Pass `{ inertia?: boolean; bounce?: boolean }` to toggle either flag. A short
|
|
1553
|
+
press still selects; a drag past a few pixels suppresses the click.
|
|
1554
|
+
|
|
1555
|
+
```tsx
|
|
1556
|
+
<PanoSceneMenu
|
|
1557
|
+
placement="bottom"
|
|
1558
|
+
align="center"
|
|
1559
|
+
inset={16}
|
|
1560
|
+
width="min(720px, 100%)"
|
|
1561
|
+
items={menuItems}
|
|
1562
|
+
activeId={activeSceneId}
|
|
1563
|
+
onSelect={setActiveSceneId}
|
|
1564
|
+
/>
|
|
1565
|
+
|
|
1566
|
+
<PanoSceneMenu
|
|
1567
|
+
placement="right"
|
|
1568
|
+
align="start"
|
|
1569
|
+
inset={{ top: 24, right: 12, bottom: 24, left: 12 }}
|
|
1570
|
+
height="70%"
|
|
1571
|
+
width={160}
|
|
1572
|
+
items={menuItems}
|
|
1573
|
+
activeId={activeSceneId}
|
|
1574
|
+
onSelect={setActiveSceneId}
|
|
1575
|
+
/>
|
|
1576
|
+
```
|
|
1577
|
+
|
|
1578
|
+
If you omit `width` on a top/bottom menu, the strip stretches between the left
|
|
1579
|
+
and right insets. If you omit `height` on a left/right menu, it stretches between
|
|
1580
|
+
the top and bottom insets. The menu also applies a max size that subtracts
|
|
1581
|
+
`inset` and safe-area insets, so `100%` still stays inside the viewer.
|
|
1582
|
+
|
|
1583
|
+
Use `style` when you need one-off positioning overrides on top of these props.
|
|
1584
|
+
|
|
1585
|
+
### Collapsible menu and arrows
|
|
1586
|
+
|
|
1587
|
+
By default the menu stays open. Set `collapsible` to show a toggle centered on
|
|
1588
|
+
the inner edge of the strip (above a bottom menu, below a top menu, toward the
|
|
1589
|
+
viewer for left/right). Collapsing slides the list off-screen along the dock
|
|
1590
|
+
edge and leaves only the expand button on the safe-area edge:
|
|
1591
|
+
|
|
1592
|
+
```tsx
|
|
1593
|
+
<PanoSceneMenu
|
|
1594
|
+
collapsible
|
|
1595
|
+
defaultExpanded
|
|
1596
|
+
items={menuItems}
|
|
1597
|
+
activeId={activeSceneId}
|
|
1598
|
+
onSelect={setActiveSceneId}
|
|
1599
|
+
/>
|
|
1600
|
+
|
|
1601
|
+
// Controlled
|
|
1602
|
+
<PanoSceneMenu
|
|
1603
|
+
collapsible
|
|
1604
|
+
expanded={menuOpen}
|
|
1605
|
+
onExpandedChange={setMenuOpen}
|
|
1606
|
+
items={menuItems}
|
|
1607
|
+
activeId={activeSceneId}
|
|
1608
|
+
onSelect={setActiveSceneId}
|
|
1609
|
+
/>
|
|
1610
|
+
```
|
|
1611
|
+
|
|
1612
|
+
Set `showArrows` to add previous/next buttons at both ends of the list. By
|
|
1613
|
+
default the buttons stop at the first and last items. Set `loop` so arrows and
|
|
1614
|
+
arrow keys wrap from the last item to the first (and vice versa). Arrows stay
|
|
1615
|
+
disabled when there are fewer than two scenes. Customize their accessible names
|
|
1616
|
+
with `previousLabel` / `nextLabel`, and the toggle with `expandLabel` /
|
|
1617
|
+
`collapseLabel`. Defaults are `"Previous scene"`, `"Next scene"`,
|
|
1618
|
+
`"Show scenes"`, and `"Hide scenes"`. The radiogroup label defaults to `"Scenes"`
|
|
1619
|
+
via `label`. When collapsible, `defaultExpanded` defaults to `true`.
|
|
1620
|
+
|
|
1621
|
+
```tsx
|
|
1622
|
+
<PanoSceneMenu
|
|
1623
|
+
showArrows
|
|
1624
|
+
loop
|
|
1625
|
+
collapsible
|
|
1626
|
+
placement="bottom"
|
|
1627
|
+
width="min(720px, 100%)"
|
|
1628
|
+
items={menuItems}
|
|
1629
|
+
activeId={activeSceneId}
|
|
1630
|
+
onSelect={setActiveSceneId}
|
|
1631
|
+
previousLabel="Previous room"
|
|
1632
|
+
nextLabel="Next room"
|
|
1633
|
+
/>
|
|
1634
|
+
```
|
|
1635
|
+
|
|
1636
|
+
### Appearance and CSS hooks
|
|
1637
|
+
|
|
1638
|
+
Pass `appearance` for common chrome tokens without writing CSS. The active-item
|
|
1639
|
+
ring sits above the thumbnail and is controlled by `selectionColor` /
|
|
1640
|
+
`selectionWidth` (`selectionWidth: 0` hides it). Scene titles default to start
|
|
1641
|
+
alignment; set `labelAlign: "center"` or `"end"` to change that.
|
|
1642
|
+
|
|
1643
|
+
```tsx
|
|
1644
|
+
<PanoSceneMenu
|
|
1645
|
+
appearance={{
|
|
1646
|
+
background: "rgba(8, 12, 18, 0.88)",
|
|
1647
|
+
itemWidth: 144,
|
|
1648
|
+
itemHeight: 88,
|
|
1649
|
+
gap: 10,
|
|
1650
|
+
borderRadius: 10,
|
|
1651
|
+
selectionColor: "#7dd3fc",
|
|
1652
|
+
selectionWidth: 2,
|
|
1653
|
+
labelBackground: "rgba(0, 0, 0, 0.6)",
|
|
1654
|
+
labelColor: "#ffffff",
|
|
1655
|
+
labelAlign: "center",
|
|
1656
|
+
fontSize: 12,
|
|
1657
|
+
controlColor: "#f8fafc",
|
|
1658
|
+
controlBackground: "rgba(255, 255, 255, 0.14)",
|
|
1659
|
+
}}
|
|
1660
|
+
items={menuItems}
|
|
1661
|
+
activeId={activeSceneId}
|
|
1662
|
+
onSelect={setActiveSceneId}
|
|
1663
|
+
/>
|
|
1664
|
+
```
|
|
1665
|
+
|
|
1666
|
+
`DEFAULT_PANO_SCENE_MENU_APPEARANCE` exports the defaults (including
|
|
1667
|
+
`padding: "8px 10px"` and `labelAlign: "start"`). For deeper styling,
|
|
1668
|
+
use `className` / `style` against these stable classes:
|
|
1669
|
+
|
|
1670
|
+
- `pano-scene-menu` — root chrome
|
|
1671
|
+
- `pano-scene-menu-body` — arrows + list row/column (owns the strip background)
|
|
1672
|
+
- `pano-scene-menu-list` — scrollable item list
|
|
1673
|
+
- `pano-scene-menu-item` / `pano-scene-menu-thumb` / `pano-scene-menu-label`
|
|
1674
|
+
- `pano-scene-menu-control` / `pano-scene-menu-arrow` / `pano-scene-menu-toggle`
|
|
1675
|
+
|
|
1676
|
+
The root also exposes `data-placement`, `data-align`, `data-orientation`
|
|
1677
|
+
(`horizontal` | `vertical`), `data-expanded`, and `data-collapsible`. The active
|
|
1678
|
+
item has `data-active="true"`.
|
|
1679
|
+
|
|
1214
1680
|
## PanoFilter
|
|
1215
1681
|
|
|
1216
1682
|
Render `PanoFilter` inside `PanoViewer` to color-grade or stylize the panorama
|
|
@@ -1257,7 +1723,7 @@ panorama when a filter is active.
|
|
|
1257
1723
|
|
|
1258
1724
|
## AutoRotate
|
|
1259
1725
|
|
|
1260
|
-
Auto-rotation is off by default. Render `AutoRotate` inside `PanoViewer` to enable it and keep rotation configuration separate from user-input controls. `speed` is measured in degrees per second; use a negative value to rotate left. `acceleration` is measured in degrees per second squared and smoothly ramps from zero to `speed` (default: `18`, so the default speed takes one second to reach). Set it to `0` for an immediate fixed speed. `startDelay` is measured in milliseconds from when `enabled` becomes true. While the user is dragging, or while drag inertia is still settling, rotation pauses and resumes from zero speed automatically.
|
|
1726
|
+
Auto-rotation is off by default. Render `AutoRotate` inside `PanoViewer` to enable it and keep rotation configuration separate from user-input controls. `speed` is measured in degrees per second; use a negative value to rotate left. `acceleration` is measured in degrees per second squared and smoothly ramps from zero to `speed` (default: `18`, so the default speed takes one second to reach). Set it to `0` for an immediate fixed speed. `startDelay` is measured in milliseconds from when `enabled` becomes true (default `2`). While the user is dragging, or while drag inertia is still settling, rotation pauses and resumes from zero speed automatically. When the component is mounted and `enabled` is omitted, it is treated as `true`; omitting the component entirely means no auto-rotation.
|
|
1261
1727
|
|
|
1262
1728
|
```tsx
|
|
1263
1729
|
<PanoViewer style={{ height: 560 }}>
|
|
@@ -1332,6 +1798,22 @@ every other scene plays `default.mp3`.
|
|
|
1332
1798
|
For a sound attached to a point in the panorama that pans with the view, use
|
|
1333
1799
|
`AudioHotspot`, not `BackgroundAudio`.
|
|
1334
1800
|
|
|
1801
|
+
### Props
|
|
1802
|
+
|
|
1803
|
+
- `src` / `sources` — `src` is the shared track, or the fallback when a scene is missing from `sources`. Either value may be a string or a string array (Howler multi-format fallback). `sources` keys are `Scene.id` values.
|
|
1804
|
+
- `sceneId` — required when `sources` is set; omitting it throws.
|
|
1805
|
+
- `playing` / `defaultPlaying` — controlled playback, or uncontrolled initial value (default `false`).
|
|
1806
|
+
- `loop` — default `true`.
|
|
1807
|
+
- `muted` — default `false`.
|
|
1808
|
+
- `volume` — default `1`, range 0–1.
|
|
1809
|
+
- `pauseWhenHidden` — default `true`; pauses while the page is hidden.
|
|
1810
|
+
- `fadeMs` — crossfade milliseconds when the resolved track changes, default `400`; `0` is a hard cut.
|
|
1811
|
+
- `crossOrigin` — `""` | `"anonymous"` | `"use-credentials"`; omit to leave unset.
|
|
1812
|
+
- `onPlaybackStateChange` — `"playing" | "paused" | "ended" | "blocked"`.
|
|
1813
|
+
- `onPlaybackError` — `play()` and other playback failures.
|
|
1814
|
+
- `onEnded` — when the track finishes; uncontrolled mode also sets internal playing to `false`.
|
|
1815
|
+
- `onError` — resource load failure, `{ error }` (distinct from `onPlaybackError`).
|
|
1816
|
+
|
|
1335
1817
|
## Gyro
|
|
1336
1818
|
|
|
1337
1819
|
`Gyro` is an opt-in device-orientation control and is disabled by default.
|
|
@@ -1405,6 +1887,22 @@ require HTTPS. A cross-origin iframe must be permitted by the host page, for
|
|
|
1405
1887
|
example with `allow="gyroscope; accelerometer"` and a compatible
|
|
1406
1888
|
`Permissions-Policy` header.
|
|
1407
1889
|
|
|
1890
|
+
### Props
|
|
1891
|
+
|
|
1892
|
+
- `enabled` — default `false`. On iOS, call `requestPermission()` inside a user gesture before setting `true`.
|
|
1893
|
+
- `camroll` — default `true`.
|
|
1894
|
+
- `absolute` — default `false` (relative mode); `true` follows the compass.
|
|
1895
|
+
- `north` — panorama yaw that represents North, in degrees, default `0`.
|
|
1896
|
+
- `friction` — `0`–`0.99`, default `0`.
|
|
1897
|
+
- `touchMode` — default `"full"`; see values above.
|
|
1898
|
+
- `softstart` — blend seconds when enabling, default `0.5`.
|
|
1899
|
+
- `desktopSupport` — default `false`.
|
|
1900
|
+
- `onAvailable` / `onUnavailable` — when sensor capability detection changes.
|
|
1901
|
+
- `onEnable` / `onDisable` — when a session actually starts / stops.
|
|
1902
|
+
- `onDenied` — when permission is denied (examples often set `enabled` back to `false`).
|
|
1903
|
+
|
|
1904
|
+
These are props on `Gyro` itself. `PanoEvents` `onGyro*` callbacks subscribe to the same event bus for viewer-level listeners.
|
|
1905
|
+
|
|
1408
1906
|
## WebVR
|
|
1409
1907
|
|
|
1410
1908
|
`WebVR` adds immersive viewing to the nearest `PanoViewer`. It prefers an
|
|
@@ -1462,6 +1960,22 @@ DOM overlays such as iframe hotspots, tooltips, and video controls are not
|
|
|
1462
1960
|
visible inside an immersive headset session; mesh-based hotspots remain
|
|
1463
1961
|
interactive through the default WebXR controller rays.
|
|
1464
1962
|
|
|
1963
|
+
### Props
|
|
1964
|
+
|
|
1965
|
+
- `chrome` — default `true` mounts Enter / Exit / Setup UI. An appearance object may set `background` (default `rgba(16, 24, 32, 0.78)`), `color` (`#ffffff`), `accent` (`#7dd3fc`), `borderRadius` (`6`), `fontSize` (`14`), and optional `setupTitle`.
|
|
1966
|
+
- `mobileVr` — default `true`.
|
|
1967
|
+
- `desktopSupport` — default `false` (independent of the same-named `Gyro` prop).
|
|
1968
|
+
- `fakeSupport` — default `true` for mouse-driven desktop simulation.
|
|
1969
|
+
- `screensize` — `"auto"` (factory / omitted storage default) or inches; numbers are clamped to **3–20** with one decimal place.
|
|
1970
|
+
- `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?, label?, id? }`.
|
|
1971
|
+
- `cursor` — default `true`; `false` hides the center reticle.
|
|
1972
|
+
- `cursorDwellMs` — default `1500`; `0` disables auto-click.
|
|
1973
|
+
- `wakelock` / `mousePointerLock` — both default to `true`.
|
|
1974
|
+
- `onAvailable` / `onUnavailable` — VR capability changes.
|
|
1975
|
+
- `onEnterVR` / `onExitVR` — mode `"webxr" | "mobilevr" | "fake"`.
|
|
1976
|
+
- `onDenied` — permission or session denial, optional `error`.
|
|
1977
|
+
- `onUnknownDevice` — when screen / lens parameters cannot be identified.
|
|
1978
|
+
|
|
1465
1979
|
## Mouse, touch, and keyboard controls
|
|
1466
1980
|
|
|
1467
1981
|
`PanoViewer` enables `MouseControls`, `TouchControls`, and `KeyboardControls` by default. Configure shared options through `controls`; render a control component only to override that channel's properties. A child instance replaces the default — you do not need to set the channel to `false` first. Set `controls.mouse` / `touch` / `keyboard` to `false` to turn a channel off.
|
|
@@ -1485,15 +1999,36 @@ interactive through the default WebXR controller rays.
|
|
|
1485
1999
|
|
|
1486
2000
|
### MouseControls
|
|
1487
2001
|
|
|
1488
|
-
|
|
2002
|
+
Override the default channel through `controls.mouse` or by rendering `<MouseControls />` as a child. A child instance does **not** inherit `controls.fovSpeed`; pass `fovSpeed` on the component when you need the cap.
|
|
2003
|
+
|
|
2004
|
+
- `enabled` — default `true`.
|
|
2005
|
+
- `rotateSpeed` — default `0.35` (scales pointer displacement; `0` disables rotate).
|
|
2006
|
+
- `zoomSpeed` — default `0.08`; each wheel step is `deltaY * zoomSpeed`.
|
|
2007
|
+
- `wheel` — default `true`.
|
|
2008
|
+
- `invert` — default `false`; may override shared `controls.invert`.
|
|
2009
|
+
- `buttons` — default `["left"]`; values `"left" | "middle" | "right"`.
|
|
2010
|
+
- `fovSpeed` — omit for no cap; when set, clamps each wheel step to `±fovSpeed` (not a per-second rate).
|
|
1489
2011
|
|
|
1490
2012
|
### TouchControls
|
|
1491
2013
|
|
|
1492
|
-
One-finger drag and optional two-finger pinch zoom
|
|
2014
|
+
One-finger drag and optional two-finger pinch zoom. Override through `controls.touch` or by rendering `<TouchControls />`.
|
|
2015
|
+
|
|
2016
|
+
- `enabled` — default `true`.
|
|
2017
|
+
- `rotateSpeed` — default `0.35`, same meaning as mouse.
|
|
2018
|
+
- `invert` — default `false`.
|
|
2019
|
+
- `pinchZoom` — default `true`. Pinch changes FOV from the finger-distance ratio and is not limited by `fovSpeed`.
|
|
1493
2020
|
|
|
1494
2021
|
### KeyboardControls
|
|
1495
2022
|
|
|
1496
|
-
Hold arrows (or custom bindings) for continuous look / FOV
|
|
2023
|
+
Hold arrows (or custom bindings) for continuous look / FOV. Override through `controls.keyboard` or by rendering `<KeyboardControls />`. Child instances likewise do not inherit `controls.fovSpeed`.
|
|
2024
|
+
|
|
2025
|
+
- `enabled` — default `true`.
|
|
2026
|
+
- `rotateSpeed` / `zoomSpeed` — default `60` / `30` (degrees per second).
|
|
2027
|
+
- `shiftMultiplier` — default `3`.
|
|
2028
|
+
- `invert` — default `false`; flips up/down only.
|
|
2029
|
+
- `fovSpeed` — omit for no cap; when set, limits with `min(zoom, fovSpeed * dt)`.
|
|
2030
|
+
- `keys` — replaces an action wholesale when that key is present; omitted actions keep defaults; an empty array unbinds the action. Matching compares both `event.key` and `event.code` (lowercased).
|
|
2031
|
+
- `onPreviousScene` / `onNextScene` — keys still `preventDefault` when bound; without a callback the scene does not change.
|
|
1497
2032
|
|
|
1498
2033
|
Shared all-mode options on `controls`: `enabled`, `invert` (drag direction for mouse/touch), `bouncingLimits`, `fovSpeed`, `frictionStop` (default `0.01`), plus existing damping / inertia.
|
|
1499
2034
|
|
|
@@ -1546,7 +2081,7 @@ export function KeyboardExample({ scenes }: { scenes: Scene[] }) {
|
|
|
1546
2081
|
|
|
1547
2082
|
Hold movement and zoom keys for continuous motion in degrees per second (`rotateSpeed` / `zoomSpeed`). Hold Shift to multiply those rates (`shiftMultiplier`, default `3`). Scene and reset bindings fire once per press. The canvas must be focused to receive keys (click the viewer first).
|
|
1548
2083
|
|
|
1549
|
-
Default bindings: arrows and WASD for look
|
|
2084
|
+
Default bindings: arrows and WASD for look; zoom in with `+` and `=`, zoom out with `-` and `_`; `0` resets; `[` / `PageUp` and `]` / `PageDown` for previous/next scene (without callbacks the scene does not change, but the key may still be intercepted). The same child-override pattern works with `MouseControls` and `TouchControls`. Set `controls.mouse={false}` / `controls.touch={false}` / `controls.keyboard={false}` only when you want that channel off.
|
|
1550
2085
|
|
|
1551
2086
|
## Panorama coordinate events
|
|
1552
2087
|
|
|
@@ -1622,25 +2157,28 @@ import {
|
|
|
1622
2157
|
|
|
1623
2158
|
Supported callbacks (krpano names in parentheses where applicable):
|
|
1624
2159
|
|
|
1625
|
-
- View: `onViewChange` (`onviewchange`), `onViewSettled
|
|
1626
|
-
-
|
|
1627
|
-
- `
|
|
2160
|
+
- View: `onViewChange` (`onviewchange`), `onViewSettled` — both receive `{ yaw, pitch, fov }`. `viewchange` fires while the view moves; `viewsettled` fires when inertia/tween first stops or a commanded animation ends.
|
|
2161
|
+
- `onViewInteractionStart` / `onViewInteractionEnd` — `{ source }` with `source` `"mouse" | "touch" | "keyboard"`.
|
|
2162
|
+
- Pointer on the panorama shell (not hotspots): `onClick`, `onDoubleClick`, `onPointerDown`, `onPointerUp`, `onPointerMove`, `onContextMenu` — `PanoramaPointerEvent` with `position`, `nativeEvent`, `button`, `buttons`, `clientX`, `clientY`, and optional `pointerId`.
|
|
2163
|
+
- `onWheel` — mouse wheel only; touch pinch does not synthesize a wheel event. Payload `{ delta, nativeEvent }`; `delta` matches the sign of `WheelEvent.deltaY`.
|
|
1628
2164
|
- Idle: `onIdle` / `onIdleEnd` with per-instance `idleTime` (ms, default `2000`)
|
|
1629
|
-
- Fullscreen: `onEnterFullscreen` / `onExitFullscreen`
|
|
1630
|
-
- `onResize` —
|
|
1631
|
-
- Auto-rotate: `onAutoRotateStart` / `onAutoRotateStop` / `onAutoRotateOneRound`
|
|
1632
|
-
- Gyro: `onGyroAvailable` / `onGyroUnavailable` / `onGyroEnable` /
|
|
1633
|
-
|
|
2165
|
+
- Fullscreen: `onEnterFullscreen` / `onExitFullscreen` (no payload)
|
|
2166
|
+
- `onResize` — `{ width, height }` from a content-box `ResizeObserver`
|
|
2167
|
+
- Auto-rotate: `onAutoRotateStart` / `onAutoRotateStop` / `onAutoRotateOneRound` (no payload)
|
|
2168
|
+
- Gyro: `onGyroAvailable` / `onGyroUnavailable` / `onGyroEnable` / `onGyroDisable` / `onGyroDenied` (no payload)
|
|
2169
|
+
- VR: `onVRAvailable` / `onVRUnavailable` / `onVREnter` / `onVRExit` / `onVRDenied` / `onVRUnknownDevice`. Enter/exit carry mode `"webxr" | "mobilevr" | "fake"`; `onVRDenied` may include an optional `error`.
|
|
1634
2170
|
|
|
1635
2171
|
`PanoViewer`'s existing `onViewChange` / `onPanoramaClick` / `onPanoramaDoubleClick` /
|
|
1636
2172
|
`onPanoramaPointerMove` props remain supported and share the same event bus.
|
|
2173
|
+
`usePanoEvents` accepts the same props as `<PanoEvents>` and returns nothing.
|
|
1637
2174
|
|
|
1638
2175
|
Resource callbacks belong to each source: `Sphere` provides `onLoad`,
|
|
1639
2176
|
`onError`, and `onPreviewError`; `Tile` / `SphereTile` provide `onLoadProgress`,
|
|
1640
2177
|
`onTileError`, and `onPreviewError`. Sources report initial render readiness
|
|
1641
2178
|
through `onReady`. Use `Scenes` callbacks `onSceneReady`, `onTransitionEnd`,
|
|
1642
|
-
and `onTransitionError` for scene state. This package does not
|
|
1643
|
-
|
|
2179
|
+
and `onTransitionError` for scene state. This package does not mirror krpano
|
|
2180
|
+
XML-loading or frame-render events one-to-one; use the `onVR*` callbacks above
|
|
2181
|
+
for VR lifecycle.
|
|
1644
2182
|
|
|
1645
2183
|
## PanoContextMenu
|
|
1646
2184
|
|
|
@@ -1734,18 +2272,32 @@ reimplementing fullscreen enter/exit state:
|
|
|
1734
2272
|
</PanoViewer>
|
|
1735
2273
|
```
|
|
1736
2274
|
|
|
1737
|
-
Override presentation on a preset with `{ preset: "fullscreen", label: "…" }
|
|
2275
|
+
Override presentation on a preset with `{ preset: "fullscreen", label: "…" }`;
|
|
2276
|
+
presets may also override `id`, `className`, `style`, `disabled`, and `hidden`.
|
|
1738
2277
|
For fully manual assembly outside the prop (for example a custom
|
|
1739
2278
|
`PanoContextMenu` child), call `createPanoContextMenuPresets({ reset,
|
|
1740
2279
|
toggleFullscreen, isFullscreen })` and use `.resetView` / `.fullscreen` /
|
|
1741
2280
|
`.defaults`.
|
|
1742
2281
|
|
|
1743
|
-
Concrete items may use an `icon` React node or an `image` URL
|
|
1744
|
-
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
2282
|
+
Concrete items may use an `icon` React node or an `image` URL (optional
|
|
2283
|
+
`imageAlt`). Items may also set `disabled`, `hidden`, `className`, and `style`.
|
|
2284
|
+
`onSelect` receives `position`, `view`, `nativeEvent`, and `close()`. Separators
|
|
2285
|
+
always come from the `"separator"` preset.
|
|
2286
|
+
|
|
2287
|
+
When `items` is provided, `prepend` / `append` are ignored. `children` is a
|
|
2288
|
+
full custom render: `(props: { event, view, close }) => ReactNode`; it takes
|
|
2289
|
+
precedence over `items` while open/close/positioning stay with the component.
|
|
2290
|
+
`className`, `style`, `itemClassName`, and `separatorClassName` are also accepted.
|
|
2291
|
+
|
|
2292
|
+
Appearance fields and defaults: `background` `#161616`, `color` `#f4f4f4`,
|
|
2293
|
+
`border` `"1px solid #2e2e2e"`, `borderRadius` `8`,
|
|
2294
|
+
`shadow` `"0 8px 24px rgba(0,0,0,0.45)"`, `opacity` `1` (background only,
|
|
2295
|
+
clamped to `[0.4, 1]`; labels and icons stay opaque), `fontSize` `13`,
|
|
2296
|
+
`minWidth` `180`, `padding` `4`, `iconSize` `16`, `itemPadding` `"6px 10px"`,
|
|
2297
|
+
`itemHoverBackground` `#2a2a2a`, `itemDisabledColor` `#777777`,
|
|
2298
|
+
`separatorColor` `#333333`, `separatorMargin` `"4px 6px"`. For an explicit
|
|
2299
|
+
translucent fill without using `opacity`, you can still pass
|
|
2300
|
+
`background: "rgba(...)"`.
|
|
1749
2301
|
|
|
1750
2302
|
For a fully custom DOM menu, set `contextMenu={false}` and render
|
|
1751
2303
|
`PanoContextMenu` as a child. Do not mount both the default menu and a child
|
|
@@ -1754,15 +2306,34 @@ For a fully custom DOM menu, set `contextMenu={false}` and render
|
|
|
1754
2306
|
|
|
1755
2307
|
## Shared hotspot contract and saved definitions
|
|
1756
2308
|
|
|
1757
|
-
Every hotspot has an `id`, optional
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
|
|
2309
|
+
Every hotspot has an `id`, optional shared fields, and semantic interaction
|
|
2310
|
+
callbacks. Point hotspots additionally use a controlled `position`, angular
|
|
2311
|
+
`width`/`height`, hotspot mode, and the zoom options described below.
|
|
2312
|
+
`interactive={false}` keeps a hotspot visible while letting a drawing tool
|
|
2313
|
+
receive the panorama pointer events beneath it. `pointerEvents="none"` ignores
|
|
2314
|
+
mouse, touch, and pen so those events pass through to the panorama even when
|
|
2315
|
+
the hotspot stays keyboard-interactive. Interactive hotspots set the canvas
|
|
2316
|
+
cursor to `pointer` while hovered, or to the hotspot's `cursor` prop when that
|
|
2317
|
+
is set.
|
|
2318
|
+
|
|
2319
|
+
Shared field defaults and usage:
|
|
2320
|
+
|
|
2321
|
+
- `visible` — default `true`.
|
|
2322
|
+
- `renderOrder` — default `0` for point hotspots; `10` for polygons / polylines.
|
|
2323
|
+
- `opacity` — default `1`; applies to point-hotspot textures / iframes.
|
|
2324
|
+
- `selected` — default `false`; when `true`, draws an ~3px selection outline (common in authoring).
|
|
2325
|
+
- `interactive` — default `true`; `pointerEvents` defaults to `"auto"`.
|
|
2326
|
+
- `onHoverChange(hovered, event)` — boolean hover state, then the interaction event.
|
|
2327
|
+
- `onClick` — position and input source (`"pointer"` or `"keyboard"`).
|
|
2328
|
+
- Point-hotspot drag: with `draggable`, `onDragStart` fires at the start,
|
|
2329
|
+
`onPositionChange` reports the new position while moving (the host must write
|
|
2330
|
+
it back), and `onDragEnd` fires on release. Polygon / polyline callbacks with
|
|
2331
|
+
the same names carry vertex sets — see those sections.
|
|
2332
|
+
|
|
2333
|
+
Default angular size when `width` / `height` are omitted: `ImageHotspot` /
|
|
2334
|
+
`SequenceHotspot` `12×8`; canvas `GraphicHotspot` `10×10` (`12×8` when
|
|
2335
|
+
`kind: "svg"` loads as an image); `VideoHotspot` `16×9`; `AudioHotspot` `8×8`;
|
|
2336
|
+
`TextHotspot` `16×6`; `IframeHotspot` `18×12`.
|
|
1766
2337
|
|
|
1767
2338
|
```tsx
|
|
1768
2339
|
<TextHotspot
|
|
@@ -1773,11 +2344,10 @@ the hotspot's `cursor` prop when that is set.
|
|
|
1773
2344
|
/>
|
|
1774
2345
|
```
|
|
1775
2346
|
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
controlled `vertices` through `onVerticesChange`.
|
|
2347
|
+
Point hotspots can use `draggable`, `onDragStart`, `onPositionChange`, and
|
|
2348
|
+
`onDragEnd`; the host must write the reported position back to its state.
|
|
2349
|
+
Polygon and polyline hotspots report controlled `vertices` through
|
|
2350
|
+
`onVerticesChange`.
|
|
1781
2351
|
|
|
1782
2352
|
Every hotspot can show a DOM tooltip at its anchor. Pass a string or
|
|
1783
2353
|
`{ text?, image?, imageAlt? }` as `tooltip`. The default `tooltipTrigger` is
|
|
@@ -1855,7 +2425,10 @@ const hotspots: HotspotDefinition[] = [
|
|
|
1855
2425
|
|
|
1856
2426
|
Definitions intentionally contain data only. Render them with a switch in the
|
|
1857
2427
|
host, where application-specific click actions, controlled media state, and
|
|
1858
|
-
error reporting belong.
|
|
2428
|
+
error reporting belong. Project JSON `label` is not a component prop;
|
|
2429
|
+
`ProjectScenes` uses `hotspot.label || hotspot.id` as the accessible name
|
|
2430
|
+
(`ariaLabel`). `selected` exists only on runtime props and is not part of the
|
|
2431
|
+
saved definition.
|
|
1859
2432
|
|
|
1860
2433
|
## ImageHotspot
|
|
1861
2434
|
|
|
@@ -1885,15 +2458,19 @@ move a selected hotspot. Clickable hotspots need `ariaLabel`: PanoViewer creates
|
|
|
1885
2458
|
an internal semantic control for Tab, Enter, and Space activation, with a
|
|
1886
2459
|
visible WebGL focus outline.
|
|
1887
2460
|
|
|
1888
|
-
`ImageHotspot` calls `onLoad(texture)` and `onError(error)`.
|
|
2461
|
+
`ImageHotspot` calls `onLoad(texture)` and `onError(error)`. When size is
|
|
2462
|
+
omitted, defaults are `width={12}` and `height={8}`.
|
|
1889
2463
|
|
|
1890
2464
|
## GraphicHotspot
|
|
1891
2465
|
|
|
1892
2466
|
`GraphicHotspot` accepts built-in `circle`, `triangle`, `diamond`, `star`,
|
|
1893
2467
|
`arrow`, `rectangle`, and `ring` graphics, an SVG URL, or safe SVG path data
|
|
1894
2468
|
with an explicit viewBox. Built-in graphics support `fill`, `stroke`, and
|
|
1895
|
-
`strokeWidth`; rectangles support a relative `cornerRadius` from `0` to `0.5
|
|
1896
|
-
|
|
2469
|
+
`strokeWidth`; rectangles support a relative `cornerRadius` from `0` to `0.5`;
|
|
2470
|
+
rings support `innerRadius` (fraction of the outer radius, default `0.58`).
|
|
2471
|
+
Canvas `strokeWidth` is line width on the **512** texture (default `8`), not
|
|
2472
|
+
the CSS-pixel stroke used by polygons. `kind: "path"` needs `path` and
|
|
2473
|
+
`viewBox: [x, y, width, height]`.
|
|
1897
2474
|
|
|
1898
2475
|
```tsx
|
|
1899
2476
|
import { GraphicHotspot } from "@ericchen1990/pano-view";
|
|
@@ -1943,7 +2520,8 @@ relative proportion, not a texture-pixel measurement.
|
|
|
1943
2520
|
`GraphicHotspot` uses the same `onLoad(texture)` and `onError(error)` callbacks
|
|
1944
2521
|
as `ImageHotspot`. Built-in paths are rasterized locally; only an SVG URL
|
|
1945
2522
|
or SVG `path` data plus an explicit `viewBox` is accepted, never arbitrary SVG
|
|
1946
|
-
markup.
|
|
2523
|
+
markup. Canvas graphics default to angular size `10×10`; `kind: "svg"` image
|
|
2524
|
+
loads default to `12×8`.
|
|
1947
2525
|
|
|
1948
2526
|
## Hotspot mode and zoom behaviour
|
|
1949
2527
|
|
|
@@ -2018,6 +2596,13 @@ stroke. Dragging moves all vertices together while keeping yaw normalized and
|
|
|
2018
2596
|
pitch clamped. Individual vertex editing is provided by the authoring
|
|
2019
2597
|
workflow, not the runtime hotspot component.
|
|
2020
2598
|
|
|
2599
|
+
Defaults: `fill` `#df6b42`, `fillOpacity` `0.34`, `stroke` `#f5fbfc`,
|
|
2600
|
+
`strokeWidth` `2`, `strokeOpacity` `1`, `renderOrder` `10`. `onDragStart` /
|
|
2601
|
+
`onDragEnd` / `onVerticesChange` carry vertex events (`vertices`,
|
|
2602
|
+
`startVertices`, `position`, `startPosition`), not point-hotspot drag events.
|
|
2603
|
+
`onInvalid` codes are `too_few_vertices`, `self_intersection`,
|
|
2604
|
+
`contains_pole`, and `outside_hemisphere`.
|
|
2605
|
+
|
|
2021
2606
|
Set `fillOpacity={0}` for a closed outline-only polygon. The fill and outline
|
|
2022
2607
|
share the same spherical edge sampling so the outline follows the fill boundary.
|
|
2023
2608
|
|
|
@@ -2046,7 +2631,10 @@ import { PolylineHotspot } from "@ericchen1990/pano-view";
|
|
|
2046
2631
|
/>;
|
|
2047
2632
|
```
|
|
2048
2633
|
|
|
2049
|
-
`onInvalid` reports the only invalid runtime shape: fewer than two vertices
|
|
2634
|
+
`onInvalid` reports the only invalid runtime shape: fewer than two vertices
|
|
2635
|
+
(`code: "too_few_vertices"`). Defaults: `stroke` `#f5fbfc`, `strokeWidth` `2`,
|
|
2636
|
+
`strokeOpacity` `1`, `renderOrder` `10`. Drag callbacks likewise carry a
|
|
2637
|
+
vertex set (`PolylineVerticesChangeEvent`).
|
|
2050
2638
|
|
|
2051
2639
|
## SequenceHotspot
|
|
2052
2640
|
|
|
@@ -2054,7 +2642,8 @@ import { PolylineHotspot } from "@ericchen1990/pano-view";
|
|
|
2054
2642
|
frames in a vertical or horizontal strip. This matches the common krpano
|
|
2055
2643
|
animated-hotspot format. Give `width` and `height` the aspect ratio of a single
|
|
2056
2644
|
frame, rather than the full strip. `playing` is controlled by the host; `fps`
|
|
2057
|
-
defaults to `12` and `
|
|
2645
|
+
defaults to `12`, `loop` to `true`, and `frameDirection` to `"vertical"`.
|
|
2646
|
+
Omitted size defaults to `12×8`.
|
|
2058
2647
|
|
|
2059
2648
|
```tsx
|
|
2060
2649
|
import { SequenceHotspot } from "@ericchen1990/pano-view";
|
|
@@ -2076,7 +2665,10 @@ import { SequenceHotspot } from "@ericchen1990/pano-view";
|
|
|
2076
2665
|
/>;
|
|
2077
2666
|
```
|
|
2078
2667
|
|
|
2079
|
-
Sequence loading uses `onLoadProgress`
|
|
2668
|
+
Sequence loading uses `onLoadProgress` (`{ id, total, loaded, failed }`) and
|
|
2669
|
+
`onError` (`{ id, src, error }`). Also available: `onFrameChange`
|
|
2670
|
+
(`{ id, index, src }`) and `onPlaybackStateChange`
|
|
2671
|
+
(`"playing" | "paused" | "ended"`, no `"blocked"`). The component does not
|
|
2080
2672
|
change `playing` by itself: update that prop in response to your UI, click
|
|
2081
2673
|
handler, `onEnded`, or playback error.
|
|
2082
2674
|
|
|
@@ -2088,7 +2680,9 @@ state. On source change or unmount, the element is paused and its texture is
|
|
|
2088
2680
|
released. `playsInline` defaults to `true` and uses the same inline-playback
|
|
2089
2681
|
attributes as `PanoVideo`. The element is mounted inside the viewer, hidden,
|
|
2090
2682
|
so mobile browsers keep it in the page. `muted` defaults to `true`, `loop` to
|
|
2091
|
-
`false`,
|
|
2683
|
+
`false`, `preload` to `"metadata"` (also `"none"` | `"auto"`), and `volume` to
|
|
2684
|
+
`1` (0–1). Omitted size defaults to `16×9`. Cross-origin media may set
|
|
2685
|
+
`crossOrigin` (`""` | `"anonymous"` | `"use-credentials"`).
|
|
2092
2686
|
|
|
2093
2687
|
```tsx
|
|
2094
2688
|
import { VideoHotspot } from "@ericchen1990/pano-view";
|
|
@@ -2117,9 +2711,10 @@ Browsers can reject unmuted or otherwise non-gesture playback. In that case
|
|
|
2117
2711
|
the browser error; retain control of `playing` in the host and offer an
|
|
2118
2712
|
explicit user action.
|
|
2119
2713
|
|
|
2120
|
-
Video reports media and poster failures through `onError
|
|
2121
|
-
`SequenceHotspot`, it does not change
|
|
2122
|
-
changes, unmounts, and React StrictMode
|
|
2714
|
+
Video reports media and poster failures through `onError`, including
|
|
2715
|
+
`source: "poster" | "video"`. Like `SequenceHotspot`, it does not change
|
|
2716
|
+
`playing` by itself. This keeps source changes, unmounts, and React StrictMode
|
|
2717
|
+
lifecycles deterministic.
|
|
2123
2718
|
|
|
2124
2719
|
## AudioHotspot
|
|
2125
2720
|
|
|
@@ -2170,19 +2765,26 @@ import { AudioHotspot } from "@ericchen1990/pano-view";
|
|
|
2170
2765
|
```
|
|
2171
2766
|
|
|
2172
2767
|
Like the other media hotspots, `AudioHotspot` does not change `playing` by
|
|
2173
|
-
itself.
|
|
2768
|
+
itself. Omitted marker size defaults to `8×8`. `crossOrigin` is also accepted;
|
|
2769
|
+
load failures go to `onError({ id, error })`, distinct from `onPlaybackError`.
|
|
2174
2770
|
|
|
2175
2771
|
## TextHotspot
|
|
2176
2772
|
|
|
2177
2773
|
`TextHotspot` rasterizes plain text onto a canvas texture. It does not accept
|
|
2178
2774
|
HTML or Markdown. `\n` is a hard line break; `whiteSpace="normal"` (default)
|
|
2179
|
-
wraps words to the hotspot's angular width
|
|
2180
|
-
the other point hotspots
|
|
2181
|
-
|
|
2182
|
-
|
|
2183
|
-
|
|
2184
|
-
`
|
|
2185
|
-
|
|
2775
|
+
wraps words to the hotspot's angular width; `"nowrap"` is also accepted.
|
|
2776
|
+
Dimensions stay in degrees like the other point hotspots; the default panel is
|
|
2777
|
+
`16×6`. Typography uses the same names as CSS (`fontFamily`, `fontSize`,
|
|
2778
|
+
`fontWeight`, `fontStyle`) but is painted onto the canvas, not applied as DOM
|
|
2779
|
+
style. `fontSize` is canvas pixels (default 96) and does not scale with panel
|
|
2780
|
+
height. `fontWeight` defaults to `600` and `fontStyle` to `"normal"`. `align`
|
|
2781
|
+
defaults to `"center"` (`left` | `center` | `right`); `verticalAlign` defaults
|
|
2782
|
+
to `"middle"` (`top` | `middle` | `bottom`). `color` defaults to `#f8fafc`;
|
|
2783
|
+
`background` defaults to `#111827` (empty string skips the panel fill);
|
|
2784
|
+
`backgroundOpacity` defaults to `0.72`. `padding` defaults to `0.08` and
|
|
2785
|
+
`borderRadius` to `0.08` (fractions of the shorter side, `0`–`0.5`).
|
|
2786
|
+
`lineHeight` defaults to `1.25` (multiple of font size). `stroke` /
|
|
2787
|
+
`strokeWidth` draw an outline when `strokeWidth > 0` (default width `0`).
|
|
2186
2788
|
|
|
2187
2789
|
```tsx
|
|
2188
2790
|
import { TextHotspot } from "@ericchen1990/pano-view";
|
|
@@ -2241,8 +2843,10 @@ interactive and blocks dragging through the iframe. A hotspot
|
|
|
2241
2843
|
`pointerEvents="none"` also forces the iframe overlay not to capture pointer
|
|
2242
2844
|
events. `sandbox` defaults to
|
|
2243
2845
|
`allow-scripts allow-popups allow-forms` without `allow-same-origin`.
|
|
2244
|
-
`referrerPolicy` defaults to `strict-origin-when-cross-origin`.
|
|
2245
|
-
|
|
2846
|
+
`referrerPolicy` defaults to `strict-origin-when-cross-origin`. Also accepted:
|
|
2847
|
+
`title` (falls back to `ariaLabel`), `allow`, `loading` (`"eager"` | `"lazy"`),
|
|
2848
|
+
`background`, and `onLoad` / `onError`. Omitted size defaults to `18×12`. Prefer
|
|
2849
|
+
a same-origin document; many third-party sites send `X-Frame-Options` and will
|
|
2246
2850
|
not load.
|
|
2247
2851
|
|
|
2248
2852
|
## Next.js and SSR
|