fix: tighten earth hud interactions

This commit is contained in:
linkong
2026-03-31 14:07:56 +08:00
parent 552e49bde0
commit c565ac0637
7 changed files with 145 additions and 49 deletions

View File

@@ -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;

View File

@@ -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 {

View File

@@ -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");