@ericchen1990/pano-view 0.1.5 → 0.2.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 +276 -7
- package/dist/index.cjs +2410 -653
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.mts +395 -12
- package/dist/index.d.ts +395 -12
- package/dist/index.mjs +2423 -671
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -191,7 +191,85 @@ export function ControlledExample() {
|
|
|
191
191
|
}
|
|
192
192
|
```
|
|
193
193
|
|
|
194
|
-
The handle exposes `getView`, `setView`, `reset`, `startAutoRotate`, `stopAutoRotate`, and `toggleFullscreen`.
|
|
194
|
+
The handle exposes `getView`, `setView`, `reset`, `startAutoRotate`, `stopAutoRotate`, and `toggleFullscreen`. Mouse, touch, and keyboard input are enabled by default — you do not need to render control components for ordinary viewing. Tune shared behaviour through `controls` (`inertia`, `invert`, `bouncingLimits`, `fovSpeed`, `frictionStop`, `rotateDamping`, `zoomDamping`, and top-level `rotateSpeed` / `zoomSpeed`). Disable a channel with `controls.mouse` / `touch` / `keyboard` set to `false`, or pass an options object (including `enabled`) to override defaults without mounting a child. The two auto-rotation handle methods remain supported for compatibility; prefer rendering `AutoRotate` for new code. A default context menu (Reset view / Enter fullscreen) is also mounted — see [Context menu](#context-menu).
|
|
195
|
+
|
|
196
|
+
## Mouse, touch, and keyboard controls
|
|
197
|
+
|
|
198
|
+
`PanoView` mounts default `MouseControls`, `TouchControls`, and `KeyboardControls` instances. Configure them through `controls` for most apps; render the components yourself only when you need to replace a channel (for example scene-switch callbacks).
|
|
199
|
+
|
|
200
|
+
```tsx
|
|
201
|
+
<PanoView
|
|
202
|
+
controls={{
|
|
203
|
+
invert: false,
|
|
204
|
+
bouncingLimits: false,
|
|
205
|
+
mouse: { zoomSpeed: 0.12, wheel: true, buttons: ["left"] },
|
|
206
|
+
touch: { pinchZoom: true },
|
|
207
|
+
keyboard: { enabled: true, rotateSpeed: 90 },
|
|
208
|
+
}}
|
|
209
|
+
style={{ height: 560 }}
|
|
210
|
+
>
|
|
211
|
+
<Sphere src="/panoramas/room.webp" />
|
|
212
|
+
</PanoView>
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
**Mouse** (pointer types `mouse` / `pen`): drag look and optional wheel zoom. Defaults: `rotateSpeed` `0.35`, `zoomSpeed` `0.08`, `wheel` `true`, `buttons` `["left"]`.
|
|
216
|
+
|
|
217
|
+
**Touch**: one-finger drag and optional two-finger pinch zoom (`pinchZoom`, default `true`).
|
|
218
|
+
|
|
219
|
+
**Keyboard**: hold arrows (or custom bindings) for continuous look / FOV; `0` resets; optional scene bindings. Defaults: `rotateSpeed` `60`, `zoomSpeed` `30`, `shiftMultiplier` `3`. Set `invert` to flip up/down only.
|
|
220
|
+
|
|
221
|
+
Shared all-mode options on `controls`: `enabled`, `invert` (drag direction for mouse/touch), `bouncingLimits`, `fovSpeed`, `frictionStop` (default `0.01`), plus existing damping / inertia.
|
|
222
|
+
|
|
223
|
+
### Overriding a control channel
|
|
224
|
+
|
|
225
|
+
Set the channel to `false` and render your own component when you need callbacks or fully custom behaviour:
|
|
226
|
+
|
|
227
|
+
```tsx
|
|
228
|
+
import { useState } from "react";
|
|
229
|
+
import {
|
|
230
|
+
KeyboardControls,
|
|
231
|
+
PanoramaScenes,
|
|
232
|
+
PanoView,
|
|
233
|
+
cycleSceneId,
|
|
234
|
+
type PanoramaScene,
|
|
235
|
+
} from "@ericchen1990/pano-view";
|
|
236
|
+
|
|
237
|
+
export function KeyboardExample({ scenes }: { scenes: PanoramaScene[] }) {
|
|
238
|
+
const [activeSceneId, setActiveSceneId] = useState(scenes[0]!.id);
|
|
239
|
+
|
|
240
|
+
return (
|
|
241
|
+
<PanoView controls={{ keyboard: false }} style={{ height: 560 }}>
|
|
242
|
+
<KeyboardControls
|
|
243
|
+
keys={{
|
|
244
|
+
left: ["ArrowLeft", "a"],
|
|
245
|
+
right: ["ArrowRight", "d"],
|
|
246
|
+
up: ["ArrowUp", "w"],
|
|
247
|
+
down: ["ArrowDown", "s"],
|
|
248
|
+
zoomIn: ["=", "+"],
|
|
249
|
+
zoomOut: ["-", "_"],
|
|
250
|
+
previousScene: ["[", "PageUp"],
|
|
251
|
+
nextScene: ["]", "PageDown"],
|
|
252
|
+
}}
|
|
253
|
+
rotateSpeed={60}
|
|
254
|
+
zoomSpeed={30}
|
|
255
|
+
onPreviousScene={() => {
|
|
256
|
+
const next = cycleSceneId(scenes, activeSceneId, -1);
|
|
257
|
+
if (next) setActiveSceneId(next);
|
|
258
|
+
}}
|
|
259
|
+
onNextScene={() => {
|
|
260
|
+
const next = cycleSceneId(scenes, activeSceneId, 1);
|
|
261
|
+
if (next) setActiveSceneId(next);
|
|
262
|
+
}}
|
|
263
|
+
/>
|
|
264
|
+
<PanoramaScenes scenes={scenes} activeSceneId={activeSceneId} />
|
|
265
|
+
</PanoView>
|
|
266
|
+
);
|
|
267
|
+
}
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
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).
|
|
271
|
+
|
|
272
|
+
Default bindings: arrows for look, `+/-` for FOV, `0` for reset, plus `[`/`PageUp` and `]`/`PageDown` for previous/next scene when callbacks are provided. The same override pattern works with `MouseControls` and `TouchControls` (`controls.mouse={false}` / `controls.touch={false}`).
|
|
195
273
|
|
|
196
274
|
## Panorama coordinate events
|
|
197
275
|
|
|
@@ -228,8 +306,167 @@ export function PlacementExample() {
|
|
|
228
306
|
|
|
229
307
|
`normalizePanoPosition`, `normalizePanoYaw`, `clampPanoPitch`, and
|
|
230
308
|
`vector3ToPanoPosition` are also exported for controlled authoring flows. Yaw
|
|
231
|
-
is normalized to `[-180, 180)`. Pitch is clamped to `[-
|
|
232
|
-
|
|
309
|
+
is normalized to `[-180, 180)`. Pitch is clamped to `[-90, 90]`; at either
|
|
310
|
+
pole, yaw is normalized to `0` because it does not identify a unique point.
|
|
311
|
+
|
|
312
|
+
## Viewer-level events (`PanoEvents`)
|
|
313
|
+
|
|
314
|
+
`PanoEvents` is the React counterpart to krpano's global `<events>` element.
|
|
315
|
+
Render one or more instances inside `PanoView` to subscribe to viewer-level
|
|
316
|
+
lifecycle and interaction callbacks. Multiple instances coexist (like named
|
|
317
|
+
krpano events); each tracks its own `idleTime`. For composition inside a custom
|
|
318
|
+
child, use `usePanoEvents` instead.
|
|
319
|
+
|
|
320
|
+
```tsx
|
|
321
|
+
import {
|
|
322
|
+
AutoRotate,
|
|
323
|
+
PanoEvents,
|
|
324
|
+
PanoView,
|
|
325
|
+
Sphere,
|
|
326
|
+
} from "@ericchen1990/pano-view";
|
|
327
|
+
|
|
328
|
+
<PanoView style={{ height: 560 }}>
|
|
329
|
+
<PanoEvents
|
|
330
|
+
idleTime={3000}
|
|
331
|
+
onIdle={() => console.log("idle")}
|
|
332
|
+
onIdleEnd={() => console.log("active again")}
|
|
333
|
+
onViewSettled={(view) => console.log("settled", view)}
|
|
334
|
+
onViewInteractionStart={({ source }) => console.log("drag", source)}
|
|
335
|
+
onClick={({ position }) => console.log("pano click", position)}
|
|
336
|
+
onEnterFullscreen={() => console.log("fullscreen")}
|
|
337
|
+
onAutoRotateOneRound={() => console.log("full turn")}
|
|
338
|
+
/>
|
|
339
|
+
<AutoRotate enabled />
|
|
340
|
+
<Sphere src="/panoramas/room.webp" />
|
|
341
|
+
</PanoView>;
|
|
342
|
+
```
|
|
343
|
+
|
|
344
|
+
Supported callbacks (krpano names in parentheses where applicable):
|
|
345
|
+
|
|
346
|
+
- View: `onViewChange` (`onviewchange`), `onViewSettled`, `onViewInteractionStart` / `onViewInteractionEnd`
|
|
347
|
+
- Pointer on the panorama shell (not hotspots): `onClick`, `onDoubleClick`, `onPointerDown`, `onPointerUp`, `onPointerMove`, `onContextMenu`
|
|
348
|
+
- `onWheel` — mouse wheel only; touch pinch does not synthesize a wheel event
|
|
349
|
+
- Idle: `onIdle` / `onIdleEnd` with per-instance `idleTime` (ms, default `2000`)
|
|
350
|
+
- Fullscreen: `onEnterFullscreen` / `onExitFullscreen`
|
|
351
|
+
- `onResize` — canvas content-box size via `ResizeObserver`
|
|
352
|
+
- Auto-rotate: `onAutoRotateStart` / `onAutoRotateStop` / `onAutoRotateOneRound`
|
|
353
|
+
|
|
354
|
+
`PanoView`'s existing `onViewChange` / `onPanoramaClick` / `onPanoramaDoubleClick` /
|
|
355
|
+
`onPanoramaPointerMove` props remain supported and share the same event bus.
|
|
356
|
+
|
|
357
|
+
Resource loading and scene blending stay on their owners: use `Sphere` /
|
|
358
|
+
`Tile` `onLoad` / `onError` / `onLoadProgress`, and `PanoramaScenes`
|
|
359
|
+
`onTransitionEnd` / `onTransitionError`. This package does not mirror krpano
|
|
360
|
+
xml/VR/gyro/frame-render events.
|
|
361
|
+
|
|
362
|
+
## Context menu
|
|
363
|
+
|
|
364
|
+
`PanoView` mounts a default context menu that replaces the browser menu on
|
|
365
|
+
right-click. Default items: **Reset view** and **Enter fullscreen** / **Exit
|
|
366
|
+
fullscreen** (label and icon follow the current fullscreen state), with a
|
|
367
|
+
separator between them.
|
|
368
|
+
|
|
369
|
+
```tsx
|
|
370
|
+
<PanoView style={{ height: 560 }}>
|
|
371
|
+
<Sphere src="/panoramas/room.webp" />
|
|
372
|
+
</PanoView>
|
|
373
|
+
```
|
|
374
|
+
|
|
375
|
+
Disable the default menu (restore the browser menu, or mount your own):
|
|
376
|
+
|
|
377
|
+
```tsx
|
|
378
|
+
<PanoView contextMenu={false}>
|
|
379
|
+
<Sphere src="/panoramas/room.webp" />
|
|
380
|
+
</PanoView>
|
|
381
|
+
```
|
|
382
|
+
|
|
383
|
+
Tune appearance while keeping the default items:
|
|
384
|
+
|
|
385
|
+
```tsx
|
|
386
|
+
<PanoView contextMenu={{ appearance: { opacity: 0.92, borderRadius: 8 } }}>
|
|
387
|
+
<Sphere src="/panoramas/room.webp" />
|
|
388
|
+
</PanoView>
|
|
389
|
+
```
|
|
390
|
+
|
|
391
|
+
### Adding items without rebuilding defaults
|
|
392
|
+
|
|
393
|
+
Use `append` or `prepend` to keep the built-in Reset / Fullscreen entries and
|
|
394
|
+
only add your own:
|
|
395
|
+
|
|
396
|
+
```tsx
|
|
397
|
+
<PanoView
|
|
398
|
+
contextMenu={{
|
|
399
|
+
append: [
|
|
400
|
+
"separator",
|
|
401
|
+
{
|
|
402
|
+
id: "copy",
|
|
403
|
+
label: "Copy yaw / pitch",
|
|
404
|
+
onSelect: ({ position }) => {
|
|
405
|
+
void navigator.clipboard.writeText(
|
|
406
|
+
`${position.yaw.toFixed(1)}, ${position.pitch.toFixed(1)}`,
|
|
407
|
+
);
|
|
408
|
+
},
|
|
409
|
+
},
|
|
410
|
+
],
|
|
411
|
+
}}
|
|
412
|
+
style={{ height: 560 }}
|
|
413
|
+
>
|
|
414
|
+
<Sphere src="/panoramas/room.webp" />
|
|
415
|
+
</PanoView>
|
|
416
|
+
```
|
|
417
|
+
|
|
418
|
+
### Built-in presets
|
|
419
|
+
|
|
420
|
+
When you need a custom order, reuse built-in presets by id instead of
|
|
421
|
+
reimplementing fullscreen enter/exit state:
|
|
422
|
+
|
|
423
|
+
- `"resetView"` — resets the view
|
|
424
|
+
- `"fullscreen"` — Enter / Exit fullscreen with the matching icon
|
|
425
|
+
- `"separator"` — a horizontal rule
|
|
426
|
+
|
|
427
|
+
```tsx
|
|
428
|
+
<PanoView
|
|
429
|
+
contextMenu={{
|
|
430
|
+
items: [
|
|
431
|
+
"resetView",
|
|
432
|
+
"separator",
|
|
433
|
+
{
|
|
434
|
+
id: "copy",
|
|
435
|
+
label: "Copy yaw / pitch",
|
|
436
|
+
onSelect: ({ position }) => {
|
|
437
|
+
void navigator.clipboard.writeText(
|
|
438
|
+
`${position.yaw.toFixed(1)}, ${position.pitch.toFixed(1)}`,
|
|
439
|
+
);
|
|
440
|
+
},
|
|
441
|
+
},
|
|
442
|
+
"separator",
|
|
443
|
+
"fullscreen",
|
|
444
|
+
],
|
|
445
|
+
}}
|
|
446
|
+
style={{ height: 560 }}
|
|
447
|
+
>
|
|
448
|
+
<Sphere src="/panoramas/room.webp" />
|
|
449
|
+
</PanoView>
|
|
450
|
+
```
|
|
451
|
+
|
|
452
|
+
Override presentation on a preset with `{ preset: "fullscreen", label: "…" }`.
|
|
453
|
+
For fully manual assembly outside the prop (for example a custom
|
|
454
|
+
`PanoContextMenu` child), call `createPanoContextMenuPresets({ reset,
|
|
455
|
+
toggleFullscreen, isFullscreen })` and use `.resetView` / `.fullscreen` /
|
|
456
|
+
`.defaults`.
|
|
457
|
+
|
|
458
|
+
Concrete items may use an `icon` React node or an `image` URL. Separators
|
|
459
|
+
always come from the `"separator"` preset. Appearance covers background, text
|
|
460
|
+
color, border, radius, shadow, background-only `opacity` (clamped from
|
|
461
|
+
`MIN_PANO_CONTEXT_MENU_BACKGROUND_OPACITY` / `0.4` to `1`; labels and icons
|
|
462
|
+
stay opaque), hover/disabled colors, and icon size. For an explicit translucent
|
|
463
|
+
fill without using `opacity`, you can still pass `background: "rgba(...)"`.
|
|
464
|
+
|
|
465
|
+
For a fully custom DOM menu, set `contextMenu={false}` and render
|
|
466
|
+
`PanoContextMenu` as a child (same override pattern as `MouseControls`). Do
|
|
467
|
+
not mount both the default menu and a child `PanoContextMenu` at once. Avoid
|
|
468
|
+
combining the context menu with `controls.mouse.buttons: ["right"]` — both
|
|
469
|
+
claim the right mouse button.
|
|
233
470
|
|
|
234
471
|
## Shared hotspot contract and saved definitions
|
|
235
472
|
|
|
@@ -289,10 +526,11 @@ import { ImageHotspot, PanoView, Sphere } from "@ericchen1990/pano-view";
|
|
|
289
526
|
</PanoView>;
|
|
290
527
|
```
|
|
291
528
|
|
|
292
|
-
`GraphicHotspot` accepts built-in `circle`, `
|
|
293
|
-
an SVG URL, or safe SVG path data
|
|
294
|
-
support `fill`, `stroke`, and
|
|
295
|
-
`cornerRadius`
|
|
529
|
+
`GraphicHotspot` accepts built-in `circle`, `triangle`, `diamond`, `star`,
|
|
530
|
+
`arrow`, `rectangle`, and `ring` graphics, an SVG URL, or safe SVG path data
|
|
531
|
+
with an explicit viewBox. Built-in graphics support `fill`, `stroke`, and
|
|
532
|
+
`strokeWidth`; rectangles support a relative `cornerRadius` from `0` to `0.5`,
|
|
533
|
+
and rings support `innerRadius`.
|
|
296
534
|
|
|
297
535
|
```tsx
|
|
298
536
|
import { GraphicHotspot } from "@ericchen1990/pano-view";
|
|
@@ -314,6 +552,31 @@ import { GraphicHotspot } from "@ericchen1990/pano-view";
|
|
|
314
552
|
/>
|
|
315
553
|
```
|
|
316
554
|
|
|
555
|
+
Triangle, diamond, star, and arrow graphics use the same paint properties. The
|
|
556
|
+
triangle and arrow point upward at `rotation={0}`; use the hotspot's `rotation`
|
|
557
|
+
property to orient them in another direction.
|
|
558
|
+
|
|
559
|
+
```tsx
|
|
560
|
+
<GraphicHotspot
|
|
561
|
+
id="direction"
|
|
562
|
+
ariaLabel="Open next scene"
|
|
563
|
+
position={{ yaw: 8, pitch: 2 }}
|
|
564
|
+
rotation={45}
|
|
565
|
+
width={8}
|
|
566
|
+
height={8}
|
|
567
|
+
graphic={{
|
|
568
|
+
kind: "arrow",
|
|
569
|
+
fill: "#df6b42",
|
|
570
|
+
stroke: "#f5fbfc",
|
|
571
|
+
strokeWidth: 8,
|
|
572
|
+
}}
|
|
573
|
+
/>
|
|
574
|
+
```
|
|
575
|
+
|
|
576
|
+
`cornerRadius` is relative to the rendered graphic's shorter side: `0` keeps
|
|
577
|
+
square corners and `0.5` produces the maximum rounded corners. This replaces
|
|
578
|
+
the previous Canvas-texture pixel interpretation.
|
|
579
|
+
|
|
317
580
|
Set `draggable` and update the controlled position from `onPositionChange` to
|
|
318
581
|
move a selected hotspot. Clickable hotspots need `ariaLabel`: PanoView creates
|
|
319
582
|
an internal semantic control for Tab, Enter, and Space activation, with a
|
|
@@ -337,6 +600,10 @@ placement properties:
|
|
|
337
600
|
- `scaleMode="fov"` (default) lets a hotspot grow when the user zooms in.
|
|
338
601
|
`scaleMode="fixed"` compensates for FOV changes and keeps its screen size
|
|
339
602
|
close to the size at `referenceFov` (default `75`).
|
|
603
|
+
- `rotation` rotates the hotspot around its own normal in degrees; positive
|
|
604
|
+
values rotate clockwise and negative values rotate counterclockwise.
|
|
605
|
+
`scale` applies an overall positive multiplier to its angular width and
|
|
606
|
+
height and defaults to `1`.
|
|
340
607
|
|
|
341
608
|
```tsx
|
|
342
609
|
<ImageHotspot
|
|
@@ -347,6 +614,8 @@ placement properties:
|
|
|
347
614
|
height={6}
|
|
348
615
|
mode="billboard"
|
|
349
616
|
distance={14}
|
|
617
|
+
rotation={-12}
|
|
618
|
+
scale={1.25}
|
|
350
619
|
scaleMode="fixed"
|
|
351
620
|
src="/hotspots/callout.webp"
|
|
352
621
|
/>
|