@ericchen1990/pano-view 2.2.2 → 2.3.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 +119 -2
- package/README.zh-CN.md +85 -1
- package/dist/index.cjs +3476 -560
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.mts +76 -2
- package/dist/index.d.ts +76 -2
- package/dist/index.mjs +3267 -334
- package/dist/index.mjs.map +1 -1
- package/package.json +4 -2
package/README.md
CHANGED
|
@@ -81,6 +81,7 @@ the stylesheet, the overlays still mount, but they render unstyled.
|
|
|
81
81
|
- [`PanoVideo`](#panovideo) — 2:1 equirectangular video on the panorama sphere
|
|
82
82
|
- [`Tile`](#tile) — krpano-style multires cube tiles
|
|
83
83
|
- [`Scenes`](#scenes) — controlled multi-scene transitions
|
|
84
|
+
- [`BackgroundAudio`](#backgroundaudio) — one shared track, or a different track per scene
|
|
84
85
|
- [`PanoFilter`](#panofilter) — color and artistic filters on the panorama source
|
|
85
86
|
|
|
86
87
|
### Controls
|
|
@@ -104,6 +105,7 @@ the stylesheet, the overlays still mount, but they render unstyled.
|
|
|
104
105
|
- [`GraphicHotspot`](#graphichotspot) — built-in shapes or SVG at a position
|
|
105
106
|
- [`SequenceHotspot`](#sequencehotspot) — sprite-sheet animation
|
|
106
107
|
- [`VideoHotspot`](#videohotspot) — HTML video texture
|
|
108
|
+
- [`AudioHotspot`](#audiohotspot) — directional sound at a spherical position
|
|
107
109
|
- [`TextHotspot`](#texthotspot) — rasterized plain text at a position
|
|
108
110
|
- [`IframeHotspot`](#iframehotspot) — embedded document overlay at a position
|
|
109
111
|
- [`PolygonHotspot`](#polygonhotspot) — closed spherical area
|
|
@@ -500,6 +502,71 @@ Auto-rotation is off by default. Render `AutoRotate` inside `PanoViewer` to enab
|
|
|
500
502
|
</PanoViewer>
|
|
501
503
|
```
|
|
502
504
|
|
|
505
|
+
## BackgroundAudio
|
|
506
|
+
|
|
507
|
+
`BackgroundAudio` plays background music or ambience for the viewer. It is not
|
|
508
|
+
a hotspot: there is no spherical position, no marker, and volume does not
|
|
509
|
+
follow the camera. Render it as a child of `PanoViewer`, **alongside** `Scenes`,
|
|
510
|
+
`Sphere`, or `Tile`. Do not put it in `Scenes`' `renderHotspots` — hotspots
|
|
511
|
+
unmount during scene transitions and would stop the audio.
|
|
512
|
+
|
|
513
|
+
`playing` is controlled by the host; the component does not change it. `loop`
|
|
514
|
+
defaults to `true`. Browsers may block autoplay; `onPlaybackStateChange` then
|
|
515
|
+
receives `"blocked"`, and the component retries `play()` after the next click
|
|
516
|
+
or keypress while `playing` remains true.
|
|
517
|
+
|
|
518
|
+
Pick one of the two patterns.
|
|
519
|
+
|
|
520
|
+
**One track for every scene** — pass `src` only; omit `sources`. Scene changes
|
|
521
|
+
do not restart the audio.
|
|
522
|
+
|
|
523
|
+
```tsx
|
|
524
|
+
import { BackgroundAudio, PanoViewer, Scenes } from "@ericchen1990/pano-view";
|
|
525
|
+
|
|
526
|
+
<PanoViewer style={{ height: 560 }}>
|
|
527
|
+
<BackgroundAudio src="/bgm/tour.mp3" playing={isBgmPlaying} />
|
|
528
|
+
<Scenes scenes={scenes} activeSceneId={activeSceneId} />
|
|
529
|
+
</PanoViewer>;
|
|
530
|
+
```
|
|
531
|
+
|
|
532
|
+
**A different track per scene** — pass `sources` (keys are `Scene.id`) and the
|
|
533
|
+
current `sceneId`. Omitting `sceneId` throws. When the file changes, playback
|
|
534
|
+
crossfades over `fadeMs` (default 400 ms; `0` is a hard cut). When two scenes
|
|
535
|
+
point at the same file, playback continues without restarting.
|
|
536
|
+
|
|
537
|
+
```tsx
|
|
538
|
+
<PanoViewer style={{ height: 560 }}>
|
|
539
|
+
<BackgroundAudio
|
|
540
|
+
sources={{
|
|
541
|
+
lobby: "/bgm/lobby.mp3",
|
|
542
|
+
terrace: "/bgm/terrace.mp3",
|
|
543
|
+
}}
|
|
544
|
+
sceneId={activeSceneId}
|
|
545
|
+
playing={isBgmPlaying}
|
|
546
|
+
/>
|
|
547
|
+
<Scenes scenes={scenes} activeSceneId={activeSceneId} />
|
|
548
|
+
</PanoViewer>;
|
|
549
|
+
```
|
|
550
|
+
|
|
551
|
+
Optional: also pass `src` as the default for scenes missing from `sources`. To
|
|
552
|
+
silence a scene, set that id to `""` (this overrides `src`; it does not fall
|
|
553
|
+
back to the default track).
|
|
554
|
+
|
|
555
|
+
```tsx
|
|
556
|
+
<BackgroundAudio
|
|
557
|
+
src="/bgm/default.mp3"
|
|
558
|
+
sources={{ lobby: "/bgm/courtyard.mp3", terrace: "" }}
|
|
559
|
+
sceneId={activeSceneId}
|
|
560
|
+
playing={isBgmPlaying}
|
|
561
|
+
/>;
|
|
562
|
+
```
|
|
563
|
+
|
|
564
|
+
In that example, `lobby` plays the courtyard clip, `terrace` is silent, and
|
|
565
|
+
every other scene plays `default.mp3`.
|
|
566
|
+
|
|
567
|
+
For a sound attached to a point in the panorama that pans with the view, use
|
|
568
|
+
`AudioHotspot`, not `BackgroundAudio`.
|
|
569
|
+
|
|
503
570
|
## Gyro
|
|
504
571
|
|
|
505
572
|
`Gyro` is an opt-in device-orientation control and is disabled by default.
|
|
@@ -996,8 +1063,8 @@ color, border, corner radius, shadow, padding, and font size.
|
|
|
996
1063
|
|
|
997
1064
|
For persistence or a host-owned editor, use the exported discriminated
|
|
998
1065
|
`HotspotDefinition` union. Adding a variant is a TypeScript breaking change for
|
|
999
|
-
exhaustive switches; handle `
|
|
1000
|
-
|
|
1066
|
+
exhaustive switches; handle `audio` alongside the existing point and path
|
|
1067
|
+
categories:
|
|
1001
1068
|
|
|
1002
1069
|
```ts
|
|
1003
1070
|
import type { HotspotDefinition } from "@ericchen1990/pano-view";
|
|
@@ -1007,6 +1074,7 @@ const hotspots: HotspotDefinition[] = [
|
|
|
1007
1074
|
{ type: "graphic", id: "marker", position: { yaw: -18, pitch: 9 }, graphic: { kind: "ring" } },
|
|
1008
1075
|
{ type: "sequence", id: "pulse", position: { yaw: -42, pitch: -7 }, src: "/hotspots/pulse.png", frameCount: 20 },
|
|
1009
1076
|
{ type: "video", id: "clip", position: { yaw: 48, pitch: 6 }, src: "/hotspots/clip.webm" },
|
|
1077
|
+
{ type: "audio", id: "fountain", position: { yaw: -30, pitch: -8 }, src: "/hotspots/fountain.mp3", range: 90 },
|
|
1010
1078
|
{ type: "text", id: "caption", position: { yaw: 0, pitch: -16 }, text: "Courtyard overlook" },
|
|
1011
1079
|
{ type: "iframe", id: "guide", position: { yaw: -62, pitch: 4 }, src: "/hotspots/embed.html" },
|
|
1012
1080
|
{ type: "polygon", id: "zone", vertices: [{ yaw: 12, pitch: 4 }, { yaw: 22, pitch: 4 }, { yaw: 18, pitch: 14 }] },
|
|
@@ -1281,6 +1349,55 @@ Video reports media and poster failures through `onError`. Like
|
|
|
1281
1349
|
`SequenceHotspot`, it does not change `playing` by itself. This keeps source
|
|
1282
1350
|
changes, unmounts, and React StrictMode lifecycles deterministic.
|
|
1283
1351
|
|
|
1352
|
+
## AudioHotspot
|
|
1353
|
+
|
|
1354
|
+
`AudioHotspot` plays a directional sound at a spherical position. Stereo
|
|
1355
|
+
panning follows the camera look direction; `range` is the look-away angle in
|
|
1356
|
+
degrees at which volume reaches silence. The default `360` disables look
|
|
1357
|
+
attenuation (stereo pan only). Use a smaller `range` such as `90` for a
|
|
1358
|
+
spot source.
|
|
1359
|
+
|
|
1360
|
+
Playback uses Web Audio via Howler.js (bundled; hosts do not install
|
|
1361
|
+
`howler`). The file must decode before play starts — MP3 is the recommended
|
|
1362
|
+
format. `src` may be a string or an array of fallback URLs. Do not use HTML5
|
|
1363
|
+
streaming (`html5`); it drops stereo panning.
|
|
1364
|
+
|
|
1365
|
+
`playing` is controlled like `VideoHotspot`. `loop` defaults to `false`,
|
|
1366
|
+
`muted` to `false`, `volume` to `1`, and `pauseWhenHidden` to `true` (pause
|
|
1367
|
+
when the tab is hidden, resume if `playing` is still true). A first user
|
|
1368
|
+
gesture unlocks audio; if autoplay is blocked, the hotspot reports
|
|
1369
|
+
`"blocked"` and retries `play()` after the next pointer or key event while
|
|
1370
|
+
`playing` remains true.
|
|
1371
|
+
|
|
1372
|
+
The default marker switches between a built-in **stopped** icon (speaker)
|
|
1373
|
+
and a **playing** icon (speaker plus sound waves). Pass `icon`
|
|
1374
|
+
for a custom stopped image and `playingIcon` for a custom playing image.
|
|
1375
|
+
If only `icon` is set, it is used for both states. `marker={false}` hides
|
|
1376
|
+
the visual for an invisible positional source. For a
|
|
1377
|
+
non-spatial tour or per-scene soundtrack, use [`BackgroundAudio`](#backgroundaudio).
|
|
1378
|
+
|
|
1379
|
+
```tsx
|
|
1380
|
+
import { AudioHotspot } from "@ericchen1990/pano-view";
|
|
1381
|
+
|
|
1382
|
+
<AudioHotspot
|
|
1383
|
+
id="fountain"
|
|
1384
|
+
ariaLabel="Fountain"
|
|
1385
|
+
position={{ yaw: -30, pitch: -8 }}
|
|
1386
|
+
src="/hotspots/fountain.mp3"
|
|
1387
|
+
playing={isFountainPlaying}
|
|
1388
|
+
loop
|
|
1389
|
+
volume={0.8}
|
|
1390
|
+
range={90}
|
|
1391
|
+
onClick={() => setFountainPlaying((playing) => !playing)}
|
|
1392
|
+
onEnded={() => setFountainPlaying(false)}
|
|
1393
|
+
onPlaybackStateChange={(state) => console.log(state)}
|
|
1394
|
+
onPlaybackError={(error) => console.error(error)}
|
|
1395
|
+
/>;
|
|
1396
|
+
```
|
|
1397
|
+
|
|
1398
|
+
Like the other media hotspots, `AudioHotspot` does not change `playing` by
|
|
1399
|
+
itself.
|
|
1400
|
+
|
|
1284
1401
|
## TextHotspot
|
|
1285
1402
|
|
|
1286
1403
|
`TextHotspot` rasterizes plain text onto a canvas texture. It does not accept
|
package/README.zh-CN.md
CHANGED
|
@@ -64,6 +64,7 @@ import "@ericchen1990/pano-view/styles.css";
|
|
|
64
64
|
- [`PanoVideo`](#panovideo) — 全景球面上的 2:1 等距圆柱视频
|
|
65
65
|
- [`Tile`](#tile) — krpano 风格多分辨率立方体 tile
|
|
66
66
|
- [`Scenes`](#scenes) — 受控多场景过渡
|
|
67
|
+
- [`BackgroundAudio`](#backgroundaudio) — 全部场景共用、或按场景切换的背景声
|
|
67
68
|
- [`PanoFilter`](#panofilter) — 对全景源应用色彩与艺术滤镜
|
|
68
69
|
|
|
69
70
|
### 控件
|
|
@@ -87,6 +88,7 @@ import "@ericchen1990/pano-view/styles.css";
|
|
|
87
88
|
- [`GraphicHotspot`](#graphichotspot) — 内置形状或 SVG
|
|
88
89
|
- [`SequenceHotspot`](#sequencehotspot) — 精灵图动画
|
|
89
90
|
- [`VideoHotspot`](#videohotspot) — HTML 视频纹理
|
|
91
|
+
- [`AudioHotspot`](#audiohotspot) — 球面位置上的定向声源
|
|
90
92
|
- [`TextHotspot`](#texthotspot) — 球面位置上的栅格化纯文本
|
|
91
93
|
- [`IframeHotspot`](#iframehotspot) — 球面位置上的嵌入文档叠加层
|
|
92
94
|
- [`PolygonHotspot`](#polygonhotspot) — 闭合球面区域
|
|
@@ -418,6 +420,56 @@ const scenes: Scene[] = [
|
|
|
418
420
|
</PanoViewer>
|
|
419
421
|
```
|
|
420
422
|
|
|
423
|
+
## BackgroundAudio
|
|
424
|
+
|
|
425
|
+
`BackgroundAudio` 在查看器里播放背景音乐或环境声。它不是热点:没有球面位置、没有标记、音量也不随视角变化。必须作为 `PanoViewer` 的子组件,与 `Scenes`、`Sphere` 或 `Tile` **并列**。不要写进 `Scenes` 的 `renderHotspots`:场景过渡时热点会卸载,背景声也会被停掉。
|
|
426
|
+
|
|
427
|
+
`playing` 由宿主控制,组件不会自行改这个值。`loop` 默认 `true`。浏览器可能拦住自动播放;此时 `onPlaybackStateChange` 收到 `"blocked"`,在 `playing` 仍为 true 时会于下一次点击或按键后重试。
|
|
428
|
+
|
|
429
|
+
有两种用法,选一种即可。
|
|
430
|
+
|
|
431
|
+
**全部场景共用一条音轨** — 只传 `src`,不要传 `sources`。切换场景时音频继续播放,不会重头开始。
|
|
432
|
+
|
|
433
|
+
```tsx
|
|
434
|
+
import { BackgroundAudio, PanoViewer, Scenes } from "@ericchen1990/pano-view";
|
|
435
|
+
|
|
436
|
+
<PanoViewer style={{ height: 560 }}>
|
|
437
|
+
<BackgroundAudio src="/bgm/tour.mp3" playing={isBgmPlaying} />
|
|
438
|
+
<Scenes scenes={scenes} activeSceneId={activeSceneId} />
|
|
439
|
+
</PanoViewer>;
|
|
440
|
+
```
|
|
441
|
+
|
|
442
|
+
**每个场景一条音轨** — 传 `sources`(键为 `Scene.id`)和当前的 `sceneId`。省略 `sceneId` 会抛错。切换场景时,若文件变了会按 `fadeMs` 交叉淡化(默认 400 毫秒;`0` 为硬切);若两个场景指向同一文件则继续播,不会重启。
|
|
443
|
+
|
|
444
|
+
```tsx
|
|
445
|
+
<PanoViewer style={{ height: 560 }}>
|
|
446
|
+
<BackgroundAudio
|
|
447
|
+
sources={{
|
|
448
|
+
lobby: "/bgm/lobby.mp3",
|
|
449
|
+
terrace: "/bgm/terrace.mp3",
|
|
450
|
+
}}
|
|
451
|
+
sceneId={activeSceneId}
|
|
452
|
+
playing={isBgmPlaying}
|
|
453
|
+
/>
|
|
454
|
+
<Scenes scenes={scenes} activeSceneId={activeSceneId} />
|
|
455
|
+
</PanoViewer>;
|
|
456
|
+
```
|
|
457
|
+
|
|
458
|
+
可选:同时传 `src` 作为未列出场景的默认音轨。某个场景要静音时,把该 id 写成 `""`(会覆盖 `src`,而不是回退到默认轨)。
|
|
459
|
+
|
|
460
|
+
```tsx
|
|
461
|
+
<BackgroundAudio
|
|
462
|
+
src="/bgm/default.mp3"
|
|
463
|
+
sources={{ lobby: "/bgm/courtyard.mp3", terrace: "" }}
|
|
464
|
+
sceneId={activeSceneId}
|
|
465
|
+
playing={isBgmPlaying}
|
|
466
|
+
/>;
|
|
467
|
+
```
|
|
468
|
+
|
|
469
|
+
上例中:`lobby` 播院子声,`terrace` 静音,其它场景播 `default.mp3`。
|
|
470
|
+
|
|
471
|
+
需要绑在全景某一点、随视角左右移动的声音,请用 `AudioHotspot`,不要用 `BackgroundAudio`。
|
|
472
|
+
|
|
421
473
|
## Gyro
|
|
422
474
|
|
|
423
475
|
`Gyro` 为可选的设备方向控制,默认关闭。在 `PanoViewer` 内渲染并通过 `enabled` 控制。相对模式(默认)将启用时的视图作为起始方向。设 `absolute` 可跟随罗盘朝向,用 `north` 指定指向北方的全景 yaw。
|
|
@@ -818,7 +870,7 @@ import {
|
|
|
818
870
|
/>
|
|
819
871
|
```
|
|
820
872
|
|
|
821
|
-
持久化或宿主编辑器可使用导出的判别联合类型 `HotspotDefinition`。新增变体会破坏 exhaustive switch 的类型兼容;除现有点与路径类别外,还需处理 `
|
|
873
|
+
持久化或宿主编辑器可使用导出的判别联合类型 `HotspotDefinition`。新增变体会破坏 exhaustive switch 的类型兼容;除现有点与路径类别外,还需处理 `audio`:
|
|
822
874
|
|
|
823
875
|
```ts
|
|
824
876
|
import type { HotspotDefinition } from "@ericchen1990/pano-view";
|
|
@@ -828,6 +880,7 @@ const hotspots: HotspotDefinition[] = [
|
|
|
828
880
|
{ type: "graphic", id: "marker", position: { yaw: -18, pitch: 9 }, graphic: { kind: "ring" } },
|
|
829
881
|
{ type: "sequence", id: "pulse", position: { yaw: -42, pitch: -7 }, src: "/hotspots/pulse.png", frameCount: 20 },
|
|
830
882
|
{ type: "video", id: "clip", position: { yaw: 48, pitch: 6 }, src: "/hotspots/clip.webm" },
|
|
883
|
+
{ type: "audio", id: "fountain", position: { yaw: -30, pitch: -8 }, src: "/hotspots/fountain.mp3", range: 90 },
|
|
831
884
|
{ type: "text", id: "caption", position: { yaw: 0, pitch: -16 }, text: "Courtyard overlook" },
|
|
832
885
|
{ type: "iframe", id: "guide", position: { yaw: -62, pitch: 4 }, src: "/hotspots/embed.html" },
|
|
833
886
|
{ type: "polygon", id: "zone", vertices: [{ yaw: 12, pitch: 4 }, { yaw: 22, pitch: 4 }, { yaw: 18, pitch: 14 }] },
|
|
@@ -1047,6 +1100,37 @@ import { VideoHotspot } from "@ericchen1990/pano-view";
|
|
|
1047
1100
|
|
|
1048
1101
|
视频通过 `onError` 报告媒体与 poster 失败。与 `SequenceHotspot` 相同,不会自行改变 `playing`,以保证源变更、卸载与 React StrictMode 生命周期可预测。
|
|
1049
1102
|
|
|
1103
|
+
## AudioHotspot
|
|
1104
|
+
|
|
1105
|
+
`AudioHotspot` 在球面位置播放定向声音。立体声像跟随相机朝向;`range` 为看离声源多少度后音量降到静音。默认 `360` 表示不因视角衰减(仅左右声像)。定点声源可使用较小值,例如 `90`。
|
|
1106
|
+
|
|
1107
|
+
播放走 Howler.js 的 Web Audio(已打进本包,宿主不必安装 `howler`)。文件需先解码再起播,推荐 MP3。`src` 可以是字符串或回退 URL 数组。不要用 HTML5 流式播放,否则会失去立体声像。
|
|
1108
|
+
|
|
1109
|
+
`playing` 与 `VideoHotspot` 一样受控。`loop` 默认 `false`,`muted` 默认 `false`,`volume` 默认 `1`,`pauseWhenHidden` 默认 `true`(标签页隐藏时暂停,仍为 `playing` 则回来后恢复)。首次用户手势会解锁音频;若自动播放被拦,会报告 `"blocked"`,并在 `playing` 仍为 true 时于下一次指针或按键后重试 `play()`。
|
|
1110
|
+
|
|
1111
|
+
默认标记在 **停止**(扬声器)与 **播放**(扬声器 + 声波)之间切换。可用 `icon` 自定义停止图标、`playingIcon` 自定义播放图标。只设 `icon` 时两种状态共用该图。`marker={false}` 则无视觉,仍作为定点声源。非空间的全 tour 或按场景背景声请使用 [`BackgroundAudio`](#backgroundaudio)。
|
|
1112
|
+
|
|
1113
|
+
```tsx
|
|
1114
|
+
import { AudioHotspot } from "@ericchen1990/pano-view";
|
|
1115
|
+
|
|
1116
|
+
<AudioHotspot
|
|
1117
|
+
id="fountain"
|
|
1118
|
+
ariaLabel="Fountain"
|
|
1119
|
+
position={{ yaw: -30, pitch: -8 }}
|
|
1120
|
+
src="/hotspots/fountain.mp3"
|
|
1121
|
+
playing={isFountainPlaying}
|
|
1122
|
+
loop
|
|
1123
|
+
volume={0.8}
|
|
1124
|
+
range={90}
|
|
1125
|
+
onClick={() => setFountainPlaying((playing) => !playing)}
|
|
1126
|
+
onEnded={() => setFountainPlaying(false)}
|
|
1127
|
+
onPlaybackStateChange={(state) => console.log(state)}
|
|
1128
|
+
onPlaybackError={(error) => console.error(error)}
|
|
1129
|
+
/>;
|
|
1130
|
+
```
|
|
1131
|
+
|
|
1132
|
+
与其他媒体热点相同,`AudioHotspot` 不会自行改变 `playing`。
|
|
1133
|
+
|
|
1050
1134
|
## TextHotspot
|
|
1051
1135
|
|
|
1052
1136
|
`TextHotspot` 将纯文本栅格化到 canvas 纹理。不接受 HTML 或 Markdown。`\n` 为硬换行;`whiteSpace="normal"`(默认)在热点角向宽度内自动换行。尺寸与其他点热点一样为度。排版使用与 CSS 相同的名称(`fontFamily`、`fontSize`、`fontWeight`、`fontStyle`),但绘制在 canvas 上,非 DOM 样式。`fontSize` 为 canvas 像素(默认 96),不随面板高度缩放。`fontWeight` 默认 `600`,`fontStyle` 默认 `"normal"`。`padding` 与 `borderRadius` 仍为纹理较短边的比例,故仍随 `width` / `height` 变化。
|