Tune BIM zoom and modal layering

This commit is contained in:
CODEX 2026-06-23 20:38:52 +03:00
parent 249d46c7c7
commit 63b72d0e36
3 changed files with 72 additions and 9 deletions

View File

@ -214,7 +214,7 @@ header {
#navCube { #navCube {
position: absolute; position: absolute;
top: 64px; top: 64px;
right: 12px; right: 64px;
width: 100px; width: 100px;
height: 100px; height: 100px;
z-index: 5; z-index: 5;
@ -1664,7 +1664,8 @@ header {
#saveProjectBackdrop, #saveProjectBackdrop,
#deleteProjectBackdrop, #deleteProjectBackdrop,
#shareModalBackdrop { #shareModalBackdrop,
.version-history-backdrop {
z-index: var(--z-critical-backdrop); z-index: var(--z-critical-backdrop);
background: rgba(10, 12, 16, 0.38); background: rgba(10, 12, 16, 0.38);
backdrop-filter: blur(18px) saturate(1.18) brightness(0.82); backdrop-filter: blur(18px) saturate(1.18) brightness(0.82);
@ -2784,6 +2785,7 @@ header {
gap: 10px; gap: 10px;
align-items: center; align-items: center;
padding: 4px 6px 4px 6px; padding: 4px 6px 4px 6px;
touch-action: none;
} }
.settings-panel .env-range-control.slider-row { .settings-panel .env-range-control.slider-row {
@ -3245,17 +3247,14 @@ html[data-share-startup="pending"] #loader.hidden {
.version-history-backdrop { .version-history-backdrop {
position: fixed; position: fixed;
inset: 0; inset: 0;
z-index: var(--z-modal, 10000); z-index: var(--z-critical-backdrop);
background: rgba(0, 0, 0, 0.58);
backdrop-filter: blur(18px);
-webkit-backdrop-filter: blur(18px);
} }
.version-history-modal { .version-history-modal {
position: fixed; position: fixed;
left: 50%; left: 50%;
top: 50%; top: 50%;
z-index: calc(var(--z-modal, 10000) + 1); z-index: var(--z-critical-modal);
display: flex; display: flex;
width: min(96vw, 1280px); width: min(96vw, 1280px);
max-height: min(76vh, 720px); max-height: min(76vh, 720px);

View File

@ -381,10 +381,23 @@ const CAMERA_CONTROL_SETTINGS = {
dollyProximityThreshold: 90, dollyProximityThreshold: 90,
}; };
const ZOOM_SPEED_PERCENT_MIN = 5;
const ZOOM_SPEED_PERCENT_MAX = 100;
const ZOOM_DOLLY_RATE_MIN = 1;
const ZOOM_DOLLY_RATE_MAX = 100;
const ZOOM_DOLLY_RESPONSE_EXPONENT = 1.45;
const normalizeZoomSpeed = (value) => { const normalizeZoomSpeed = (value) => {
const numeric = Number(value); const numeric = Number(value);
if (!Number.isFinite(numeric)) return 20; if (!Number.isFinite(numeric)) return 20;
return Math.min(100, Math.max(5, Math.round(numeric))); return Math.min(ZOOM_SPEED_PERCENT_MAX, Math.max(ZOOM_SPEED_PERCENT_MIN, Math.round(numeric)));
};
const getZoomDollyRate = (value) => {
const normalized = normalizeZoomSpeed(value);
const range = ZOOM_SPEED_PERCENT_MAX - ZOOM_SPEED_PERCENT_MIN;
const t = range > 0 ? (normalized - ZOOM_SPEED_PERCENT_MIN) / range : 0;
return ZOOM_DOLLY_RATE_MIN + (Math.pow(t, ZOOM_DOLLY_RESPONSE_EXPONENT) * (ZOOM_DOLLY_RATE_MAX - ZOOM_DOLLY_RATE_MIN));
}; };
const isHexColor = (value) => /^#([0-9a-f]{3}|[0-9a-f]{6})$/i.test(String(value || "").trim()); const isHexColor = (value) => /^#([0-9a-f]{3}|[0-9a-f]{6})$/i.test(String(value || "").trim());
@ -453,7 +466,7 @@ const configureCameraControl = () => {
control.smartPivot = false; control.smartPivot = false;
control.zoomOnMouseWheel = true; control.zoomOnMouseWheel = true;
control.keyboardDollyRate = CAMERA_CONTROL_SETTINGS.keyboardDollyRate; control.keyboardDollyRate = CAMERA_CONTROL_SETTINGS.keyboardDollyRate;
control.mouseWheelDollyRate = activeZoomSpeed; control.mouseWheelDollyRate = getZoomDollyRate(activeZoomSpeed);
control.dollyInertia = CAMERA_CONTROL_SETTINGS.dollyInertia; control.dollyInertia = CAMERA_CONTROL_SETTINGS.dollyInertia;
control.dollyMinSpeed = CAMERA_CONTROL_SETTINGS.dollyMinSpeed; control.dollyMinSpeed = CAMERA_CONTROL_SETTINGS.dollyMinSpeed;
control.dollyProximityThreshold = CAMERA_CONTROL_SETTINGS.dollyProximityThreshold; control.dollyProximityThreshold = CAMERA_CONTROL_SETTINGS.dollyProximityThreshold;

View File

@ -60,6 +60,57 @@ export function createSliderControl({
onChange?.(v); onChange?.(v);
}); });
const clampValue = (next) => Math.min(Number(input.max), Math.max(Number(input.min), next));
const valueFromClientX = (clientX) => {
const rect = input.getBoundingClientRect();
const minValue = Number(input.min);
const maxValue = Number(input.max);
const range = maxValue - minValue;
if (!rect.width || !range) return Number(input.value);
const ratio = Math.max(0, Math.min(1, (clientX - rect.left) / rect.width));
const raw = minValue + (ratio * range);
if (input.step === "any") return clampValue(raw);
const stepValue = Number(input.step) || 1;
return clampValue(minValue + (Math.round((raw - minValue) / stepValue) * stepValue));
};
const commitValue = (nextValue) => {
input.value = nextValue;
updateFill();
onChange?.(Number(input.value));
};
let activeTouchPointerId = null;
const isTouchLikePointer = (event) => event.pointerType === "touch" || event.pointerType === "pen";
row.addEventListener("pointerdown", (event) => {
if (!isTouchLikePointer(event) || (event.button !== undefined && event.button !== 0)) return;
activeTouchPointerId = event.pointerId;
event.preventDefault();
row.setPointerCapture?.(event.pointerId);
input.focus?.({ preventScroll: true });
commitValue(valueFromClientX(event.clientX));
});
row.addEventListener("pointermove", (event) => {
if (activeTouchPointerId !== event.pointerId) return;
event.preventDefault();
commitValue(valueFromClientX(event.clientX));
});
const finishTouchPointer = (event) => {
if (activeTouchPointerId !== event.pointerId) return;
activeTouchPointerId = null;
if (row.hasPointerCapture?.(event.pointerId)) {
row.releasePointerCapture?.(event.pointerId);
}
};
row.addEventListener("pointerup", finishTouchPointer);
row.addEventListener("pointercancel", finishTouchPointer);
updateFill(); updateFill();
row.appendChild(lbl); row.appendChild(lbl);