@ericchen1990/pano-view 3.6.1 → 3.6.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/README.md +1 -1
- package/README.zh-CN.md +1 -1
- package/dist/index.cjs +108 -29
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +108 -29
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -879,7 +879,7 @@ function TourPlayer() {
|
|
|
879
879
|
|
|
880
880
|
The two deprecated auto-rotation handle methods (`startAutoRotate`, `stopAutoRotate`) remain for compatibility. Mouse, touch, and keyboard input are enabled by default — you do not need to render control components for ordinary viewing. Render `MouseControls`, `TouchControls`, or `KeyboardControls` only to override properties (the child replaces that channel's default instance). 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. Auto-rotation is off until you render `AutoRotate`. A default context menu (Reset view / Enter fullscreen) is also mounted — see [`PanoContextMenu`](#panocontextmenu).
|
|
881
881
|
|
|
882
|
-
Mouse, pen, and single-touch drags anchor the grabbed panorama point and update rotation once per render frame, with a short time-based filter (about 10ms of following delay during steady movement) to smooth uneven input samples. The grabbed point aligns
|
|
882
|
+
Mouse, pen, and single-touch drags anchor the grabbed panorama point and update rotation once per render frame, with a short time-based filter (about 10ms of following delay during steady movement) to smooth uneven input samples. The grabbed point aligns once movement settles, and releasing the pointer completes that alignment immediately. Flick inertia follows the latest pointer motion, so a pause before release does not continue an earlier flick. View limits and positions near the poles can restrict tracking. The default `rotateSpeed` of `0.35` gives 1:1 tracking; other values scale pointer displacement relative to it, `0` disables rotation, and `invert` reverses direction. The separate `rotateDamping` setting does not apply during anchored drags; release inertia is retained. Pinch and wheel zoom continue to follow the target FOV smoothly.
|
|
883
883
|
|
|
884
884
|
Other rotation input and zoom update a target view that the camera follows smoothly. `rotateDamping` and `zoomDamping` control that following speed in seconds^-1 (defaults: `14` and `16` respectively); smaller positive values make the camera approach the target more gradually, while `0` disables smoothing for that axis. Both values must be non-negative finite numbers. Imperative `setView()` and `reset()` remain immediate.
|
|
885
885
|
|
package/README.zh-CN.md
CHANGED
|
@@ -852,7 +852,7 @@ function TourPlayer() {
|
|
|
852
852
|
|
|
853
853
|
两个已废弃的自动旋转方法(`startAutoRotate`、`stopAutoRotate`)继续保留以兼容。鼠标、触控与键盘输入默认启用 — 普通浏览无需再渲染控件组件。仅当需要覆盖属性时渲染 `MouseControls`、`TouchControls` 或 `KeyboardControls`(子组件会替换该通道的默认实例)。通过 `controls` 调节共享行为(`inertia`、`invert`、`bouncingLimits`、`fovSpeed`、`frictionStop`、`rotateDamping`、`zoomDamping`,以及顶层的 `rotateSpeed` / `zoomSpeed`)。将 `controls.mouse` / `touch` / `keyboard` 设为 `false` 可关闭某通道,或传入选项对象(含 `enabled`)在不挂载子组件的情况下覆盖默认值。自动旋转在渲染 `AutoRotate` 之前处于关闭状态。默认右键菜单(重置视图 / 进入全屏)也会挂载 — 见 [`PanoContextMenu`](#panocontextmenu)。
|
|
854
854
|
|
|
855
|
-
鼠标、笔和单指拖拽会锚定按下的全景点,并在每个渲染帧更新旋转;使用短暂的时间平滑吸收输入采样的波动(匀速移动时约 10ms
|
|
855
|
+
鼠标、笔和单指拖拽会锚定按下的全景点,并在每个渲染帧更新旋转;使用短暂的时间平滑吸收输入采样的波动(匀速移动时约 10ms 跟随延迟)。指针停住并收敛后,按下点会与指针对齐;松手时也会立刻完成这次对齐。甩动惯性只跟随最近一段指针运动,松手前如果已经停住,不会把更早的速度继续带出去。视角边界或极点附近可能限制跟随。`rotateSpeed` 默认 `0.35` 表示 1:1 跟随,其他值按其相对倍率调整指针位移,`0` 禁止旋转;`invert` 反转方向。双指缩放和滚轮继续更新目标 FOV,相机平滑跟随。拖拽释放后保留惯性。`rotateDamping` 与 `zoomDamping` 控制跟随速度(旋转平滑不作用于锚定拖拽期间),单位为秒⁻¹(默认分别为 `14` 与 `16`);较小的正值使相机更缓慢地接近目标视角,`0` 表示该轴不做平滑。两者必须为非负有限数。命令式 `setView()` 与 `reset()` 仍为即时生效。
|
|
856
856
|
|
|
857
857
|
画布光标默认为 `grab`,鼠标拖拽时为 `grabbing`,悬停在可交互热点上为 `pointer`,拖拽热点时为 `move`。可通过 `cursors`(`default`、`dragging`、`hotspot`、`hotspotDragging`)覆盖,或传 `cursors={false}` 保持画布光标不变。热点可设置 `cursor`,仅替换该热点的悬停光标。
|
|
858
858
|
|
package/dist/index.cjs
CHANGED
|
@@ -2896,7 +2896,12 @@ var PanoramaViewRuntime = (0, import_react3.forwardRef)(function PanoramaViewRun
|
|
|
2896
2896
|
}
|
|
2897
2897
|
}
|
|
2898
2898
|
markDirty();
|
|
2899
|
-
|
|
2899
|
+
const releaseVelocity = applyOptions?.velocity;
|
|
2900
|
+
if (!gyroControlsView && releaseVelocity && Number.isFinite(releaseVelocity.yaw) && Number.isFinite(releaseVelocity.pitch)) {
|
|
2901
|
+
yawVelocityRef.current = releaseVelocity.yaw;
|
|
2902
|
+
pitchVelocityRef.current = releaseVelocity.pitch;
|
|
2903
|
+
lastVelocitySampleAtRef.current = performance.now();
|
|
2904
|
+
} else if (applyOptions?.recordVelocity && !gyroControlsView) {
|
|
2900
2905
|
const now = performance.now();
|
|
2901
2906
|
const previousAt = lastVelocitySampleAtRef.current;
|
|
2902
2907
|
const elapsed = previousAt === null ? 1 / 60 : Math.max((now - previousAt) / 1e3, 1 / 120);
|
|
@@ -3736,6 +3741,26 @@ var import_react9 = require("react");
|
|
|
3736
3741
|
var import_three2 = require("three");
|
|
3737
3742
|
var DEFAULT_POINTER_DRAG_SPEED = 0.35;
|
|
3738
3743
|
var POINTER_FOLLOW_TIME_SECONDS = 0.01;
|
|
3744
|
+
var MIN_VELOCITY_SAMPLE_SECONDS = 1 / 120;
|
|
3745
|
+
function pointerReleaseVelocity(samples, releasedAt, solve, maxAgeMs = POINTER_VELOCITY_MAX_AGE_MS) {
|
|
3746
|
+
if (samples.length < 2) return { yaw: 0, pitch: 0 };
|
|
3747
|
+
const newest = samples[samples.length - 1];
|
|
3748
|
+
if (releasedAt < newest.at || releasedAt - newest.at > maxAgeMs) return { yaw: 0, pitch: 0 };
|
|
3749
|
+
let oldest = newest;
|
|
3750
|
+
for (let index = samples.length - 1; index >= 0; index -= 1) {
|
|
3751
|
+
const sample = samples[index];
|
|
3752
|
+
if (releasedAt - sample.at <= maxAgeMs) oldest = sample;
|
|
3753
|
+
else break;
|
|
3754
|
+
}
|
|
3755
|
+
if (oldest === newest) return { yaw: 0, pitch: 0 };
|
|
3756
|
+
const elapsed = Math.max((newest.at - oldest.at) / 1e3, MIN_VELOCITY_SAMPLE_SECONDS);
|
|
3757
|
+
const from = solve(oldest);
|
|
3758
|
+
const to = solve(newest);
|
|
3759
|
+
return {
|
|
3760
|
+
yaw: normalizeViewYaw(to.yaw - from.yaw) / elapsed,
|
|
3761
|
+
pitch: (to.pitch - from.pitch) / elapsed
|
|
3762
|
+
};
|
|
3763
|
+
}
|
|
3739
3764
|
var PointerDragAnchor = class {
|
|
3740
3765
|
constructor(start, origin, rect, projection) {
|
|
3741
3766
|
this.start = start;
|
|
@@ -3793,8 +3818,11 @@ function createPointerDrag(camera, element, runtime, source) {
|
|
|
3793
3818
|
let lastPoint;
|
|
3794
3819
|
let fresh = false;
|
|
3795
3820
|
let lastFrameDelta = 1 / 60;
|
|
3821
|
+
let samples = [];
|
|
3822
|
+
let gestureSpeed = DEFAULT_POINTER_DRAG_SPEED;
|
|
3823
|
+
let gestureInvert = false;
|
|
3796
3824
|
const key = (rect) => `${rect.left},${rect.top},${rect.width},${rect.height},${camera.projectionMatrix.elements.join(",")}`;
|
|
3797
|
-
const
|
|
3825
|
+
const place = (point) => {
|
|
3798
3826
|
const handle = runtime();
|
|
3799
3827
|
displayed = { ...point };
|
|
3800
3828
|
lastPoint = { ...point };
|
|
@@ -3806,69 +3834,113 @@ function createPointerDrag(camera, element, runtime, source) {
|
|
|
3806
3834
|
projectionKey = key(rect);
|
|
3807
3835
|
anchor = new PointerDragAnchor(point, handle.getView(), rect, camera.clone());
|
|
3808
3836
|
};
|
|
3809
|
-
const
|
|
3837
|
+
const remember = (point, at) => {
|
|
3838
|
+
samples.push({ x: point.x, y: point.y, at });
|
|
3839
|
+
const cutoff = at - POINTER_VELOCITY_MAX_AGE_MS;
|
|
3840
|
+
while (samples.length > 2 && samples[0].at < cutoff) samples.shift();
|
|
3841
|
+
};
|
|
3842
|
+
const apply = (point, previous, speed, invert, recordVelocity, releaseVelocity) => {
|
|
3810
3843
|
const handle = runtime();
|
|
3811
3844
|
if (!handle) return;
|
|
3812
3845
|
const rect = element.getBoundingClientRect();
|
|
3813
3846
|
if (anchor && key(rect) !== projectionKey) {
|
|
3814
3847
|
const queued = pending;
|
|
3815
|
-
|
|
3848
|
+
place(displayed ?? previous);
|
|
3816
3849
|
pending = queued;
|
|
3817
3850
|
}
|
|
3851
|
+
const options = {
|
|
3852
|
+
recordVelocity: releaseVelocity ? false : recordVelocity,
|
|
3853
|
+
source,
|
|
3854
|
+
...releaseVelocity ? { velocity: releaseVelocity } : {}
|
|
3855
|
+
};
|
|
3818
3856
|
if (anchor) {
|
|
3819
3857
|
handle.applyViewDelta(anchor.solve(point, speed, invert), {
|
|
3820
|
-
|
|
3821
|
-
|
|
3822
|
-
source
|
|
3858
|
+
...options,
|
|
3859
|
+
pointerOrigin: anchor.origin
|
|
3823
3860
|
});
|
|
3824
3861
|
} else {
|
|
3825
3862
|
handle.applyViewDelta({
|
|
3826
3863
|
yaw: -(point.x - previous.x) * 360 * speed / Math.max(rect.width, 1) * (invert ? -1 : 1),
|
|
3827
3864
|
pitch: (point.y - previous.y) * 180 * speed / Math.max(rect.height, 1) * (invert ? -1 : 1)
|
|
3828
|
-
},
|
|
3865
|
+
}, options);
|
|
3829
3866
|
}
|
|
3830
3867
|
};
|
|
3868
|
+
const valid = () => revision === (runtime()?.getPointerDragState().revision ?? -1) && !runtime()?.isInteractionLocked();
|
|
3869
|
+
const clear = () => {
|
|
3870
|
+
anchor = void 0;
|
|
3871
|
+
revision = -1;
|
|
3872
|
+
pending = void 0;
|
|
3873
|
+
displayed = void 0;
|
|
3874
|
+
samples = [];
|
|
3875
|
+
};
|
|
3831
3876
|
return {
|
|
3832
|
-
start,
|
|
3833
|
-
|
|
3834
|
-
|
|
3835
|
-
|
|
3836
|
-
|
|
3837
|
-
|
|
3838
|
-
displayed = void 0;
|
|
3877
|
+
start(point, at = performance.now()) {
|
|
3878
|
+
gestureSpeed = DEFAULT_POINTER_DRAG_SPEED;
|
|
3879
|
+
gestureInvert = false;
|
|
3880
|
+
samples = [];
|
|
3881
|
+
remember(point, at);
|
|
3882
|
+
place(point);
|
|
3839
3883
|
},
|
|
3884
|
+
valid,
|
|
3885
|
+
clear,
|
|
3840
3886
|
suspend: () => {
|
|
3841
3887
|
anchor = void 0;
|
|
3842
3888
|
pending = void 0;
|
|
3843
3889
|
displayed = void 0;
|
|
3890
|
+
samples = [];
|
|
3844
3891
|
},
|
|
3845
|
-
move(point, previous, speed, invert) {
|
|
3892
|
+
move(point, previous, speed, invert, at = performance.now()) {
|
|
3846
3893
|
fresh = true;
|
|
3894
|
+
gestureSpeed = speed;
|
|
3895
|
+
gestureInvert = invert;
|
|
3896
|
+
remember(point, at);
|
|
3847
3897
|
pending = { point, previous: lastPoint ?? previous, speed, invert };
|
|
3848
3898
|
},
|
|
3849
|
-
flush(deltaSeconds = lastFrameDelta, immediate = false) {
|
|
3850
|
-
lastFrameDelta = deltaSeconds;
|
|
3899
|
+
flush(deltaSeconds = lastFrameDelta, immediate = false, releaseVelocity) {
|
|
3900
|
+
if (deltaSeconds > 0) lastFrameDelta = deltaSeconds;
|
|
3851
3901
|
if (!pending) return;
|
|
3852
3902
|
const sample = pending;
|
|
3853
3903
|
if (!anchor || immediate) {
|
|
3854
3904
|
pending = void 0;
|
|
3855
|
-
apply(sample.point, sample.previous, sample.speed, sample.invert, fresh);
|
|
3905
|
+
apply(sample.point, sample.previous, sample.speed, sample.invert, fresh, releaseVelocity);
|
|
3856
3906
|
fresh = false;
|
|
3857
3907
|
lastPoint = sample.point;
|
|
3858
3908
|
displayed = sample.point;
|
|
3859
3909
|
return;
|
|
3860
3910
|
}
|
|
3911
|
+
const dt = deltaSeconds > 0 ? deltaSeconds : lastFrameDelta;
|
|
3861
3912
|
const previousDisplay = displayed ?? sample.previous;
|
|
3862
3913
|
const dx = sample.point.x - previousDisplay.x;
|
|
3863
3914
|
const dy = sample.point.y - previousDisplay.y;
|
|
3864
3915
|
const distance = Math.hypot(dx, dy);
|
|
3865
|
-
const ratio = distance > 0.01 ? POINTER_FOLLOW_TIME_SECONDS / (POINTER_FOLLOW_TIME_SECONDS +
|
|
3916
|
+
const ratio = distance > 0.01 ? POINTER_FOLLOW_TIME_SECONDS / (POINTER_FOLLOW_TIME_SECONDS + dt) : 0;
|
|
3866
3917
|
const point = { x: sample.point.x - dx * ratio, y: sample.point.y - dy * ratio };
|
|
3867
3918
|
apply(point, sample.previous, sample.speed, sample.invert, fresh);
|
|
3868
3919
|
fresh = false;
|
|
3869
3920
|
lastPoint = sample.point;
|
|
3870
3921
|
displayed = point;
|
|
3871
3922
|
if (ratio === 0) pending = void 0;
|
|
3923
|
+
},
|
|
3924
|
+
release() {
|
|
3925
|
+
const handle = runtime();
|
|
3926
|
+
if (!handle || !valid()) {
|
|
3927
|
+
clear();
|
|
3928
|
+
return;
|
|
3929
|
+
}
|
|
3930
|
+
const newestAt = samples[samples.length - 1]?.at ?? 0;
|
|
3931
|
+
const velocity = pointerReleaseVelocity(samples, Math.max(performance.now(), newestAt), (point) => {
|
|
3932
|
+
if (anchor) return anchor.solve(point, gestureSpeed, gestureInvert);
|
|
3933
|
+
const origin = samples[0] ?? point;
|
|
3934
|
+
const rect = element.getBoundingClientRect();
|
|
3935
|
+
const sign = gestureInvert ? -1 : 1;
|
|
3936
|
+
return {
|
|
3937
|
+
yaw: -(point.x - origin.x) * 360 * gestureSpeed / Math.max(rect.width, 1) * sign,
|
|
3938
|
+
pitch: (point.y - origin.y) * 180 * gestureSpeed / Math.max(rect.height, 1) * sign
|
|
3939
|
+
};
|
|
3940
|
+
});
|
|
3941
|
+
if (pending) this.flush(lastFrameDelta, true, velocity);
|
|
3942
|
+
else handle.applyViewDelta({}, { velocity, source });
|
|
3943
|
+
clear();
|
|
3872
3944
|
}
|
|
3873
3945
|
};
|
|
3874
3946
|
}
|
|
@@ -3884,6 +3956,9 @@ var BUTTON_FLAG = {
|
|
|
3884
3956
|
function resolveSpeed2(value, fallback) {
|
|
3885
3957
|
return Number.isFinite(value) && value >= 0 ? value : fallback;
|
|
3886
3958
|
}
|
|
3959
|
+
function pointerAt(event) {
|
|
3960
|
+
return event.timeStamp > 0 ? event.timeStamp : performance.now();
|
|
3961
|
+
}
|
|
3887
3962
|
function resolveButtons(buttons) {
|
|
3888
3963
|
const list = buttons && buttons.length > 0 ? buttons : ["left"];
|
|
3889
3964
|
return new Set(list.map((button) => BUTTON_FLAG[button]));
|
|
@@ -3970,7 +4045,7 @@ function MouseControls({
|
|
|
3970
4045
|
});
|
|
3971
4046
|
element.setPointerCapture?.(event.pointerId);
|
|
3972
4047
|
syncActive();
|
|
3973
|
-
drag.start({ x: event.clientX, y: event.clientY });
|
|
4048
|
+
drag.start({ x: event.clientX, y: event.clientY }, pointerAt(event));
|
|
3974
4049
|
};
|
|
3975
4050
|
const onPointerMove = (event) => {
|
|
3976
4051
|
if (!isMouseLike(event)) {
|
|
@@ -3991,15 +4066,15 @@ function MouseControls({
|
|
|
3991
4066
|
};
|
|
3992
4067
|
pointersRef.current.set(event.pointerId, next);
|
|
3993
4068
|
const speed = resolveSpeed2(optionsRef.current.rotateSpeed, DEFAULT_ROTATE_SPEED2);
|
|
3994
|
-
drag.move(next, previous, speed, optionsRef.current.invert);
|
|
4069
|
+
drag.move(next, previous, speed, optionsRef.current.invert, pointerAt(event));
|
|
3995
4070
|
};
|
|
3996
4071
|
const releasePointer = (event) => {
|
|
3997
4072
|
if (!pointersRef.current.has(event.pointerId)) {
|
|
3998
4073
|
return;
|
|
3999
4074
|
}
|
|
4000
|
-
if (event.type === "pointerup") drag.
|
|
4075
|
+
if (event.type === "pointerup") drag.release();
|
|
4076
|
+
else drag.clear();
|
|
4001
4077
|
pointersRef.current.delete(event.pointerId);
|
|
4002
|
-
drag.clear();
|
|
4003
4078
|
if (element.hasPointerCapture?.(event.pointerId)) {
|
|
4004
4079
|
element.releasePointerCapture?.(event.pointerId);
|
|
4005
4080
|
}
|
|
@@ -5642,6 +5717,9 @@ function notifyScenesHost(host) {
|
|
|
5642
5717
|
var import_fiber8 = require("@react-three/fiber");
|
|
5643
5718
|
var import_react18 = require("react");
|
|
5644
5719
|
var DEFAULT_ROTATE_SPEED3 = 0.35;
|
|
5720
|
+
function pointerAt2(event) {
|
|
5721
|
+
return event.timeStamp > 0 ? event.timeStamp : performance.now();
|
|
5722
|
+
}
|
|
5645
5723
|
function resolveSpeed3(value, fallback) {
|
|
5646
5724
|
return Number.isFinite(value) && value >= 0 ? value : fallback;
|
|
5647
5725
|
}
|
|
@@ -5710,7 +5788,7 @@ function TouchControls({
|
|
|
5710
5788
|
});
|
|
5711
5789
|
element.setPointerCapture?.(event.pointerId);
|
|
5712
5790
|
syncActive();
|
|
5713
|
-
drag.start({ x: event.clientX, y: event.clientY });
|
|
5791
|
+
drag.start({ x: event.clientX, y: event.clientY }, pointerAt2(event));
|
|
5714
5792
|
if (pointersRef.current.size >= 2) {
|
|
5715
5793
|
drag.suspend();
|
|
5716
5794
|
const [first, second] = Array.from(pointersRef.current.values());
|
|
@@ -5757,15 +5835,16 @@ function TouchControls({
|
|
|
5757
5835
|
return;
|
|
5758
5836
|
}
|
|
5759
5837
|
const speed = resolveSpeed3(optionsRef.current.rotateSpeed, DEFAULT_ROTATE_SPEED3);
|
|
5760
|
-
drag.move(next, previous, speed, optionsRef.current.invert);
|
|
5838
|
+
drag.move(next, previous, speed, optionsRef.current.invert, pointerAt2(event));
|
|
5761
5839
|
};
|
|
5762
5840
|
const releasePointer = (event) => {
|
|
5763
5841
|
if (!pointersRef.current.has(event.pointerId)) {
|
|
5764
5842
|
return;
|
|
5765
5843
|
}
|
|
5766
|
-
|
|
5844
|
+
const pinch = pointersRef.current.size >= 2;
|
|
5845
|
+
if (event.type === "pointerup" && !pinch) drag.release();
|
|
5846
|
+
else drag.clear();
|
|
5767
5847
|
pointersRef.current.delete(event.pointerId);
|
|
5768
|
-
drag.clear();
|
|
5769
5848
|
if (element.hasPointerCapture?.(event.pointerId)) {
|
|
5770
5849
|
element.releasePointerCapture?.(event.pointerId);
|
|
5771
5850
|
}
|
|
@@ -5774,7 +5853,7 @@ function TouchControls({
|
|
|
5774
5853
|
}
|
|
5775
5854
|
syncActive(event.type !== "pointerup");
|
|
5776
5855
|
const remaining = pointersRef.current.values().next().value;
|
|
5777
|
-
if (remaining) drag.start(remaining);
|
|
5856
|
+
if (remaining) drag.start(remaining, pointerAt2(event));
|
|
5778
5857
|
};
|
|
5779
5858
|
element.addEventListener("pointerdown", onPointerDown);
|
|
5780
5859
|
element.addEventListener("pointermove", onPointerMove);
|