fix: tighten earth hud interactions
This commit is contained in:
@@ -635,6 +635,14 @@ input[type="range"]::-webkit-slider-thumb {
|
||||
transform: translate(-50%, 0);
|
||||
}
|
||||
|
||||
#zoom-control-group.force-closed #zoom-toolbar,
|
||||
#info-control-group.force-closed #info-toolbar {
|
||||
opacity: 0;
|
||||
visibility: hidden;
|
||||
pointer-events: none;
|
||||
transform: translate(-50%, 8px);
|
||||
}
|
||||
|
||||
#zoom-control-group #zoom-toolbar .zoom-percent {
|
||||
min-width: 0;
|
||||
width: 42px;
|
||||
|
||||
@@ -184,6 +184,7 @@
|
||||
0 10px 24px rgba(0, 0, 0, 0.16);
|
||||
padding: 0;
|
||||
overflow: hidden;
|
||||
pointer-events: auto;
|
||||
}
|
||||
|
||||
.info-card.no-border {
|
||||
@@ -216,6 +217,7 @@
|
||||
overflow-y: auto;
|
||||
scrollbar-width: thin;
|
||||
scrollbar-color: rgba(160, 220, 255, 0.45) transparent;
|
||||
pointer-events: auto;
|
||||
}
|
||||
|
||||
#info-card-content::-webkit-scrollbar {
|
||||
@@ -240,6 +242,7 @@
|
||||
justify-content: space-between;
|
||||
padding: 6px;
|
||||
border-bottom: 1px solid rgba(255, 255, 255, 0.05);
|
||||
pointer-events: auto;
|
||||
}
|
||||
|
||||
.info-card-property:last-child {
|
||||
|
||||
157
frontend/public/earth/js/controls.js
vendored
157
frontend/public/earth/js/controls.js
vendored
@@ -6,6 +6,7 @@ import { toggleTerrain } from "./earth.js";
|
||||
import {
|
||||
reloadData,
|
||||
clearLockedObject,
|
||||
clearLockedObjectAndInfo,
|
||||
setCablesEnabled,
|
||||
setSatellitesEnabled,
|
||||
} from "./main.js";
|
||||
@@ -20,13 +21,85 @@ import { toggleBGP, getShowBGP, getBGPCount } from "./bgp.js";
|
||||
export let autoRotate = true;
|
||||
export let zoomLevel = 1.0;
|
||||
export let showTerrain = false;
|
||||
export let isDragging = false;
|
||||
export let layoutExpanded = false;
|
||||
|
||||
let earthObj = null;
|
||||
let listeners = [];
|
||||
let cleanupFns = [];
|
||||
|
||||
function getFloatingGroups() {
|
||||
return [
|
||||
document.getElementById("zoom-control-group"),
|
||||
document.getElementById("info-control-group"),
|
||||
].filter(Boolean);
|
||||
}
|
||||
|
||||
function isFloatingMenuVisible() {
|
||||
return getFloatingGroups().some((group) => {
|
||||
return (
|
||||
group.classList.contains("open") ||
|
||||
group.matches(":hover") ||
|
||||
group.matches(":focus-within")
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
function closeFloatingMenus() {
|
||||
getFloatingGroups().forEach((group) => {
|
||||
group.classList.remove("open");
|
||||
group.classList.add("force-closed");
|
||||
});
|
||||
|
||||
if (document.activeElement instanceof HTMLElement) {
|
||||
document.activeElement.blur();
|
||||
}
|
||||
}
|
||||
|
||||
function clearForcedFloatingClose() {
|
||||
getFloatingGroups().forEach((group) => {
|
||||
group.classList.remove("force-closed");
|
||||
});
|
||||
}
|
||||
|
||||
function clearForcedFloatingCloseIfPointerOutside() {
|
||||
getFloatingGroups().forEach((group) => {
|
||||
if (!group.matches(":hover")) {
|
||||
group.classList.remove("force-closed");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function setFloatingMenuOpen(group, shouldOpen) {
|
||||
closeFloatingMenus();
|
||||
clearForcedFloatingClose();
|
||||
group?.classList.toggle("open", shouldOpen);
|
||||
}
|
||||
|
||||
function setButtonTooltip(button, text) {
|
||||
const tooltip = button?.querySelector(".tooltip");
|
||||
if (tooltip) {
|
||||
tooltip.textContent = text;
|
||||
}
|
||||
}
|
||||
|
||||
function clearSelectionIfHiding(shouldHide) {
|
||||
if (shouldHide) {
|
||||
clearLockedObject();
|
||||
}
|
||||
}
|
||||
|
||||
function bindFloatingMenu(trigger, group) {
|
||||
bindListener(trigger, "click", (event) => {
|
||||
event.stopPropagation();
|
||||
const shouldOpen = !group?.classList.contains("open");
|
||||
setFloatingMenuOpen(group, shouldOpen);
|
||||
});
|
||||
|
||||
bindListener(group, "click", (event) => {
|
||||
event.stopPropagation();
|
||||
});
|
||||
}
|
||||
|
||||
function bindListener(element, eventName, handler, options) {
|
||||
if (!element) return;
|
||||
element.addEventListener(eventName, handler, options);
|
||||
@@ -50,6 +123,7 @@ export function setupControls(camera, renderer, scene, earth) {
|
||||
setupRotateControls(camera, earth);
|
||||
setupTerrainControls();
|
||||
setupLiquidGlassInteractions();
|
||||
setupKeyboardControls();
|
||||
}
|
||||
|
||||
function setupZoomControls(camera) {
|
||||
@@ -308,8 +382,7 @@ function setupTerrainControls() {
|
||||
|
||||
if (trailsBtn) {
|
||||
trailsBtn.classList.add("active");
|
||||
const tooltip = trailsBtn.querySelector(".tooltip");
|
||||
if (tooltip) tooltip.textContent = "隐藏轨迹";
|
||||
setButtonTooltip(trailsBtn, "隐藏轨迹");
|
||||
}
|
||||
|
||||
bindListener(searchBtn, "click", () => {
|
||||
@@ -320,8 +393,7 @@ function setupTerrainControls() {
|
||||
showTerrain = !showTerrain;
|
||||
toggleTerrain(showTerrain);
|
||||
this.classList.toggle("active", showTerrain);
|
||||
const tooltip = this.querySelector(".tooltip");
|
||||
if (tooltip) tooltip.textContent = showTerrain ? "隐藏地形" : "显示地形";
|
||||
setButtonTooltip(this, showTerrain ? "隐藏地形" : "显示地形");
|
||||
const terrainStatus = document.getElementById("terrain-status");
|
||||
if (terrainStatus)
|
||||
terrainStatus.textContent = showTerrain ? "开启" : "关闭";
|
||||
@@ -330,9 +402,7 @@ function setupTerrainControls() {
|
||||
|
||||
bindListener(satellitesBtn, "click", async function () {
|
||||
const showSats = !getShowSatellites();
|
||||
if (!showSats) {
|
||||
clearLockedObject();
|
||||
}
|
||||
clearSelectionIfHiding(!showSats);
|
||||
try {
|
||||
await setSatellitesEnabled(showSats);
|
||||
if (!showSats) {
|
||||
@@ -350,13 +420,10 @@ function setupTerrainControls() {
|
||||
|
||||
bindListener(bgpBtn, "click", function () {
|
||||
const showNextBGP = !getShowBGP();
|
||||
if (!showNextBGP) {
|
||||
clearLockedObject();
|
||||
}
|
||||
clearSelectionIfHiding(!showNextBGP);
|
||||
toggleBGP(showNextBGP);
|
||||
this.classList.toggle("active", showNextBGP);
|
||||
const tooltip = this.querySelector(".tooltip");
|
||||
if (tooltip) tooltip.textContent = showNextBGP ? "隐藏BGP观测" : "显示BGP观测";
|
||||
setButtonTooltip(this, showNextBGP ? "隐藏BGP观测" : "显示BGP观测");
|
||||
const bgpCountEl = document.getElementById("bgp-anomaly-count");
|
||||
if (bgpCountEl) {
|
||||
bgpCountEl.textContent = `${getBGPCount()} 条`;
|
||||
@@ -369,16 +436,13 @@ function setupTerrainControls() {
|
||||
const nextShowTrails = !isActive;
|
||||
toggleTrails(nextShowTrails);
|
||||
this.classList.toggle("active", nextShowTrails);
|
||||
const tooltip = this.querySelector(".tooltip");
|
||||
if (tooltip) tooltip.textContent = nextShowTrails ? "隐藏轨迹" : "显示轨迹";
|
||||
setButtonTooltip(this, nextShowTrails ? "隐藏轨迹" : "显示轨迹");
|
||||
showStatusMessage(nextShowTrails ? "轨迹已显示" : "轨迹已隐藏", "info");
|
||||
});
|
||||
|
||||
bindListener(cablesBtn, "click", async function () {
|
||||
const showNextCables = !getShowCables();
|
||||
if (!showNextCables) {
|
||||
clearLockedObject();
|
||||
}
|
||||
clearSelectionIfHiding(!showNextCables);
|
||||
try {
|
||||
await setCablesEnabled(showNextCables);
|
||||
} catch (error) {
|
||||
@@ -390,39 +454,27 @@ function setupTerrainControls() {
|
||||
await reloadData();
|
||||
});
|
||||
|
||||
bindListener(zoomTrigger, "click", (event) => {
|
||||
event.stopPropagation();
|
||||
infoGroup?.classList.remove("open");
|
||||
zoomGroup?.classList.toggle("open");
|
||||
});
|
||||
|
||||
bindListener(zoomGroup, "click", (event) => {
|
||||
event.stopPropagation();
|
||||
});
|
||||
|
||||
bindListener(infoTrigger, "click", (event) => {
|
||||
event.stopPropagation();
|
||||
zoomGroup?.classList.remove("open");
|
||||
infoGroup?.classList.toggle("open");
|
||||
});
|
||||
|
||||
bindListener(infoGroup, "click", (event) => {
|
||||
event.stopPropagation();
|
||||
});
|
||||
bindFloatingMenu(zoomTrigger, zoomGroup);
|
||||
bindFloatingMenu(infoTrigger, infoGroup);
|
||||
|
||||
bindListener(document, "click", (event) => {
|
||||
if (zoomGroup?.classList.contains("open")) {
|
||||
if (!zoomGroup.contains(event.target)) {
|
||||
zoomGroup.classList.remove("open");
|
||||
}
|
||||
}
|
||||
if (infoGroup?.classList.contains("open")) {
|
||||
if (!infoGroup.contains(event.target)) {
|
||||
infoGroup.classList.remove("open");
|
||||
}
|
||||
const openGroups = [zoomGroup, infoGroup].filter((group) =>
|
||||
group?.classList.contains("open"),
|
||||
);
|
||||
if (openGroups.length === 0) return;
|
||||
|
||||
const clickedInsideOpenGroup = openGroups.some((group) =>
|
||||
group.contains(event.target),
|
||||
);
|
||||
if (!clickedInsideOpenGroup) {
|
||||
closeFloatingMenus();
|
||||
}
|
||||
});
|
||||
|
||||
bindListener(document, "mousemove", () => {
|
||||
clearForcedFloatingCloseIfPointerOutside();
|
||||
});
|
||||
|
||||
bindListener(layoutBtn, "click", () => {
|
||||
const expanded = toggleLayoutExpanded(container);
|
||||
showStatusMessage(expanded ? "布局已最大化" : "布局已恢复", "info");
|
||||
@@ -431,6 +483,19 @@ function setupTerrainControls() {
|
||||
updateLayoutUI(container);
|
||||
}
|
||||
|
||||
function setupKeyboardControls() {
|
||||
bindListener(document, "keydown", (event) => {
|
||||
if (event.key !== "Escape") return;
|
||||
|
||||
if (isFloatingMenuVisible()) {
|
||||
closeFloatingMenus();
|
||||
return;
|
||||
}
|
||||
|
||||
clearLockedObjectAndInfo();
|
||||
});
|
||||
}
|
||||
|
||||
function setupLiquidGlassInteractions() {
|
||||
const surfaces = document.querySelectorAll(".liquid-glass-surface");
|
||||
|
||||
|
||||
Reference in New Issue
Block a user