@ericchen1990/pano-view 3.6.3 → 3.6.4
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 +346 -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 +367 -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,93 @@ 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
|
+
function suppressFollowingClick(target) {
|
|
11557
|
+
let cleaned = false;
|
|
11558
|
+
const cleanup = () => {
|
|
11559
|
+
if (cleaned) {
|
|
11560
|
+
return;
|
|
11561
|
+
}
|
|
11562
|
+
cleaned = true;
|
|
11563
|
+
target.removeEventListener("click", swallowClick, true);
|
|
11564
|
+
target.removeEventListener("pointerdown", cancel, true);
|
|
11565
|
+
};
|
|
11566
|
+
const swallowClick = (event) => {
|
|
11567
|
+
event.preventDefault();
|
|
11568
|
+
event.stopPropagation();
|
|
11569
|
+
event.stopImmediatePropagation();
|
|
11570
|
+
cleanup();
|
|
11571
|
+
};
|
|
11572
|
+
const cancel = () => {
|
|
11573
|
+
cleanup();
|
|
11574
|
+
};
|
|
11575
|
+
target.addEventListener("click", swallowClick, true);
|
|
11576
|
+
target.addEventListener("pointerdown", cancel, true);
|
|
11577
|
+
setTimeout(cleanup, CLICK_SUPPRESS_MS);
|
|
11578
|
+
}
|
|
11579
|
+
|
|
11439
11580
|
// src/hotspot/hotspot-tooltip.tsx
|
|
11440
11581
|
var import_react40 = require("react");
|
|
11441
11582
|
|
|
@@ -11695,6 +11836,21 @@ function fixedScaleFactor(cameraFov, referenceFov) {
|
|
|
11695
11836
|
const safeReferenceFov = import_three22.MathUtils.clamp(referenceFov, 1, 179);
|
|
11696
11837
|
return Math.tan(import_three22.MathUtils.degToRad(cameraFov) / 2) / Math.tan(import_three22.MathUtils.degToRad(safeReferenceFov) / 2);
|
|
11697
11838
|
}
|
|
11839
|
+
function selectionFrameScale(worldScaleX, worldScaleY, cameraSpaceDepth, fovDegrees, viewportHeight, outsetPixels) {
|
|
11840
|
+
const pixels = Number.isFinite(outsetPixels) && outsetPixels > 0 ? outsetPixels : 0;
|
|
11841
|
+
const depth = Number.isFinite(cameraSpaceDepth) ? Math.max(cameraSpaceDepth, 1e-4) : 1e-4;
|
|
11842
|
+
const height = Number.isFinite(viewportHeight) ? Math.max(viewportHeight, 1) : 1;
|
|
11843
|
+
const fov = Number.isFinite(fovDegrees) ? import_three22.MathUtils.clamp(fovDegrees, 1, 179) : 75;
|
|
11844
|
+
const pixelsPerWorldUnit = height / (2 * Math.tan(import_three22.MathUtils.degToRad(fov) * 0.5) * depth);
|
|
11845
|
+
const axis = (worldScale) => {
|
|
11846
|
+
const pixelsPerLocalUnit = pixelsPerWorldUnit * Math.abs(worldScale);
|
|
11847
|
+
if (pixelsPerLocalUnit < 1e-4) {
|
|
11848
|
+
return 1;
|
|
11849
|
+
}
|
|
11850
|
+
return 1 + 2 * pixels / pixelsPerLocalUnit;
|
|
11851
|
+
};
|
|
11852
|
+
return [axis(worldScaleX), axis(worldScaleY)];
|
|
11853
|
+
}
|
|
11698
11854
|
|
|
11699
11855
|
// src/hotspot/hotspot-stroke-line.tsx
|
|
11700
11856
|
var import_drei = require("@react-three/drei");
|
|
@@ -11777,6 +11933,11 @@ var DEFAULT_REFERENCE_FOV = 75;
|
|
|
11777
11933
|
var MIN_ANGULAR_SIZE = 0.01;
|
|
11778
11934
|
var MAX_ANGULAR_SIZE = 179;
|
|
11779
11935
|
var DRAG_EPSILON_DEGREES = 1e-3;
|
|
11936
|
+
var SELECTION_BORDER_WIDTH = 3;
|
|
11937
|
+
var SELECTION_BORDER_GAP = 3;
|
|
11938
|
+
var SELECTION_BORDER_OUTSET = SELECTION_BORDER_GAP + SELECTION_BORDER_WIDTH / 2;
|
|
11939
|
+
var SELECTION_CAMERA_POSITION = new import_three24.Vector3();
|
|
11940
|
+
var SELECTION_WORLD_SCALE = new import_three24.Vector3();
|
|
11780
11941
|
var SELECTION_BORDER_POINTS = [
|
|
11781
11942
|
[-0.5, -0.5, 1e-3],
|
|
11782
11943
|
[0.5, -0.5, 1e-3],
|
|
@@ -11864,6 +12025,34 @@ function angularDistance(a, b) {
|
|
|
11864
12025
|
const bVector = panoPositionToVector3(b);
|
|
11865
12026
|
return import_three24.MathUtils.radToDeg(aVector.angleTo(bVector));
|
|
11866
12027
|
}
|
|
12028
|
+
var DRAG_POINTER = new import_three24.Vector2();
|
|
12029
|
+
var DRAG_RAYCASTER = new import_three24.Raycaster();
|
|
12030
|
+
function aimDragRaycaster(event, element, camera) {
|
|
12031
|
+
const rect = element.getBoundingClientRect();
|
|
12032
|
+
if (!(rect.width > 0) || !(rect.height > 0)) {
|
|
12033
|
+
return false;
|
|
12034
|
+
}
|
|
12035
|
+
DRAG_POINTER.set(
|
|
12036
|
+
(event.clientX - rect.left) / rect.width * 2 - 1,
|
|
12037
|
+
-((event.clientY - rect.top) / rect.height) * 2 + 1
|
|
12038
|
+
);
|
|
12039
|
+
camera.updateMatrixWorld();
|
|
12040
|
+
DRAG_RAYCASTER.setFromCamera(DRAG_POINTER, camera);
|
|
12041
|
+
return true;
|
|
12042
|
+
}
|
|
12043
|
+
function pointerToPanoPosition(event, element, camera) {
|
|
12044
|
+
if (!aimDragRaycaster(event, element, camera)) {
|
|
12045
|
+
return null;
|
|
12046
|
+
}
|
|
12047
|
+
return vector3ToPanoPosition(DRAG_RAYCASTER.ray.direction);
|
|
12048
|
+
}
|
|
12049
|
+
function pointerHitsHotspot(event, element, camera, group) {
|
|
12050
|
+
if (!group || !aimDragRaycaster(event, element, camera)) {
|
|
12051
|
+
return false;
|
|
12052
|
+
}
|
|
12053
|
+
group.updateMatrixWorld(true);
|
|
12054
|
+
return DRAG_RAYCASTER.intersectObject(group, true).length > 0;
|
|
12055
|
+
}
|
|
11867
12056
|
function isTooltipOpen(trigger, hovered, focused, pinned) {
|
|
11868
12057
|
switch (trigger) {
|
|
11869
12058
|
case "always":
|
|
@@ -12059,6 +12248,24 @@ function updateTooltipAnchor(anchor, group, placement, useAngularScale, camera,
|
|
|
12059
12248
|
}
|
|
12060
12249
|
setTooltipAnchorOnLocalBox(anchor, placement, -0.5, 0.5, -0.5, 0.5);
|
|
12061
12250
|
}
|
|
12251
|
+
function placeSelectionFrame(frame, group, camera, viewportHeight) {
|
|
12252
|
+
if (!frame) {
|
|
12253
|
+
return;
|
|
12254
|
+
}
|
|
12255
|
+
group.getWorldScale(SELECTION_WORLD_SCALE);
|
|
12256
|
+
SELECTION_CAMERA_POSITION.setFromMatrixPosition(group.matrixWorld).applyMatrix4(
|
|
12257
|
+
camera.matrixWorldInverse
|
|
12258
|
+
);
|
|
12259
|
+
const [scaleX, scaleY] = selectionFrameScale(
|
|
12260
|
+
SELECTION_WORLD_SCALE.x,
|
|
12261
|
+
SELECTION_WORLD_SCALE.y,
|
|
12262
|
+
-SELECTION_CAMERA_POSITION.z,
|
|
12263
|
+
camera instanceof import_three24.PerspectiveCamera ? camera.fov : DEFAULT_REFERENCE_FOV,
|
|
12264
|
+
viewportHeight,
|
|
12265
|
+
SELECTION_BORDER_OUTSET
|
|
12266
|
+
);
|
|
12267
|
+
frame.scale.set(scaleX, scaleY, 1);
|
|
12268
|
+
}
|
|
12062
12269
|
function HotspotAnchor({
|
|
12063
12270
|
id,
|
|
12064
12271
|
position,
|
|
@@ -12098,10 +12305,13 @@ function HotspotAnchor({
|
|
|
12098
12305
|
const hotspotTargetRegistry = (0, import_react42.useContext)(HotspotTargetRegistryContext);
|
|
12099
12306
|
const eventBus = (0, import_react42.useContext)(PanoEventBusContext);
|
|
12100
12307
|
const cursorApi = usePanoCursor();
|
|
12101
|
-
const { camera, size } = (0, import_fiber21.useThree)();
|
|
12308
|
+
const { camera, gl, size } = (0, import_fiber21.useThree)();
|
|
12102
12309
|
const groupRef = (0, import_react42.useRef)(null);
|
|
12310
|
+
const selectionFrameRef = (0, import_react42.useRef)(null);
|
|
12103
12311
|
const tooltipAnchorRef = (0, import_react42.useRef)(null);
|
|
12104
12312
|
const dragStateRef = (0, import_react42.useRef)(null);
|
|
12313
|
+
const updateDragRef = (0, import_react42.useRef)(() => void 0);
|
|
12314
|
+
const endDragRef = (0, import_react42.useRef)(() => void 0);
|
|
12105
12315
|
const suppressNextClickRef = (0, import_react42.useRef)(false);
|
|
12106
12316
|
const [hovered, setHovered] = (0, import_react42.useState)(false);
|
|
12107
12317
|
const [dragging, setDragging] = (0, import_react42.useState)(false);
|
|
@@ -12171,7 +12381,9 @@ function HotspotAnchor({
|
|
|
12171
12381
|
const cursorClaimId = hotspotCursorClaimId(id);
|
|
12172
12382
|
(0, import_react42.useEffect)(
|
|
12173
12383
|
() => () => {
|
|
12174
|
-
dragStateRef.current
|
|
12384
|
+
const dragState = dragStateRef.current;
|
|
12385
|
+
dragState?.stopTracking();
|
|
12386
|
+
dragState?.releaseInteractionLock();
|
|
12175
12387
|
dragStateRef.current = null;
|
|
12176
12388
|
},
|
|
12177
12389
|
[]
|
|
@@ -12228,6 +12440,7 @@ function HotspotAnchor({
|
|
|
12228
12440
|
if (!dragState) {
|
|
12229
12441
|
return;
|
|
12230
12442
|
}
|
|
12443
|
+
dragState.stopTracking();
|
|
12231
12444
|
dragState.releaseInteractionLock();
|
|
12232
12445
|
dragStateRef.current = null;
|
|
12233
12446
|
setDragging(false);
|
|
@@ -12250,6 +12463,7 @@ function HotspotAnchor({
|
|
|
12250
12463
|
worldHeight,
|
|
12251
12464
|
resolvedDistance
|
|
12252
12465
|
);
|
|
12466
|
+
placeSelectionFrame(selectionFrameRef.current, group, camera, size.height);
|
|
12253
12467
|
if (tooltipOpen && tooltipAnchorRef.current) {
|
|
12254
12468
|
updateTooltipAnchor(
|
|
12255
12469
|
tooltipAnchorRef.current,
|
|
@@ -12268,6 +12482,7 @@ function HotspotAnchor({
|
|
|
12268
12482
|
rotation,
|
|
12269
12483
|
scaleMode,
|
|
12270
12484
|
size,
|
|
12485
|
+
selected,
|
|
12271
12486
|
surfaceQuaternion3,
|
|
12272
12487
|
tooltipOpen,
|
|
12273
12488
|
tooltipPlacement,
|
|
@@ -12293,6 +12508,12 @@ function HotspotAnchor({
|
|
|
12293
12508
|
worldHeight,
|
|
12294
12509
|
resolvedDistance
|
|
12295
12510
|
);
|
|
12511
|
+
placeSelectionFrame(
|
|
12512
|
+
selectionFrameRef.current,
|
|
12513
|
+
group,
|
|
12514
|
+
state.camera,
|
|
12515
|
+
state.size.height
|
|
12516
|
+
);
|
|
12296
12517
|
if (tooltipOpen && tooltipAnchorRef.current) {
|
|
12297
12518
|
updateTooltipAnchor(
|
|
12298
12519
|
tooltipAnchorRef.current,
|
|
@@ -12308,31 +12529,78 @@ function HotspotAnchor({
|
|
|
12308
12529
|
event.stopPropagation();
|
|
12309
12530
|
event.nativeEvent.stopImmediatePropagation?.();
|
|
12310
12531
|
};
|
|
12311
|
-
const
|
|
12532
|
+
const dragPosition = (dragState, event) => {
|
|
12533
|
+
const pointerPosition = pointerToPanoPosition(event, gl.domElement, camera);
|
|
12534
|
+
if (!pointerPosition) {
|
|
12535
|
+
return dragState.lastPosition;
|
|
12536
|
+
}
|
|
12537
|
+
return translateHotspotPosition(
|
|
12538
|
+
dragState.startPosition,
|
|
12539
|
+
dragState.pointerOrigin,
|
|
12540
|
+
pointerPosition
|
|
12541
|
+
);
|
|
12542
|
+
};
|
|
12543
|
+
updateDragRef.current = (nativeEvent) => {
|
|
12312
12544
|
const dragState = dragStateRef.current;
|
|
12313
|
-
if (!dragState || dragState.pointerId !==
|
|
12545
|
+
if (!dragState || dragState.pointerId !== nativeEvent.pointerId) {
|
|
12314
12546
|
return;
|
|
12315
12547
|
}
|
|
12316
|
-
|
|
12317
|
-
|
|
12318
|
-
|
|
12319
|
-
|
|
12320
|
-
|
|
12321
|
-
|
|
12322
|
-
|
|
12323
|
-
|
|
12324
|
-
|
|
12325
|
-
|
|
12326
|
-
|
|
12327
|
-
|
|
12548
|
+
const nextPosition = dragPosition(dragState, nativeEvent);
|
|
12549
|
+
dragState.lastPosition = nextPosition;
|
|
12550
|
+
dragState.moved || (dragState.moved = angularDistance(dragState.startPosition, nextPosition) > DRAG_EPSILON_DEGREES);
|
|
12551
|
+
onPositionChange?.({
|
|
12552
|
+
id,
|
|
12553
|
+
position: nextPosition,
|
|
12554
|
+
source: "pointer",
|
|
12555
|
+
nativeEvent,
|
|
12556
|
+
startPosition: dragState.startPosition
|
|
12557
|
+
});
|
|
12558
|
+
};
|
|
12559
|
+
endDragRef.current = (nativeEvent, emitDragEnd) => {
|
|
12560
|
+
const dragState = dragStateRef.current;
|
|
12561
|
+
if (!dragState || dragState.pointerId !== nativeEvent.pointerId) {
|
|
12562
|
+
return;
|
|
12328
12563
|
}
|
|
12329
|
-
|
|
12564
|
+
const nextPosition = dragPosition(dragState, nativeEvent);
|
|
12565
|
+
dragState.lastPosition = nextPosition;
|
|
12566
|
+
dragState.moved || (dragState.moved = angularDistance(dragState.startPosition, nextPosition) > DRAG_EPSILON_DEGREES);
|
|
12567
|
+
const moved = dragState.moved;
|
|
12568
|
+
const startPosition = dragState.startPosition;
|
|
12569
|
+
dragState.stopTracking();
|
|
12330
12570
|
dragState.releaseInteractionLock();
|
|
12331
12571
|
dragStateRef.current = null;
|
|
12572
|
+
suppressNextClickRef.current = moved;
|
|
12332
12573
|
setDragging(false);
|
|
12333
|
-
if (emitDragEnd) {
|
|
12334
|
-
|
|
12574
|
+
if (!emitDragEnd) {
|
|
12575
|
+
return;
|
|
12576
|
+
}
|
|
12577
|
+
const overHotspot = pointerHitsHotspot(
|
|
12578
|
+
nativeEvent,
|
|
12579
|
+
gl.domElement,
|
|
12580
|
+
camera,
|
|
12581
|
+
groupRef.current
|
|
12582
|
+
);
|
|
12583
|
+
eventHovered.current = overHotspot;
|
|
12584
|
+
setHovered(overHotspot);
|
|
12585
|
+
if (moved && typeof window !== "undefined") {
|
|
12586
|
+
suppressFollowingClick(window);
|
|
12587
|
+
}
|
|
12588
|
+
if (moved) {
|
|
12589
|
+
onPositionChange?.({
|
|
12590
|
+
id,
|
|
12591
|
+
position: nextPosition,
|
|
12592
|
+
source: "pointer",
|
|
12593
|
+
nativeEvent,
|
|
12594
|
+
startPosition
|
|
12595
|
+
});
|
|
12335
12596
|
}
|
|
12597
|
+
onDragEnd?.({
|
|
12598
|
+
id,
|
|
12599
|
+
position: nextPosition,
|
|
12600
|
+
source: "pointer",
|
|
12601
|
+
nativeEvent,
|
|
12602
|
+
startPosition
|
|
12603
|
+
});
|
|
12336
12604
|
};
|
|
12337
12605
|
return /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)(
|
|
12338
12606
|
"group",
|
|
@@ -12352,23 +12620,42 @@ function HotspotAnchor({
|
|
|
12352
12620
|
}
|
|
12353
12621
|
suppressNextClickRef.current = false;
|
|
12354
12622
|
} : void 0,
|
|
12355
|
-
onLostPointerCapture: acceptsPointer ? (event) =>
|
|
12356
|
-
|
|
12623
|
+
onLostPointerCapture: acceptsPointer ? (event) => {
|
|
12624
|
+
endDragRef.current(event.nativeEvent, true);
|
|
12625
|
+
} : void 0,
|
|
12626
|
+
onPointerCancel: acceptsPointer ? (event) => {
|
|
12627
|
+
endDragRef.current(event.nativeEvent, true);
|
|
12628
|
+
} : void 0,
|
|
12357
12629
|
onPointerDown: acceptsPointer ? (event) => {
|
|
12358
|
-
|
|
12630
|
+
const native = event.nativeEvent;
|
|
12359
12631
|
if (!draggable || dragStateRef.current) {
|
|
12632
|
+
stopPointerEvent(event);
|
|
12360
12633
|
return;
|
|
12361
12634
|
}
|
|
12635
|
+
const pointerId = native.pointerId;
|
|
12636
|
+
stopPointerEvent(event);
|
|
12362
12637
|
const releaseInteractionLock = controlsRef?.current?.acquireInteractionLock() ?? (() => void 0);
|
|
12363
|
-
const
|
|
12364
|
-
target.setPointerCapture?.(event.pointerId);
|
|
12638
|
+
const pointerOrigin = pointerToPanoPosition(native, gl.domElement, camera) ?? vector3ToPanoPosition(event.ray.direction);
|
|
12365
12639
|
const startPosition = { ...normalizedPosition };
|
|
12640
|
+
const tracksOutsideMesh = typeof window !== "undefined";
|
|
12641
|
+
const stopTracking = tracksOutsideMesh ? trackPointerDrag(
|
|
12642
|
+
window,
|
|
12643
|
+
gl.domElement,
|
|
12644
|
+
pointerId,
|
|
12645
|
+
(pointerEvent) => updateDragRef.current(pointerEvent),
|
|
12646
|
+
(pointerEvent) => endDragRef.current(pointerEvent, true)
|
|
12647
|
+
) : () => void 0;
|
|
12366
12648
|
dragStateRef.current = {
|
|
12367
|
-
pointerId
|
|
12649
|
+
pointerId,
|
|
12368
12650
|
startPosition,
|
|
12651
|
+
pointerOrigin,
|
|
12652
|
+
lastPosition: startPosition,
|
|
12369
12653
|
releaseInteractionLock,
|
|
12370
|
-
moved: false
|
|
12654
|
+
moved: false,
|
|
12655
|
+
tracksOutsideMesh,
|
|
12656
|
+
stopTracking
|
|
12371
12657
|
};
|
|
12658
|
+
eventHovered.current = false;
|
|
12372
12659
|
setHovered(false);
|
|
12373
12660
|
setDragging(true);
|
|
12374
12661
|
onDragStart?.({
|
|
@@ -12379,17 +12666,13 @@ function HotspotAnchor({
|
|
|
12379
12666
|
} : void 0,
|
|
12380
12667
|
onPointerMove: acceptsPointer ? (event) => {
|
|
12381
12668
|
const dragState = dragStateRef.current;
|
|
12382
|
-
if (!dragState || dragState.pointerId !== event.pointerId) {
|
|
12669
|
+
if (!dragState || dragState.pointerId !== event.nativeEvent.pointerId) {
|
|
12383
12670
|
return;
|
|
12384
12671
|
}
|
|
12385
12672
|
stopPointerEvent(event);
|
|
12386
|
-
|
|
12387
|
-
|
|
12388
|
-
|
|
12389
|
-
...makeInteractionEvent(id, nextPosition, event),
|
|
12390
|
-
source: "pointer",
|
|
12391
|
-
startPosition: dragState.startPosition
|
|
12392
|
-
});
|
|
12673
|
+
if (!dragState.tracksOutsideMesh) {
|
|
12674
|
+
updateDragRef.current(event.nativeEvent);
|
|
12675
|
+
}
|
|
12393
12676
|
} : void 0,
|
|
12394
12677
|
onPointerOut: acceptsPointer ? (event) => {
|
|
12395
12678
|
if (!dragStateRef.current) {
|
|
@@ -12418,7 +12701,10 @@ function HotspotAnchor({
|
|
|
12418
12701
|
);
|
|
12419
12702
|
}
|
|
12420
12703
|
} : void 0,
|
|
12421
|
-
onPointerUp: acceptsPointer ? (event) =>
|
|
12704
|
+
onPointerUp: acceptsPointer ? (event) => {
|
|
12705
|
+
stopPointerEvent(event);
|
|
12706
|
+
endDragRef.current(event.nativeEvent, true);
|
|
12707
|
+
} : void 0,
|
|
12422
12708
|
children: [
|
|
12423
12709
|
focused ? focusContent ?? /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
|
|
12424
12710
|
"lineSegments",
|
|
@@ -12431,16 +12717,16 @@ function HotspotAnchor({
|
|
|
12431
12717
|
}
|
|
12432
12718
|
) : null,
|
|
12433
12719
|
children,
|
|
12434
|
-
selected && useAngularScale ? /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
|
|
12720
|
+
selected && useAngularScale ? /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("group", { ref: selectionFrameRef, children: /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
|
|
12435
12721
|
HotspotStrokeLine,
|
|
12436
12722
|
{
|
|
12437
12723
|
points: SELECTION_BORDER_POINTS,
|
|
12438
12724
|
color: "#ffb300",
|
|
12439
|
-
lineWidth:
|
|
12725
|
+
lineWidth: SELECTION_BORDER_WIDTH,
|
|
12440
12726
|
opacity: 1,
|
|
12441
12727
|
renderOrder: renderOrder + 1
|
|
12442
12728
|
}
|
|
12443
|
-
) : null,
|
|
12729
|
+
) }) : null,
|
|
12444
12730
|
tooltipOpen && tooltipContent ? /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("group", { ref: tooltipAnchorRef, children: /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
|
|
12445
12731
|
HotspotTooltip,
|
|
12446
12732
|
{
|
|
@@ -13943,11 +14229,15 @@ function createTextTexture(text, width, height, style, rasterLong) {
|
|
|
13943
14229
|
if (!context) {
|
|
13944
14230
|
throw new Error("Canvas 2D is unavailable for this text hotspot.");
|
|
13945
14231
|
}
|
|
13946
|
-
|
|
13947
|
-
|
|
13948
|
-
|
|
13949
|
-
|
|
13950
|
-
|
|
14232
|
+
context.setTransform(
|
|
14233
|
+
canvasWidth / layout.canvasWidth,
|
|
14234
|
+
0,
|
|
14235
|
+
0,
|
|
14236
|
+
canvasHeight / layout.canvasHeight,
|
|
14237
|
+
0,
|
|
14238
|
+
0
|
|
14239
|
+
);
|
|
14240
|
+
drawTextHotspot(context, layout.canvasWidth, layout.canvasHeight, text, style);
|
|
13951
14241
|
const texture = new import_three32.CanvasTexture(canvas);
|
|
13952
14242
|
texture.colorSpace = import_three32.SRGBColorSpace;
|
|
13953
14243
|
texture.wrapS = import_three32.ClampToEdgeWrapping;
|
|
@@ -14007,13 +14297,7 @@ function useTextTexture(text, width, height, style, scale, scaleMode, referenceF
|
|
|
14007
14297
|
let nextTexture = null;
|
|
14008
14298
|
const run = async () => {
|
|
14009
14299
|
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
|
-
});
|
|
14300
|
+
const { font } = resolveTextFont(style);
|
|
14017
14301
|
try {
|
|
14018
14302
|
await document.fonts?.load(font);
|
|
14019
14303
|
} catch {
|