@project-skymap/library 0.7.0 → 0.7.2
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/dist/index.cjs +92 -16
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +92 -16
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -844,12 +844,19 @@ var createEngine_exports = {};
|
|
|
844
844
|
__export(createEngine_exports, {
|
|
845
845
|
createEngine: () => createEngine
|
|
846
846
|
});
|
|
847
|
+
function triggerHaptic(style = "light") {
|
|
848
|
+
if (typeof navigator !== "undefined" && "vibrate" in navigator) {
|
|
849
|
+
const durations = { light: 10, medium: 25, heavy: 50 };
|
|
850
|
+
navigator.vibrate(durations[style]);
|
|
851
|
+
}
|
|
852
|
+
}
|
|
847
853
|
function createEngine({
|
|
848
854
|
container,
|
|
849
855
|
onSelect,
|
|
850
856
|
onHover,
|
|
851
857
|
onArrangementChange,
|
|
852
|
-
onFovChange
|
|
858
|
+
onFovChange,
|
|
859
|
+
onLongPress
|
|
853
860
|
}) {
|
|
854
861
|
let hoveredBookId = null;
|
|
855
862
|
let focusedBookId = null;
|
|
@@ -919,13 +926,20 @@ function createEngine({
|
|
|
919
926
|
pinchStartDistance: 0,
|
|
920
927
|
pinchStartFov: ENGINE_CONFIG.defaultFov,
|
|
921
928
|
pinchCenterX: 0,
|
|
922
|
-
pinchCenterY: 0
|
|
929
|
+
pinchCenterY: 0,
|
|
930
|
+
// Double-tap detection
|
|
931
|
+
lastTapTime: 0,
|
|
932
|
+
lastTapX: 0,
|
|
933
|
+
lastTapY: 0,
|
|
934
|
+
// Long-press detection
|
|
935
|
+
longPressTimer: null,
|
|
936
|
+
longPressTriggered: false
|
|
923
937
|
};
|
|
924
938
|
const mouseNDC = new THREE5__namespace.Vector2();
|
|
925
939
|
let isMouseInWindow = false;
|
|
926
940
|
let isTouchDevice = false;
|
|
927
941
|
let edgeHoverStart = 0;
|
|
928
|
-
let handlers = { onSelect, onHover, onArrangementChange, onFovChange };
|
|
942
|
+
let handlers = { onSelect, onHover, onArrangementChange, onFovChange, onLongPress };
|
|
929
943
|
let currentConfig;
|
|
930
944
|
const constellationLayer = new ConstellationArtworkLayer(scene);
|
|
931
945
|
function mix(a, b, t) {
|
|
@@ -2418,6 +2432,11 @@ function createEngine({
|
|
|
2418
2432
|
function onTouchStart(e) {
|
|
2419
2433
|
e.preventDefault();
|
|
2420
2434
|
isTouchDevice = true;
|
|
2435
|
+
if (state.longPressTimer) {
|
|
2436
|
+
clearTimeout(state.longPressTimer);
|
|
2437
|
+
state.longPressTimer = null;
|
|
2438
|
+
}
|
|
2439
|
+
state.longPressTriggered = false;
|
|
2421
2440
|
const touches = e.touches;
|
|
2422
2441
|
state.touchCount = touches.length;
|
|
2423
2442
|
if (touches.length === 1) {
|
|
@@ -2433,6 +2452,23 @@ function createEngine({
|
|
|
2433
2452
|
state.isDragging = true;
|
|
2434
2453
|
state.velocityX = 0;
|
|
2435
2454
|
state.velocityY = 0;
|
|
2455
|
+
state.longPressTimer = setTimeout(() => {
|
|
2456
|
+
if (!state.touchMoved && state.touchCount === 1) {
|
|
2457
|
+
state.longPressTriggered = true;
|
|
2458
|
+
const rect = renderer.domElement.getBoundingClientRect();
|
|
2459
|
+
const mX = state.touchStartX - rect.left;
|
|
2460
|
+
const mY = state.touchStartY - rect.top;
|
|
2461
|
+
mouseNDC.x = mX / rect.width * 2 - 1;
|
|
2462
|
+
mouseNDC.y = -(mY / rect.height) * 2 + 1;
|
|
2463
|
+
const syntheticEvent = {
|
|
2464
|
+
clientX: state.touchStartX,
|
|
2465
|
+
clientY: state.touchStartY
|
|
2466
|
+
};
|
|
2467
|
+
const hit = pick(syntheticEvent);
|
|
2468
|
+
triggerHaptic("heavy");
|
|
2469
|
+
handlers.onLongPress?.(hit?.node ?? null, state.touchStartX, state.touchStartY);
|
|
2470
|
+
}
|
|
2471
|
+
}, ENGINE_CONFIG.longPressDelay);
|
|
2436
2472
|
} else if (touches.length === 2) {
|
|
2437
2473
|
const t0 = touches[0];
|
|
2438
2474
|
const t1 = touches[1];
|
|
@@ -2459,6 +2495,10 @@ function createEngine({
|
|
|
2459
2495
|
const totalDy = touch.clientY - state.touchStartY;
|
|
2460
2496
|
if (Math.sqrt(totalDx * totalDx + totalDy * totalDy) > ENGINE_CONFIG.tapMaxDistance) {
|
|
2461
2497
|
state.touchMoved = true;
|
|
2498
|
+
if (state.longPressTimer) {
|
|
2499
|
+
clearTimeout(state.longPressTimer);
|
|
2500
|
+
state.longPressTimer = null;
|
|
2501
|
+
}
|
|
2462
2502
|
}
|
|
2463
2503
|
const speedScale = state.fov / ENGINE_CONFIG.defaultFov;
|
|
2464
2504
|
const rotLock = Math.max(0, Math.min(1, (state.fov - 100) / (ENGINE_CONFIG.maxFov - 100)));
|
|
@@ -2493,11 +2533,21 @@ function createEngine({
|
|
|
2493
2533
|
}
|
|
2494
2534
|
function onTouchEnd(e) {
|
|
2495
2535
|
e.preventDefault();
|
|
2536
|
+
if (state.longPressTimer) {
|
|
2537
|
+
clearTimeout(state.longPressTimer);
|
|
2538
|
+
state.longPressTimer = null;
|
|
2539
|
+
}
|
|
2496
2540
|
const remainingTouches = e.touches.length;
|
|
2497
2541
|
if (remainingTouches === 0) {
|
|
2498
|
-
const
|
|
2499
|
-
const
|
|
2542
|
+
const now = performance.now();
|
|
2543
|
+
const duration = now - state.touchStartTime;
|
|
2544
|
+
const wasTap = !state.touchMoved && duration < ENGINE_CONFIG.tapMaxDuration && !state.longPressTriggered;
|
|
2500
2545
|
if (wasTap) {
|
|
2546
|
+
const timeSinceLastTap = now - state.lastTapTime;
|
|
2547
|
+
const distFromLastTap = Math.sqrt(
|
|
2548
|
+
Math.pow(state.touchStartX - state.lastTapX, 2) + Math.pow(state.touchStartY - state.lastTapY, 2)
|
|
2549
|
+
);
|
|
2550
|
+
const isDoubleTap = timeSinceLastTap < ENGINE_CONFIG.doubleTapMaxDelay && distFromLastTap < ENGINE_CONFIG.doubleTapMaxDistance;
|
|
2501
2551
|
const rect = renderer.domElement.getBoundingClientRect();
|
|
2502
2552
|
const mX = state.touchStartX - rect.left;
|
|
2503
2553
|
const mY = state.touchStartY - rect.top;
|
|
@@ -2508,13 +2558,28 @@ function createEngine({
|
|
|
2508
2558
|
clientY: state.touchStartY
|
|
2509
2559
|
};
|
|
2510
2560
|
const hit = pick(syntheticEvent);
|
|
2511
|
-
if (
|
|
2512
|
-
|
|
2513
|
-
|
|
2514
|
-
|
|
2515
|
-
|
|
2561
|
+
if (isDoubleTap) {
|
|
2562
|
+
if (hit) {
|
|
2563
|
+
triggerHaptic("medium");
|
|
2564
|
+
flyTo(hit.node.id, ENGINE_CONFIG.minFov);
|
|
2565
|
+
handlers.onSelect?.(hit.node);
|
|
2566
|
+
}
|
|
2567
|
+
state.lastTapTime = 0;
|
|
2568
|
+
state.lastTapX = 0;
|
|
2569
|
+
state.lastTapY = 0;
|
|
2516
2570
|
} else {
|
|
2517
|
-
|
|
2571
|
+
if (hit) {
|
|
2572
|
+
triggerHaptic("light");
|
|
2573
|
+
handlers.onSelect?.(hit.node);
|
|
2574
|
+
constellationLayer.setFocused(hit.node.id);
|
|
2575
|
+
if (hit.node.level === 2) setFocusedBook(hit.node.id);
|
|
2576
|
+
else if (hit.node.level === 3 && hit.node.parent) setFocusedBook(hit.node.parent);
|
|
2577
|
+
} else {
|
|
2578
|
+
setFocusedBook(null);
|
|
2579
|
+
}
|
|
2580
|
+
state.lastTapTime = now;
|
|
2581
|
+
state.lastTapX = state.touchStartX;
|
|
2582
|
+
state.lastTapY = state.touchStartY;
|
|
2518
2583
|
}
|
|
2519
2584
|
}
|
|
2520
2585
|
state.isDragging = false;
|
|
@@ -2533,6 +2598,10 @@ function createEngine({
|
|
|
2533
2598
|
}
|
|
2534
2599
|
function onTouchCancel(e) {
|
|
2535
2600
|
e.preventDefault();
|
|
2601
|
+
if (state.longPressTimer) {
|
|
2602
|
+
clearTimeout(state.longPressTimer);
|
|
2603
|
+
state.longPressTimer = null;
|
|
2604
|
+
}
|
|
2536
2605
|
state.isDragging = false;
|
|
2537
2606
|
state.dragMode = "none";
|
|
2538
2607
|
state.touchCount = 0;
|
|
@@ -2918,8 +2987,14 @@ var init_createEngine = __esm({
|
|
|
2918
2987
|
// Snappier than mouse (0.92)
|
|
2919
2988
|
tapMaxDuration: 300,
|
|
2920
2989
|
// ms
|
|
2921
|
-
tapMaxDistance: 10
|
|
2990
|
+
tapMaxDistance: 10,
|
|
2922
2991
|
// px
|
|
2992
|
+
doubleTapMaxDelay: 300,
|
|
2993
|
+
// ms between taps
|
|
2994
|
+
doubleTapMaxDistance: 30,
|
|
2995
|
+
// px between tap locations
|
|
2996
|
+
longPressDelay: 500
|
|
2997
|
+
// ms to trigger long-press
|
|
2923
2998
|
};
|
|
2924
2999
|
ORDER_REVEAL_CONFIG = {
|
|
2925
3000
|
globalDim: 0.85,
|
|
@@ -2930,7 +3005,7 @@ var init_createEngine = __esm({
|
|
|
2930
3005
|
}
|
|
2931
3006
|
});
|
|
2932
3007
|
var StarMap = react.forwardRef(
|
|
2933
|
-
({ config, className, onSelect, onHover, onArrangementChange, onFovChange }, ref) => {
|
|
3008
|
+
({ config, className, onSelect, onHover, onArrangementChange, onFovChange, onLongPress }, ref) => {
|
|
2934
3009
|
const containerRef = react.useRef(null);
|
|
2935
3010
|
const engineRef = react.useRef(null);
|
|
2936
3011
|
react.useImperativeHandle(ref, () => ({
|
|
@@ -2953,7 +3028,8 @@ var StarMap = react.forwardRef(
|
|
|
2953
3028
|
onSelect,
|
|
2954
3029
|
onHover,
|
|
2955
3030
|
onArrangementChange,
|
|
2956
|
-
onFovChange
|
|
3031
|
+
onFovChange,
|
|
3032
|
+
onLongPress
|
|
2957
3033
|
});
|
|
2958
3034
|
engineRef.current.setConfig(config);
|
|
2959
3035
|
engineRef.current.start();
|
|
@@ -2969,8 +3045,8 @@ var StarMap = react.forwardRef(
|
|
|
2969
3045
|
engineRef.current?.setConfig?.(config);
|
|
2970
3046
|
}, [config]);
|
|
2971
3047
|
react.useEffect(() => {
|
|
2972
|
-
engineRef.current?.setHandlers?.({ onSelect, onHover, onArrangementChange, onFovChange });
|
|
2973
|
-
}, [onSelect, onHover, onArrangementChange, onFovChange]);
|
|
3048
|
+
engineRef.current?.setHandlers?.({ onSelect, onHover, onArrangementChange, onFovChange, onLongPress });
|
|
3049
|
+
}, [onSelect, onHover, onArrangementChange, onFovChange, onLongPress]);
|
|
2974
3050
|
return /* @__PURE__ */ jsxRuntime.jsx("div", { ref: containerRef, className, style: { width: "100%", height: "100%" } });
|
|
2975
3051
|
}
|
|
2976
3052
|
);
|