@ericchen1990/pano-view 3.6.3 → 3.6.5
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.zh-CN.md +4 -4
- package/dist/index.cjs +347 -62
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.mts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.mjs +368 -80
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/README.zh-CN.md
CHANGED
|
@@ -60,15 +60,15 @@
|
|
|
60
60
|
| 更新 | 生成并发布文件,再管理入口引用和缓存 | 无需发布静态文件;生效时间仍取决于后端和客户端缓存 |
|
|
61
61
|
| 权限与个性化 | 公开文件适合公开内容;私有分发需要托管/CDN 访问控制 | 后端可按用户、组织鉴权并返回不同配置 |
|
|
62
62
|
| 可用性与维护 | 依赖静态托管/CDN;维护发布及缓存更新流程 | 依赖 API 及其数据库/缓存服务;维护查询、鉴权及缓存失效 |
|
|
63
|
-
| 适用场景 |
|
|
63
|
+
| 适用场景 | 公开、已发布的漫游;更新较少;读取流量大 | 编辑预览、私有内容、频繁更新和个性化 |
|
|
64
64
|
|
|
65
|
-
|
|
65
|
+
**公开、已发布的漫游优先使用 JSON + CDN;编辑预览、私有和个性化内容优先使用数据库 + API。** 同时需要两者时,以数据库作为编辑来源,发布时生成 JSON 快照。这种混合方式需要应用自己的发布流程;包不会自动生成或同步快照。
|
|
66
66
|
|
|
67
67
|
两条路径均不会自动轮询或订阅配置变化。更新已打开的页面时,宿主应用需要重新加载配置,并将新数据传给查看器。未命中缓存的 API 读取可以返回当前数据库值,但不会让已有查看器自动更新。
|
|
68
68
|
|
|
69
69
|
### 数据库配置接入
|
|
70
70
|
|
|
71
|
-
|
|
71
|
+
后端先查询漫游并完成鉴权,再将需要公开给查看器的配置字段映射为项目。下例的 `readAuthorizedTour` 由宿主应用实现,负责数据库读取和访问检查,不是包提供的 API。它返回的场景记录包含转换时保存的精简 `viewerConfig`、资源目录、视角和热点定义:
|
|
72
72
|
|
|
73
73
|
```ts
|
|
74
74
|
import { loadPanoProject } from "@ericchen1990/pano-view/data";
|
|
@@ -1826,7 +1826,7 @@ import { VideoHotspot } from "@ericchen1990/pano-view";
|
|
|
1826
1826
|
|
|
1827
1827
|
`playing` 与 `VideoHotspot` 一样受控。`loop` 默认 `false`,`muted` 默认 `false`,`volume` 默认 `1`,`pauseWhenHidden` 默认 `true`(标签页隐藏时暂停,`playing` 仍为 `true` 时,在标签页恢复可见后继续播放)。首次用户手势会解锁音频;若自动播放被阻止,会报告 `"blocked"`,并在 `playing` 仍为 true 时于下一次指针或按键后重试 `play()`。
|
|
1828
1828
|
|
|
1829
|
-
默认标记在 **停止**(扬声器)与 **播放**(扬声器 + 声波)之间切换。可用 `icon` 自定义停止图标、`playingIcon` 自定义播放图标。只设 `icon` 时两种状态共用该图。`marker={false}`
|
|
1829
|
+
默认标记在 **停止**(扬声器)与 **播放**(扬声器 + 声波)之间切换。可用 `icon` 自定义停止图标、`playingIcon` 自定义播放图标。只设 `icon` 时两种状态共用该图。`marker={false}` 隐藏标记,仍保留定点声源。整个漫游共用或按场景切换的非空间背景音频请使用 [`BackgroundAudio`](#backgroundaudio)。
|
|
1830
1830
|
|
|
1831
1831
|
```tsx
|
|
1832
1832
|
import { AudioHotspot } from "@ericchen1990/pano-view";
|
package/dist/index.cjs
CHANGED
|
@@ -4027,7 +4027,10 @@ function MouseControls({
|
|
|
4027
4027
|
const element = gl.domElement;
|
|
4028
4028
|
const drag = createPointerDrag(camera, element, () => controlsRef.current, "mouse");
|
|
4029
4029
|
const allowedButtons = resolveButtons(buttons);
|
|
4030
|
+
const pending = /* @__PURE__ */ new Set();
|
|
4031
|
+
let disposed = false;
|
|
4030
4032
|
const clearPointers = () => {
|
|
4033
|
+
pending.clear();
|
|
4031
4034
|
for (const pointerId of pointersRef.current.keys()) {
|
|
4032
4035
|
if (element.hasPointerCapture?.(pointerId)) {
|
|
4033
4036
|
element.releasePointerCapture?.(pointerId);
|
|
@@ -4053,6 +4056,25 @@ function MouseControls({
|
|
|
4053
4056
|
{ cancelled }
|
|
4054
4057
|
);
|
|
4055
4058
|
};
|
|
4059
|
+
const beginPointer = (event) => {
|
|
4060
|
+
if (disposed || controlsRef.current?.isInteractionLocked()) {
|
|
4061
|
+
return;
|
|
4062
|
+
}
|
|
4063
|
+
if (pointersRef.current.has(event.pointerId)) {
|
|
4064
|
+
return;
|
|
4065
|
+
}
|
|
4066
|
+
pointersRef.current.set(event.pointerId, {
|
|
4067
|
+
x: event.clientX,
|
|
4068
|
+
y: event.clientY,
|
|
4069
|
+
at: performance.now()
|
|
4070
|
+
});
|
|
4071
|
+
try {
|
|
4072
|
+
element.setPointerCapture?.(event.pointerId);
|
|
4073
|
+
} catch {
|
|
4074
|
+
}
|
|
4075
|
+
syncActive();
|
|
4076
|
+
drag.start({ x: event.clientX, y: event.clientY }, pointerAt(event));
|
|
4077
|
+
};
|
|
4056
4078
|
const onPointerDown = (event) => {
|
|
4057
4079
|
if (!isMouseLike(event)) {
|
|
4058
4080
|
return;
|
|
@@ -4064,14 +4086,14 @@ function MouseControls({
|
|
|
4064
4086
|
return;
|
|
4065
4087
|
}
|
|
4066
4088
|
event.preventDefault();
|
|
4067
|
-
|
|
4068
|
-
|
|
4069
|
-
|
|
4070
|
-
|
|
4089
|
+
const pointerId = event.pointerId;
|
|
4090
|
+
pending.add(pointerId);
|
|
4091
|
+
queueMicrotask(() => {
|
|
4092
|
+
if (!pending.delete(pointerId)) {
|
|
4093
|
+
return;
|
|
4094
|
+
}
|
|
4095
|
+
beginPointer(event);
|
|
4071
4096
|
});
|
|
4072
|
-
element.setPointerCapture?.(event.pointerId);
|
|
4073
|
-
syncActive();
|
|
4074
|
-
drag.start({ x: event.clientX, y: event.clientY }, pointerAt(event));
|
|
4075
4097
|
};
|
|
4076
4098
|
const onPointerMove = (event) => {
|
|
4077
4099
|
if (!isMouseLike(event)) {
|
|
@@ -4095,6 +4117,7 @@ function MouseControls({
|
|
|
4095
4117
|
drag.move(next, previous, speed, optionsRef.current.invert, pointerAt(event));
|
|
4096
4118
|
};
|
|
4097
4119
|
const releasePointer = (event) => {
|
|
4120
|
+
pending.delete(event.pointerId);
|
|
4098
4121
|
if (!pointersRef.current.has(event.pointerId)) {
|
|
4099
4122
|
return;
|
|
4100
4123
|
}
|
|
@@ -4136,6 +4159,7 @@ function MouseControls({
|
|
|
4136
4159
|
element.addEventListener("lostpointercapture", releasePointer);
|
|
4137
4160
|
element.addEventListener("wheel", onWheel, { passive: false });
|
|
4138
4161
|
return () => {
|
|
4162
|
+
disposed = true;
|
|
4139
4163
|
validateDragRef.current = void 0;
|
|
4140
4164
|
element.removeEventListener("pointerdown", onPointerDown);
|
|
4141
4165
|
element.removeEventListener("pointermove", onPointerMove);
|
|
@@ -5630,6 +5654,12 @@ function vector3ToPanoPosition(vector) {
|
|
|
5630
5654
|
)
|
|
5631
5655
|
});
|
|
5632
5656
|
}
|
|
5657
|
+
function translateHotspotPosition(origin, from, to) {
|
|
5658
|
+
return normalizePanoPosition({
|
|
5659
|
+
yaw: origin.yaw + normalizePanoYaw(to.yaw - from.yaw),
|
|
5660
|
+
pitch: origin.pitch + (to.pitch - from.pitch)
|
|
5661
|
+
});
|
|
5662
|
+
}
|
|
5633
5663
|
|
|
5634
5664
|
// src/panorama-radius.ts
|
|
5635
5665
|
var DEFAULT_PANORAMA_RADIUS = 1e3;
|
|
@@ -5773,7 +5803,10 @@ function TouchControls({
|
|
|
5773
5803
|
(0, import_react18.useEffect)(() => {
|
|
5774
5804
|
const element = gl.domElement;
|
|
5775
5805
|
const drag = createPointerDrag(camera, element, () => controlsRef.current, "touch");
|
|
5806
|
+
const pending = /* @__PURE__ */ new Set();
|
|
5807
|
+
let disposed = false;
|
|
5776
5808
|
const clearPointers = () => {
|
|
5809
|
+
pending.clear();
|
|
5777
5810
|
for (const pointerId of pointersRef.current.keys()) {
|
|
5778
5811
|
if (element.hasPointerCapture?.(pointerId)) {
|
|
5779
5812
|
element.releasePointerCapture?.(pointerId);
|
|
@@ -5799,20 +5832,22 @@ function TouchControls({
|
|
|
5799
5832
|
{ cancelled }
|
|
5800
5833
|
);
|
|
5801
5834
|
};
|
|
5802
|
-
const
|
|
5803
|
-
if (
|
|
5835
|
+
const beginPointer = (event) => {
|
|
5836
|
+
if (disposed || controlsRef.current?.isInteractionLocked()) {
|
|
5804
5837
|
return;
|
|
5805
5838
|
}
|
|
5806
|
-
if (
|
|
5839
|
+
if (pointersRef.current.has(event.pointerId)) {
|
|
5807
5840
|
return;
|
|
5808
5841
|
}
|
|
5809
|
-
event.preventDefault();
|
|
5810
5842
|
pointersRef.current.set(event.pointerId, {
|
|
5811
5843
|
x: event.clientX,
|
|
5812
5844
|
y: event.clientY,
|
|
5813
5845
|
at: performance.now()
|
|
5814
5846
|
});
|
|
5815
|
-
|
|
5847
|
+
try {
|
|
5848
|
+
element.setPointerCapture?.(event.pointerId);
|
|
5849
|
+
} catch {
|
|
5850
|
+
}
|
|
5816
5851
|
syncActive();
|
|
5817
5852
|
drag.start({ x: event.clientX, y: event.clientY }, pointerAt2(event));
|
|
5818
5853
|
if (pointersRef.current.size >= 2) {
|
|
@@ -5821,6 +5856,23 @@ function TouchControls({
|
|
|
5821
5856
|
pinchDistanceRef.current = pointerDistance(first, second);
|
|
5822
5857
|
}
|
|
5823
5858
|
};
|
|
5859
|
+
const onPointerDown = (event) => {
|
|
5860
|
+
if (event.pointerType !== "touch") {
|
|
5861
|
+
return;
|
|
5862
|
+
}
|
|
5863
|
+
if (controlsRef.current?.isInteractionLocked()) {
|
|
5864
|
+
return;
|
|
5865
|
+
}
|
|
5866
|
+
event.preventDefault();
|
|
5867
|
+
const pointerId = event.pointerId;
|
|
5868
|
+
pending.add(pointerId);
|
|
5869
|
+
queueMicrotask(() => {
|
|
5870
|
+
if (!pending.delete(pointerId)) {
|
|
5871
|
+
return;
|
|
5872
|
+
}
|
|
5873
|
+
beginPointer(event);
|
|
5874
|
+
});
|
|
5875
|
+
};
|
|
5824
5876
|
const onPointerMove = (event) => {
|
|
5825
5877
|
if (event.pointerType !== "touch") {
|
|
5826
5878
|
return;
|
|
@@ -5864,6 +5916,7 @@ function TouchControls({
|
|
|
5864
5916
|
drag.move(next, previous, speed, optionsRef.current.invert, pointerAt2(event));
|
|
5865
5917
|
};
|
|
5866
5918
|
const releasePointer = (event) => {
|
|
5919
|
+
pending.delete(event.pointerId);
|
|
5867
5920
|
if (!pointersRef.current.has(event.pointerId)) {
|
|
5868
5921
|
return;
|
|
5869
5922
|
}
|
|
@@ -5887,6 +5940,7 @@ function TouchControls({
|
|
|
5887
5940
|
element.addEventListener("pointercancel", releasePointer);
|
|
5888
5941
|
element.addEventListener("lostpointercapture", releasePointer);
|
|
5889
5942
|
return () => {
|
|
5943
|
+
disposed = true;
|
|
5890
5944
|
validateDragRef.current = void 0;
|
|
5891
5945
|
element.removeEventListener("pointerdown", onPointerDown);
|
|
5892
5946
|
element.removeEventListener("pointermove", onPointerMove);
|
|
@@ -11436,6 +11490,94 @@ var import_fiber21 = require("@react-three/fiber");
|
|
|
11436
11490
|
var import_react42 = require("react");
|
|
11437
11491
|
var import_three24 = require("three");
|
|
11438
11492
|
|
|
11493
|
+
// src/hotspot/drag-session.ts
|
|
11494
|
+
var CLICK_SUPPRESS_MS = 1e3;
|
|
11495
|
+
function trackPointerDrag(source, element, pointerId, onMove, onEnd) {
|
|
11496
|
+
let stopped = false;
|
|
11497
|
+
const releaseCapture = () => {
|
|
11498
|
+
if (!element.hasPointerCapture?.(pointerId)) {
|
|
11499
|
+
return;
|
|
11500
|
+
}
|
|
11501
|
+
try {
|
|
11502
|
+
element.releasePointerCapture?.(pointerId);
|
|
11503
|
+
} catch {
|
|
11504
|
+
}
|
|
11505
|
+
};
|
|
11506
|
+
const stop = () => {
|
|
11507
|
+
if (stopped) {
|
|
11508
|
+
return;
|
|
11509
|
+
}
|
|
11510
|
+
stopped = true;
|
|
11511
|
+
source.removeEventListener("pointermove", onPointerMove, true);
|
|
11512
|
+
source.removeEventListener("pointerup", onPointerUp, true);
|
|
11513
|
+
source.removeEventListener("pointercancel", onPointerCancel, true);
|
|
11514
|
+
element.removeEventListener("lostpointercapture", onLostCapture);
|
|
11515
|
+
releaseCapture();
|
|
11516
|
+
};
|
|
11517
|
+
const finish = (event) => {
|
|
11518
|
+
if (stopped || event.pointerId !== pointerId) {
|
|
11519
|
+
return;
|
|
11520
|
+
}
|
|
11521
|
+
stop();
|
|
11522
|
+
onEnd(event);
|
|
11523
|
+
};
|
|
11524
|
+
const onPointerMove = (event) => {
|
|
11525
|
+
if (stopped || event.pointerId !== pointerId) {
|
|
11526
|
+
return;
|
|
11527
|
+
}
|
|
11528
|
+
try {
|
|
11529
|
+
event.preventDefault?.();
|
|
11530
|
+
} catch {
|
|
11531
|
+
}
|
|
11532
|
+
onMove(event);
|
|
11533
|
+
};
|
|
11534
|
+
const onPointerUp = (event) => {
|
|
11535
|
+
finish(event);
|
|
11536
|
+
};
|
|
11537
|
+
const onPointerCancel = (event) => {
|
|
11538
|
+
finish(event);
|
|
11539
|
+
};
|
|
11540
|
+
const onLostCapture = (event) => {
|
|
11541
|
+
if ((event.buttons ?? 0) > 0) {
|
|
11542
|
+
return;
|
|
11543
|
+
}
|
|
11544
|
+
finish(event);
|
|
11545
|
+
};
|
|
11546
|
+
try {
|
|
11547
|
+
element.setPointerCapture?.(pointerId);
|
|
11548
|
+
} catch {
|
|
11549
|
+
}
|
|
11550
|
+
source.addEventListener("pointermove", onPointerMove, true);
|
|
11551
|
+
source.addEventListener("pointerup", onPointerUp, true);
|
|
11552
|
+
source.addEventListener("pointercancel", onPointerCancel, true);
|
|
11553
|
+
element.addEventListener("lostpointercapture", onLostCapture);
|
|
11554
|
+
return stop;
|
|
11555
|
+
}
|
|
11556
|
+
var CAPTURE = { capture: true };
|
|
11557
|
+
function suppressFollowingClick(target) {
|
|
11558
|
+
let cleaned = false;
|
|
11559
|
+
const cleanup = () => {
|
|
11560
|
+
if (cleaned) {
|
|
11561
|
+
return;
|
|
11562
|
+
}
|
|
11563
|
+
cleaned = true;
|
|
11564
|
+
target.removeEventListener("click", swallowClick, CAPTURE);
|
|
11565
|
+
target.removeEventListener("pointerdown", cancel, CAPTURE);
|
|
11566
|
+
};
|
|
11567
|
+
const swallowClick = (event) => {
|
|
11568
|
+
event.preventDefault();
|
|
11569
|
+
event.stopPropagation();
|
|
11570
|
+
event.stopImmediatePropagation();
|
|
11571
|
+
cleanup();
|
|
11572
|
+
};
|
|
11573
|
+
const cancel = () => {
|
|
11574
|
+
cleanup();
|
|
11575
|
+
};
|
|
11576
|
+
target.addEventListener("click", swallowClick, CAPTURE);
|
|
11577
|
+
target.addEventListener("pointerdown", cancel, CAPTURE);
|
|
11578
|
+
setTimeout(cleanup, CLICK_SUPPRESS_MS);
|
|
11579
|
+
}
|
|
11580
|
+
|
|
11439
11581
|
// src/hotspot/hotspot-tooltip.tsx
|
|
11440
11582
|
var import_react40 = require("react");
|
|
11441
11583
|
|
|
@@ -11695,6 +11837,21 @@ function fixedScaleFactor(cameraFov, referenceFov) {
|
|
|
11695
11837
|
const safeReferenceFov = import_three22.MathUtils.clamp(referenceFov, 1, 179);
|
|
11696
11838
|
return Math.tan(import_three22.MathUtils.degToRad(cameraFov) / 2) / Math.tan(import_three22.MathUtils.degToRad(safeReferenceFov) / 2);
|
|
11697
11839
|
}
|
|
11840
|
+
function selectionFrameScale(worldScaleX, worldScaleY, cameraSpaceDepth, fovDegrees, viewportHeight, outsetPixels) {
|
|
11841
|
+
const pixels = Number.isFinite(outsetPixels) && outsetPixels > 0 ? outsetPixels : 0;
|
|
11842
|
+
const depth = Number.isFinite(cameraSpaceDepth) ? Math.max(cameraSpaceDepth, 1e-4) : 1e-4;
|
|
11843
|
+
const height = Number.isFinite(viewportHeight) ? Math.max(viewportHeight, 1) : 1;
|
|
11844
|
+
const fov = Number.isFinite(fovDegrees) ? import_three22.MathUtils.clamp(fovDegrees, 1, 179) : 75;
|
|
11845
|
+
const pixelsPerWorldUnit = height / (2 * Math.tan(import_three22.MathUtils.degToRad(fov) * 0.5) * depth);
|
|
11846
|
+
const axis = (worldScale) => {
|
|
11847
|
+
const pixelsPerLocalUnit = pixelsPerWorldUnit * Math.abs(worldScale);
|
|
11848
|
+
if (pixelsPerLocalUnit < 1e-4) {
|
|
11849
|
+
return 1;
|
|
11850
|
+
}
|
|
11851
|
+
return 1 + 2 * pixels / pixelsPerLocalUnit;
|
|
11852
|
+
};
|
|
11853
|
+
return [axis(worldScaleX), axis(worldScaleY)];
|
|
11854
|
+
}
|
|
11698
11855
|
|
|
11699
11856
|
// src/hotspot/hotspot-stroke-line.tsx
|
|
11700
11857
|
var import_drei = require("@react-three/drei");
|
|
@@ -11777,6 +11934,11 @@ var DEFAULT_REFERENCE_FOV = 75;
|
|
|
11777
11934
|
var MIN_ANGULAR_SIZE = 0.01;
|
|
11778
11935
|
var MAX_ANGULAR_SIZE = 179;
|
|
11779
11936
|
var DRAG_EPSILON_DEGREES = 1e-3;
|
|
11937
|
+
var SELECTION_BORDER_WIDTH = 3;
|
|
11938
|
+
var SELECTION_BORDER_GAP = 3;
|
|
11939
|
+
var SELECTION_BORDER_OUTSET = SELECTION_BORDER_GAP + SELECTION_BORDER_WIDTH / 2;
|
|
11940
|
+
var SELECTION_CAMERA_POSITION = new import_three24.Vector3();
|
|
11941
|
+
var SELECTION_WORLD_SCALE = new import_three24.Vector3();
|
|
11780
11942
|
var SELECTION_BORDER_POINTS = [
|
|
11781
11943
|
[-0.5, -0.5, 1e-3],
|
|
11782
11944
|
[0.5, -0.5, 1e-3],
|
|
@@ -11864,6 +12026,34 @@ function angularDistance(a, b) {
|
|
|
11864
12026
|
const bVector = panoPositionToVector3(b);
|
|
11865
12027
|
return import_three24.MathUtils.radToDeg(aVector.angleTo(bVector));
|
|
11866
12028
|
}
|
|
12029
|
+
var DRAG_POINTER = new import_three24.Vector2();
|
|
12030
|
+
var DRAG_RAYCASTER = new import_three24.Raycaster();
|
|
12031
|
+
function aimDragRaycaster(event, element, camera) {
|
|
12032
|
+
const rect = element.getBoundingClientRect();
|
|
12033
|
+
if (!(rect.width > 0) || !(rect.height > 0)) {
|
|
12034
|
+
return false;
|
|
12035
|
+
}
|
|
12036
|
+
DRAG_POINTER.set(
|
|
12037
|
+
(event.clientX - rect.left) / rect.width * 2 - 1,
|
|
12038
|
+
-((event.clientY - rect.top) / rect.height) * 2 + 1
|
|
12039
|
+
);
|
|
12040
|
+
camera.updateMatrixWorld();
|
|
12041
|
+
DRAG_RAYCASTER.setFromCamera(DRAG_POINTER, camera);
|
|
12042
|
+
return true;
|
|
12043
|
+
}
|
|
12044
|
+
function pointerToPanoPosition(event, element, camera) {
|
|
12045
|
+
if (!aimDragRaycaster(event, element, camera)) {
|
|
12046
|
+
return null;
|
|
12047
|
+
}
|
|
12048
|
+
return vector3ToPanoPosition(DRAG_RAYCASTER.ray.direction);
|
|
12049
|
+
}
|
|
12050
|
+
function pointerHitsHotspot(event, element, camera, group) {
|
|
12051
|
+
if (!group || !aimDragRaycaster(event, element, camera)) {
|
|
12052
|
+
return false;
|
|
12053
|
+
}
|
|
12054
|
+
group.updateMatrixWorld(true);
|
|
12055
|
+
return DRAG_RAYCASTER.intersectObject(group, true).length > 0;
|
|
12056
|
+
}
|
|
11867
12057
|
function isTooltipOpen(trigger, hovered, focused, pinned) {
|
|
11868
12058
|
switch (trigger) {
|
|
11869
12059
|
case "always":
|
|
@@ -12059,6 +12249,24 @@ function updateTooltipAnchor(anchor, group, placement, useAngularScale, camera,
|
|
|
12059
12249
|
}
|
|
12060
12250
|
setTooltipAnchorOnLocalBox(anchor, placement, -0.5, 0.5, -0.5, 0.5);
|
|
12061
12251
|
}
|
|
12252
|
+
function placeSelectionFrame(frame, group, camera, viewportHeight) {
|
|
12253
|
+
if (!frame) {
|
|
12254
|
+
return;
|
|
12255
|
+
}
|
|
12256
|
+
group.getWorldScale(SELECTION_WORLD_SCALE);
|
|
12257
|
+
SELECTION_CAMERA_POSITION.setFromMatrixPosition(group.matrixWorld).applyMatrix4(
|
|
12258
|
+
camera.matrixWorldInverse
|
|
12259
|
+
);
|
|
12260
|
+
const [scaleX, scaleY] = selectionFrameScale(
|
|
12261
|
+
SELECTION_WORLD_SCALE.x,
|
|
12262
|
+
SELECTION_WORLD_SCALE.y,
|
|
12263
|
+
-SELECTION_CAMERA_POSITION.z,
|
|
12264
|
+
camera instanceof import_three24.PerspectiveCamera ? camera.fov : DEFAULT_REFERENCE_FOV,
|
|
12265
|
+
viewportHeight,
|
|
12266
|
+
SELECTION_BORDER_OUTSET
|
|
12267
|
+
);
|
|
12268
|
+
frame.scale.set(scaleX, scaleY, 1);
|
|
12269
|
+
}
|
|
12062
12270
|
function HotspotAnchor({
|
|
12063
12271
|
id,
|
|
12064
12272
|
position,
|
|
@@ -12098,10 +12306,13 @@ function HotspotAnchor({
|
|
|
12098
12306
|
const hotspotTargetRegistry = (0, import_react42.useContext)(HotspotTargetRegistryContext);
|
|
12099
12307
|
const eventBus = (0, import_react42.useContext)(PanoEventBusContext);
|
|
12100
12308
|
const cursorApi = usePanoCursor();
|
|
12101
|
-
const { camera, size } = (0, import_fiber21.useThree)();
|
|
12309
|
+
const { camera, gl, size } = (0, import_fiber21.useThree)();
|
|
12102
12310
|
const groupRef = (0, import_react42.useRef)(null);
|
|
12311
|
+
const selectionFrameRef = (0, import_react42.useRef)(null);
|
|
12103
12312
|
const tooltipAnchorRef = (0, import_react42.useRef)(null);
|
|
12104
12313
|
const dragStateRef = (0, import_react42.useRef)(null);
|
|
12314
|
+
const updateDragRef = (0, import_react42.useRef)(() => void 0);
|
|
12315
|
+
const endDragRef = (0, import_react42.useRef)(() => void 0);
|
|
12105
12316
|
const suppressNextClickRef = (0, import_react42.useRef)(false);
|
|
12106
12317
|
const [hovered, setHovered] = (0, import_react42.useState)(false);
|
|
12107
12318
|
const [dragging, setDragging] = (0, import_react42.useState)(false);
|
|
@@ -12171,7 +12382,9 @@ function HotspotAnchor({
|
|
|
12171
12382
|
const cursorClaimId = hotspotCursorClaimId(id);
|
|
12172
12383
|
(0, import_react42.useEffect)(
|
|
12173
12384
|
() => () => {
|
|
12174
|
-
dragStateRef.current
|
|
12385
|
+
const dragState = dragStateRef.current;
|
|
12386
|
+
dragState?.stopTracking();
|
|
12387
|
+
dragState?.releaseInteractionLock();
|
|
12175
12388
|
dragStateRef.current = null;
|
|
12176
12389
|
},
|
|
12177
12390
|
[]
|
|
@@ -12228,6 +12441,7 @@ function HotspotAnchor({
|
|
|
12228
12441
|
if (!dragState) {
|
|
12229
12442
|
return;
|
|
12230
12443
|
}
|
|
12444
|
+
dragState.stopTracking();
|
|
12231
12445
|
dragState.releaseInteractionLock();
|
|
12232
12446
|
dragStateRef.current = null;
|
|
12233
12447
|
setDragging(false);
|
|
@@ -12250,6 +12464,7 @@ function HotspotAnchor({
|
|
|
12250
12464
|
worldHeight,
|
|
12251
12465
|
resolvedDistance
|
|
12252
12466
|
);
|
|
12467
|
+
placeSelectionFrame(selectionFrameRef.current, group, camera, size.height);
|
|
12253
12468
|
if (tooltipOpen && tooltipAnchorRef.current) {
|
|
12254
12469
|
updateTooltipAnchor(
|
|
12255
12470
|
tooltipAnchorRef.current,
|
|
@@ -12268,6 +12483,7 @@ function HotspotAnchor({
|
|
|
12268
12483
|
rotation,
|
|
12269
12484
|
scaleMode,
|
|
12270
12485
|
size,
|
|
12486
|
+
selected,
|
|
12271
12487
|
surfaceQuaternion3,
|
|
12272
12488
|
tooltipOpen,
|
|
12273
12489
|
tooltipPlacement,
|
|
@@ -12293,6 +12509,12 @@ function HotspotAnchor({
|
|
|
12293
12509
|
worldHeight,
|
|
12294
12510
|
resolvedDistance
|
|
12295
12511
|
);
|
|
12512
|
+
placeSelectionFrame(
|
|
12513
|
+
selectionFrameRef.current,
|
|
12514
|
+
group,
|
|
12515
|
+
state.camera,
|
|
12516
|
+
state.size.height
|
|
12517
|
+
);
|
|
12296
12518
|
if (tooltipOpen && tooltipAnchorRef.current) {
|
|
12297
12519
|
updateTooltipAnchor(
|
|
12298
12520
|
tooltipAnchorRef.current,
|
|
@@ -12308,31 +12530,78 @@ function HotspotAnchor({
|
|
|
12308
12530
|
event.stopPropagation();
|
|
12309
12531
|
event.nativeEvent.stopImmediatePropagation?.();
|
|
12310
12532
|
};
|
|
12311
|
-
const
|
|
12533
|
+
const dragPosition = (dragState, event) => {
|
|
12534
|
+
const pointerPosition = pointerToPanoPosition(event, gl.domElement, camera);
|
|
12535
|
+
if (!pointerPosition) {
|
|
12536
|
+
return dragState.lastPosition;
|
|
12537
|
+
}
|
|
12538
|
+
return translateHotspotPosition(
|
|
12539
|
+
dragState.startPosition,
|
|
12540
|
+
dragState.pointerOrigin,
|
|
12541
|
+
pointerPosition
|
|
12542
|
+
);
|
|
12543
|
+
};
|
|
12544
|
+
updateDragRef.current = (nativeEvent) => {
|
|
12312
12545
|
const dragState = dragStateRef.current;
|
|
12313
|
-
if (!dragState || dragState.pointerId !==
|
|
12546
|
+
if (!dragState || dragState.pointerId !== nativeEvent.pointerId) {
|
|
12314
12547
|
return;
|
|
12315
12548
|
}
|
|
12316
|
-
|
|
12317
|
-
|
|
12318
|
-
|
|
12319
|
-
|
|
12320
|
-
|
|
12321
|
-
|
|
12322
|
-
|
|
12323
|
-
|
|
12324
|
-
|
|
12325
|
-
|
|
12326
|
-
|
|
12327
|
-
|
|
12549
|
+
const nextPosition = dragPosition(dragState, nativeEvent);
|
|
12550
|
+
dragState.lastPosition = nextPosition;
|
|
12551
|
+
dragState.moved || (dragState.moved = angularDistance(dragState.startPosition, nextPosition) > DRAG_EPSILON_DEGREES);
|
|
12552
|
+
onPositionChange?.({
|
|
12553
|
+
id,
|
|
12554
|
+
position: nextPosition,
|
|
12555
|
+
source: "pointer",
|
|
12556
|
+
nativeEvent,
|
|
12557
|
+
startPosition: dragState.startPosition
|
|
12558
|
+
});
|
|
12559
|
+
};
|
|
12560
|
+
endDragRef.current = (nativeEvent, emitDragEnd) => {
|
|
12561
|
+
const dragState = dragStateRef.current;
|
|
12562
|
+
if (!dragState || dragState.pointerId !== nativeEvent.pointerId) {
|
|
12563
|
+
return;
|
|
12328
12564
|
}
|
|
12329
|
-
|
|
12565
|
+
const nextPosition = dragPosition(dragState, nativeEvent);
|
|
12566
|
+
dragState.lastPosition = nextPosition;
|
|
12567
|
+
dragState.moved || (dragState.moved = angularDistance(dragState.startPosition, nextPosition) > DRAG_EPSILON_DEGREES);
|
|
12568
|
+
const moved = dragState.moved;
|
|
12569
|
+
const startPosition = dragState.startPosition;
|
|
12570
|
+
dragState.stopTracking();
|
|
12330
12571
|
dragState.releaseInteractionLock();
|
|
12331
12572
|
dragStateRef.current = null;
|
|
12573
|
+
suppressNextClickRef.current = moved;
|
|
12332
12574
|
setDragging(false);
|
|
12333
|
-
if (emitDragEnd) {
|
|
12334
|
-
|
|
12575
|
+
if (!emitDragEnd) {
|
|
12576
|
+
return;
|
|
12577
|
+
}
|
|
12578
|
+
const overHotspot = pointerHitsHotspot(
|
|
12579
|
+
nativeEvent,
|
|
12580
|
+
gl.domElement,
|
|
12581
|
+
camera,
|
|
12582
|
+
groupRef.current
|
|
12583
|
+
);
|
|
12584
|
+
eventHovered.current = overHotspot;
|
|
12585
|
+
setHovered(overHotspot);
|
|
12586
|
+
if (moved && typeof window !== "undefined") {
|
|
12587
|
+
suppressFollowingClick(window);
|
|
12588
|
+
}
|
|
12589
|
+
if (moved) {
|
|
12590
|
+
onPositionChange?.({
|
|
12591
|
+
id,
|
|
12592
|
+
position: nextPosition,
|
|
12593
|
+
source: "pointer",
|
|
12594
|
+
nativeEvent,
|
|
12595
|
+
startPosition
|
|
12596
|
+
});
|
|
12335
12597
|
}
|
|
12598
|
+
onDragEnd?.({
|
|
12599
|
+
id,
|
|
12600
|
+
position: nextPosition,
|
|
12601
|
+
source: "pointer",
|
|
12602
|
+
nativeEvent,
|
|
12603
|
+
startPosition
|
|
12604
|
+
});
|
|
12336
12605
|
};
|
|
12337
12606
|
return /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)(
|
|
12338
12607
|
"group",
|
|
@@ -12352,23 +12621,42 @@ function HotspotAnchor({
|
|
|
12352
12621
|
}
|
|
12353
12622
|
suppressNextClickRef.current = false;
|
|
12354
12623
|
} : void 0,
|
|
12355
|
-
onLostPointerCapture: acceptsPointer ? (event) =>
|
|
12356
|
-
|
|
12624
|
+
onLostPointerCapture: acceptsPointer ? (event) => {
|
|
12625
|
+
endDragRef.current(event.nativeEvent, true);
|
|
12626
|
+
} : void 0,
|
|
12627
|
+
onPointerCancel: acceptsPointer ? (event) => {
|
|
12628
|
+
endDragRef.current(event.nativeEvent, true);
|
|
12629
|
+
} : void 0,
|
|
12357
12630
|
onPointerDown: acceptsPointer ? (event) => {
|
|
12358
|
-
|
|
12631
|
+
const native = event.nativeEvent;
|
|
12359
12632
|
if (!draggable || dragStateRef.current) {
|
|
12633
|
+
stopPointerEvent(event);
|
|
12360
12634
|
return;
|
|
12361
12635
|
}
|
|
12636
|
+
const pointerId = native.pointerId;
|
|
12637
|
+
stopPointerEvent(event);
|
|
12362
12638
|
const releaseInteractionLock = controlsRef?.current?.acquireInteractionLock() ?? (() => void 0);
|
|
12363
|
-
const
|
|
12364
|
-
target.setPointerCapture?.(event.pointerId);
|
|
12639
|
+
const pointerOrigin = pointerToPanoPosition(native, gl.domElement, camera) ?? vector3ToPanoPosition(event.ray.direction);
|
|
12365
12640
|
const startPosition = { ...normalizedPosition };
|
|
12641
|
+
const tracksOutsideMesh = typeof window !== "undefined";
|
|
12642
|
+
const stopTracking = tracksOutsideMesh ? trackPointerDrag(
|
|
12643
|
+
window,
|
|
12644
|
+
gl.domElement,
|
|
12645
|
+
pointerId,
|
|
12646
|
+
(pointerEvent) => updateDragRef.current(pointerEvent),
|
|
12647
|
+
(pointerEvent) => endDragRef.current(pointerEvent, true)
|
|
12648
|
+
) : () => void 0;
|
|
12366
12649
|
dragStateRef.current = {
|
|
12367
|
-
pointerId
|
|
12650
|
+
pointerId,
|
|
12368
12651
|
startPosition,
|
|
12652
|
+
pointerOrigin,
|
|
12653
|
+
lastPosition: startPosition,
|
|
12369
12654
|
releaseInteractionLock,
|
|
12370
|
-
moved: false
|
|
12655
|
+
moved: false,
|
|
12656
|
+
tracksOutsideMesh,
|
|
12657
|
+
stopTracking
|
|
12371
12658
|
};
|
|
12659
|
+
eventHovered.current = false;
|
|
12372
12660
|
setHovered(false);
|
|
12373
12661
|
setDragging(true);
|
|
12374
12662
|
onDragStart?.({
|
|
@@ -12379,17 +12667,13 @@ function HotspotAnchor({
|
|
|
12379
12667
|
} : void 0,
|
|
12380
12668
|
onPointerMove: acceptsPointer ? (event) => {
|
|
12381
12669
|
const dragState = dragStateRef.current;
|
|
12382
|
-
if (!dragState || dragState.pointerId !== event.pointerId) {
|
|
12670
|
+
if (!dragState || dragState.pointerId !== event.nativeEvent.pointerId) {
|
|
12383
12671
|
return;
|
|
12384
12672
|
}
|
|
12385
12673
|
stopPointerEvent(event);
|
|
12386
|
-
|
|
12387
|
-
|
|
12388
|
-
|
|
12389
|
-
...makeInteractionEvent(id, nextPosition, event),
|
|
12390
|
-
source: "pointer",
|
|
12391
|
-
startPosition: dragState.startPosition
|
|
12392
|
-
});
|
|
12674
|
+
if (!dragState.tracksOutsideMesh) {
|
|
12675
|
+
updateDragRef.current(event.nativeEvent);
|
|
12676
|
+
}
|
|
12393
12677
|
} : void 0,
|
|
12394
12678
|
onPointerOut: acceptsPointer ? (event) => {
|
|
12395
12679
|
if (!dragStateRef.current) {
|
|
@@ -12418,7 +12702,10 @@ function HotspotAnchor({
|
|
|
12418
12702
|
);
|
|
12419
12703
|
}
|
|
12420
12704
|
} : void 0,
|
|
12421
|
-
onPointerUp: acceptsPointer ? (event) =>
|
|
12705
|
+
onPointerUp: acceptsPointer ? (event) => {
|
|
12706
|
+
stopPointerEvent(event);
|
|
12707
|
+
endDragRef.current(event.nativeEvent, true);
|
|
12708
|
+
} : void 0,
|
|
12422
12709
|
children: [
|
|
12423
12710
|
focused ? focusContent ?? /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
|
|
12424
12711
|
"lineSegments",
|
|
@@ -12431,16 +12718,16 @@ function HotspotAnchor({
|
|
|
12431
12718
|
}
|
|
12432
12719
|
) : null,
|
|
12433
12720
|
children,
|
|
12434
|
-
selected && useAngularScale ? /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
|
|
12721
|
+
selected && useAngularScale ? /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("group", { ref: selectionFrameRef, children: /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
|
|
12435
12722
|
HotspotStrokeLine,
|
|
12436
12723
|
{
|
|
12437
12724
|
points: SELECTION_BORDER_POINTS,
|
|
12438
12725
|
color: "#ffb300",
|
|
12439
|
-
lineWidth:
|
|
12726
|
+
lineWidth: SELECTION_BORDER_WIDTH,
|
|
12440
12727
|
opacity: 1,
|
|
12441
12728
|
renderOrder: renderOrder + 1
|
|
12442
12729
|
}
|
|
12443
|
-
) : null,
|
|
12730
|
+
) }) : null,
|
|
12444
12731
|
tooltipOpen && tooltipContent ? /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("group", { ref: tooltipAnchorRef, children: /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
|
|
12445
12732
|
HotspotTooltip,
|
|
12446
12733
|
{
|
|
@@ -13943,11 +14230,15 @@ function createTextTexture(text, width, height, style, rasterLong) {
|
|
|
13943
14230
|
if (!context) {
|
|
13944
14231
|
throw new Error("Canvas 2D is unavailable for this text hotspot.");
|
|
13945
14232
|
}
|
|
13946
|
-
|
|
13947
|
-
|
|
13948
|
-
|
|
13949
|
-
|
|
13950
|
-
|
|
14233
|
+
context.setTransform(
|
|
14234
|
+
canvasWidth / layout.canvasWidth,
|
|
14235
|
+
0,
|
|
14236
|
+
0,
|
|
14237
|
+
canvasHeight / layout.canvasHeight,
|
|
14238
|
+
0,
|
|
14239
|
+
0
|
|
14240
|
+
);
|
|
14241
|
+
drawTextHotspot(context, layout.canvasWidth, layout.canvasHeight, text, style);
|
|
13951
14242
|
const texture = new import_three32.CanvasTexture(canvas);
|
|
13952
14243
|
texture.colorSpace = import_three32.SRGBColorSpace;
|
|
13953
14244
|
texture.wrapS = import_three32.ClampToEdgeWrapping;
|
|
@@ -14007,13 +14298,7 @@ function useTextTexture(text, width, height, style, scale, scaleMode, referenceF
|
|
|
14007
14298
|
let nextTexture = null;
|
|
14008
14299
|
const run = async () => {
|
|
14009
14300
|
try {
|
|
14010
|
-
const
|
|
14011
|
-
const layoutLong = Math.max(layout.canvasWidth, layout.canvasHeight);
|
|
14012
|
-
const drawScale = rasterLong / layoutLong;
|
|
14013
|
-
const { font } = resolveTextFont({
|
|
14014
|
-
...style,
|
|
14015
|
-
fontSize: resolvePositive(style.fontSize, DEFAULT_FONT_SIZE) * drawScale
|
|
14016
|
-
});
|
|
14301
|
+
const { font } = resolveTextFont(style);
|
|
14017
14302
|
try {
|
|
14018
14303
|
await document.fonts?.load(font);
|
|
14019
14304
|
} catch {
|