fix: make earth satellite and cable toggles fully unload
This commit is contained in:
@@ -7,6 +7,20 @@ This project follows the repository versioning rule:
|
|||||||
- `feature` -> `+0.1.0`
|
- `feature` -> `+0.1.0`
|
||||||
- `bugfix` -> `+0.0.1`
|
- `bugfix` -> `+0.0.1`
|
||||||
|
|
||||||
|
## 0.21.8
|
||||||
|
|
||||||
|
Released: 2026-03-27
|
||||||
|
|
||||||
|
### Highlights
|
||||||
|
|
||||||
|
- Improved Earth-layer performance controls so satellite and cable toggles now fully unload their scene/runtime state instead of only hiding objects.
|
||||||
|
|
||||||
|
### Improved
|
||||||
|
|
||||||
|
- Improved the Earth satellite toggle to stop position/trail updates and release satellite rendering resources while disabled, then reload on demand when re-enabled.
|
||||||
|
- Improved the Earth cable toggle to unload submarine cable and landing-point objects while disabled so drag and interaction cost drops with the layer turned off.
|
||||||
|
- Improved Earth data reload behavior so disabled satellite and cable layers stay disabled instead of being implicitly reloaded during refresh.
|
||||||
|
|
||||||
## 0.21.7
|
## 0.21.7
|
||||||
|
|
||||||
Released: 2026-03-27
|
Released: 2026-03-27
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "planet-frontend",
|
"name": "planet-frontend",
|
||||||
"version": "0.21.7",
|
"version": "0.21.8",
|
||||||
"private": true,
|
"private": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@ant-design/icons": "^5.2.6",
|
"@ant-design/icons": "^5.2.6",
|
||||||
|
|||||||
43
frontend/public/earth/js/controls.js
vendored
43
frontend/public/earth/js/controls.js
vendored
@@ -3,14 +3,18 @@
|
|||||||
import { CONFIG, EARTH_CONFIG } from "./constants.js";
|
import { CONFIG, EARTH_CONFIG } from "./constants.js";
|
||||||
import { updateZoomDisplay, showStatusMessage } from "./ui.js";
|
import { updateZoomDisplay, showStatusMessage } from "./ui.js";
|
||||||
import { toggleTerrain } from "./earth.js";
|
import { toggleTerrain } from "./earth.js";
|
||||||
import { reloadData, clearLockedObject } from "./main.js";
|
|
||||||
import {
|
import {
|
||||||
toggleSatellites,
|
reloadData,
|
||||||
|
clearLockedObject,
|
||||||
|
setCablesEnabled,
|
||||||
|
setSatellitesEnabled,
|
||||||
|
} from "./main.js";
|
||||||
|
import {
|
||||||
toggleTrails,
|
toggleTrails,
|
||||||
getShowSatellites,
|
getShowSatellites,
|
||||||
getSatelliteCount,
|
getSatelliteCount,
|
||||||
} from "./satellites.js";
|
} from "./satellites.js";
|
||||||
import { toggleCables, getShowCables } from "./cables.js";
|
import { getShowCables } from "./cables.js";
|
||||||
import { toggleBGP, getShowBGP, getBGPCount } from "./bgp.js";
|
import { toggleBGP, getShowBGP, getBGPCount } from "./bgp.js";
|
||||||
|
|
||||||
export let autoRotate = true;
|
export let autoRotate = true;
|
||||||
@@ -324,19 +328,24 @@ function setupTerrainControls() {
|
|||||||
showStatusMessage(showTerrain ? "地形已显示" : "地形已隐藏", "info");
|
showStatusMessage(showTerrain ? "地形已显示" : "地形已隐藏", "info");
|
||||||
});
|
});
|
||||||
|
|
||||||
bindListener(satellitesBtn, "click", function () {
|
bindListener(satellitesBtn, "click", async function () {
|
||||||
const showSats = !getShowSatellites();
|
const showSats = !getShowSatellites();
|
||||||
if (!showSats) {
|
if (!showSats) {
|
||||||
clearLockedObject();
|
clearLockedObject();
|
||||||
}
|
}
|
||||||
toggleSatellites(showSats);
|
try {
|
||||||
this.classList.toggle("active", showSats);
|
await setSatellitesEnabled(showSats);
|
||||||
const tooltip = this.querySelector(".tooltip");
|
if (!showSats) {
|
||||||
if (tooltip) tooltip.textContent = showSats ? "隐藏卫星" : "显示卫星";
|
showStatusMessage("卫星已隐藏", "info");
|
||||||
|
} else {
|
||||||
const satelliteCountEl = document.getElementById("satellite-count");
|
const satelliteCountEl = document.getElementById("satellite-count");
|
||||||
if (satelliteCountEl)
|
if (satelliteCountEl) {
|
||||||
satelliteCountEl.textContent = getSatelliteCount() + " 颗";
|
satelliteCountEl.textContent = `${getSatelliteCount()} 颗`;
|
||||||
showStatusMessage(showSats ? "卫星已显示" : "卫星已隐藏", "info");
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error("切换卫星显示失败:", error);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
bindListener(bgpBtn, "click", function () {
|
bindListener(bgpBtn, "click", function () {
|
||||||
@@ -365,16 +374,16 @@ function setupTerrainControls() {
|
|||||||
showStatusMessage(nextShowTrails ? "轨迹已显示" : "轨迹已隐藏", "info");
|
showStatusMessage(nextShowTrails ? "轨迹已显示" : "轨迹已隐藏", "info");
|
||||||
});
|
});
|
||||||
|
|
||||||
bindListener(cablesBtn, "click", function () {
|
bindListener(cablesBtn, "click", async function () {
|
||||||
const showNextCables = !getShowCables();
|
const showNextCables = !getShowCables();
|
||||||
if (!showNextCables) {
|
if (!showNextCables) {
|
||||||
clearLockedObject();
|
clearLockedObject();
|
||||||
}
|
}
|
||||||
toggleCables(showNextCables);
|
try {
|
||||||
this.classList.toggle("active", showNextCables);
|
await setCablesEnabled(showNextCables);
|
||||||
const tooltip = this.querySelector(".tooltip");
|
} catch (error) {
|
||||||
if (tooltip) tooltip.textContent = showNextCables ? "隐藏线缆" : "显示线缆";
|
console.error("切换线缆显示失败:", error);
|
||||||
showStatusMessage(showNextCables ? "线缆已显示" : "线缆已隐藏", "info");
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
bindListener(reloadBtn, "click", async () => {
|
bindListener(reloadBtn, "click", async () => {
|
||||||
|
|||||||
@@ -38,6 +38,8 @@ import {
|
|||||||
resetLandingPointVisualState,
|
resetLandingPointVisualState,
|
||||||
getShowCables,
|
getShowCables,
|
||||||
clearCableData,
|
clearCableData,
|
||||||
|
getLandingPoints,
|
||||||
|
toggleCables,
|
||||||
} from "./cables.js";
|
} from "./cables.js";
|
||||||
import {
|
import {
|
||||||
createSatellites,
|
createSatellites,
|
||||||
@@ -126,6 +128,10 @@ let initialized = false;
|
|||||||
let destroyed = false;
|
let destroyed = false;
|
||||||
let isDataLoading = false;
|
let isDataLoading = false;
|
||||||
let currentLoadToken = 0;
|
let currentLoadToken = 0;
|
||||||
|
let cablesEnabled = true;
|
||||||
|
let satellitesEnabled = true;
|
||||||
|
let cableToggleToken = 0;
|
||||||
|
let satelliteToggleToken = 0;
|
||||||
|
|
||||||
const clock = new THREE.Clock();
|
const clock = new THREE.Clock();
|
||||||
const interactionRaycaster = new THREE.Raycaster();
|
const interactionRaycaster = new THREE.Raycaster();
|
||||||
@@ -344,6 +350,124 @@ function buildLoadErrorMessage(errors) {
|
|||||||
.join(";");
|
.join(";");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function updateSatelliteToggleUi(enabled, satelliteCount = getSatelliteCount()) {
|
||||||
|
const satBtn = document.getElementById("toggle-satellites");
|
||||||
|
if (satBtn) {
|
||||||
|
satBtn.classList.toggle("active", enabled);
|
||||||
|
const tooltip = satBtn.querySelector(".tooltip");
|
||||||
|
if (tooltip) tooltip.textContent = enabled ? "隐藏卫星" : "显示卫星";
|
||||||
|
}
|
||||||
|
|
||||||
|
const satelliteCountEl = document.getElementById("satellite-count");
|
||||||
|
if (satelliteCountEl) {
|
||||||
|
satelliteCountEl.textContent = `${enabled ? satelliteCount : 0} 颗`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateCableToggleUi(enabled) {
|
||||||
|
const cableBtn = document.getElementById("toggle-cables");
|
||||||
|
if (cableBtn) {
|
||||||
|
cableBtn.classList.toggle("active", enabled);
|
||||||
|
const tooltip = cableBtn.querySelector(".tooltip");
|
||||||
|
if (tooltip) tooltip.textContent = enabled ? "隐藏线缆" : "显示线缆";
|
||||||
|
}
|
||||||
|
|
||||||
|
const cableCountEl = document.getElementById("cable-count");
|
||||||
|
if (cableCountEl) {
|
||||||
|
cableCountEl.textContent = `${enabled ? getCableLines().length : 0}个`;
|
||||||
|
}
|
||||||
|
|
||||||
|
const landingPointCountEl = document.getElementById("landing-point-count");
|
||||||
|
if (landingPointCountEl) {
|
||||||
|
landingPointCountEl.textContent = `${enabled ? getLandingPoints().length : 0}个`;
|
||||||
|
}
|
||||||
|
|
||||||
|
const statusEl = document.getElementById("cable-status-summary");
|
||||||
|
if (statusEl && !enabled) {
|
||||||
|
statusEl.textContent = "0/0 运行中";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function ensureCablesEnabled() {
|
||||||
|
if (!scene || !camera || !renderer || destroyed) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
const earth = getEarth();
|
||||||
|
if (!earth) return 0;
|
||||||
|
|
||||||
|
cablesEnabled = true;
|
||||||
|
const requestToken = ++cableToggleToken;
|
||||||
|
|
||||||
|
clearCableData(earth);
|
||||||
|
const [cableCount] = await Promise.all([
|
||||||
|
loadGeoJSONFromPath(scene, earth),
|
||||||
|
loadLandingPoints(scene, earth),
|
||||||
|
]);
|
||||||
|
|
||||||
|
if (requestToken !== cableToggleToken || !cablesEnabled || destroyed) {
|
||||||
|
clearCableData(earth);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
toggleCables(true);
|
||||||
|
updateCableToggleUi(true);
|
||||||
|
setLegendItems("cables", getCableLegendItems());
|
||||||
|
refreshLegend();
|
||||||
|
return cableCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
function disableCables() {
|
||||||
|
cablesEnabled = false;
|
||||||
|
cableToggleToken += 1;
|
||||||
|
clearCableData(getEarth());
|
||||||
|
updateCableToggleUi(false);
|
||||||
|
setLegendItems("cables", getCableLegendItems());
|
||||||
|
refreshLegend();
|
||||||
|
}
|
||||||
|
|
||||||
|
async function ensureSatellitesEnabled() {
|
||||||
|
if (!scene || !camera || !renderer || destroyed) return 0;
|
||||||
|
|
||||||
|
const earth = getEarth();
|
||||||
|
if (!earth) return 0;
|
||||||
|
|
||||||
|
satellitesEnabled = true;
|
||||||
|
const requestToken = ++satelliteToggleToken;
|
||||||
|
|
||||||
|
if (!getSatellitePoints()) {
|
||||||
|
createSatellites(scene, earth);
|
||||||
|
}
|
||||||
|
|
||||||
|
clearSatelliteData();
|
||||||
|
const satelliteCount = await loadSatellites();
|
||||||
|
|
||||||
|
if (
|
||||||
|
requestToken !== satelliteToggleToken ||
|
||||||
|
!satellitesEnabled ||
|
||||||
|
destroyed
|
||||||
|
) {
|
||||||
|
resetSatelliteState();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
updateSatellitePositions(POSITION_UPDATE_FORCE_DELTA, true);
|
||||||
|
toggleSatellites(true);
|
||||||
|
updateSatelliteToggleUi(true, satelliteCount);
|
||||||
|
setLegendItems("satellites", getSatelliteLegendItems());
|
||||||
|
refreshLegend();
|
||||||
|
return satelliteCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
function disableSatellites() {
|
||||||
|
satellitesEnabled = false;
|
||||||
|
satelliteToggleToken += 1;
|
||||||
|
resetSatelliteState();
|
||||||
|
updateSatelliteToggleUi(false, 0);
|
||||||
|
setLegendItems("satellites", getSatelliteLegendItems());
|
||||||
|
refreshLegend();
|
||||||
|
}
|
||||||
|
|
||||||
function updateStatsSummary() {
|
function updateStatsSummary() {
|
||||||
updateEarthStats({
|
updateEarthStats({
|
||||||
cableCount: getCableLines().length,
|
cableCount: getCableLines().length,
|
||||||
@@ -479,25 +603,8 @@ async function loadData(showWhiteSphere = false) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const results = await Promise.allSettled([
|
const results = await Promise.allSettled([
|
||||||
loadGeoJSONFromPath(scene, earth),
|
cablesEnabled ? ensureCablesEnabled() : Promise.resolve(0),
|
||||||
loadLandingPoints(scene, earth),
|
satellitesEnabled ? ensureSatellitesEnabled() : Promise.resolve(0),
|
||||||
(async () => {
|
|
||||||
clearSatelliteData();
|
|
||||||
const satelliteCount = await loadSatellites();
|
|
||||||
const satelliteCountEl = document.getElementById("satellite-count");
|
|
||||||
if (satelliteCountEl) {
|
|
||||||
satelliteCountEl.textContent = `${satelliteCount} 颗`;
|
|
||||||
}
|
|
||||||
updateSatellitePositions(POSITION_UPDATE_FORCE_DELTA, true);
|
|
||||||
toggleSatellites(true);
|
|
||||||
const satBtn = document.getElementById("toggle-satellites");
|
|
||||||
if (satBtn) {
|
|
||||||
satBtn.classList.add("active");
|
|
||||||
const tooltip = satBtn.querySelector(".tooltip");
|
|
||||||
if (tooltip) tooltip.textContent = "隐藏卫星";
|
|
||||||
}
|
|
||||||
return satelliteCount;
|
|
||||||
})(),
|
|
||||||
(async () => {
|
(async () => {
|
||||||
clearBGPData(earth);
|
clearBGPData(earth);
|
||||||
const bgpResult = await loadBGPAnomalies(scene, earth);
|
const bgpResult = await loadBGPAnomalies(scene, earth);
|
||||||
@@ -526,13 +633,10 @@ async function loadData(showWhiteSphere = false) {
|
|||||||
errors.push({ label: "电缆", reason: results[0].reason });
|
errors.push({ label: "电缆", reason: results[0].reason });
|
||||||
}
|
}
|
||||||
if (results[1].status === "rejected") {
|
if (results[1].status === "rejected") {
|
||||||
errors.push({ label: "登陆点", reason: results[1].reason });
|
errors.push({ label: "卫星", reason: results[1].reason });
|
||||||
}
|
}
|
||||||
if (results[2].status === "rejected") {
|
if (results[2].status === "rejected") {
|
||||||
errors.push({ label: "卫星", reason: results[2].reason });
|
errors.push({ label: "BGP异常", reason: results[2].reason });
|
||||||
}
|
|
||||||
if (results[3].status === "rejected") {
|
|
||||||
errors.push({ label: "BGP异常", reason: results[3].reason });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (errors.length > 0) {
|
if (errors.length > 0) {
|
||||||
@@ -545,6 +649,8 @@ async function loadData(showWhiteSphere = false) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
updateStatsSummary();
|
updateStatsSummary();
|
||||||
|
updateCableToggleUi(cablesEnabled);
|
||||||
|
updateSatelliteToggleUi(satellitesEnabled);
|
||||||
setLegendItems("cables", getCableLegendItems());
|
setLegendItems("cables", getCableLegendItems());
|
||||||
setLegendItems("satellites", getSatelliteLegendItems());
|
setLegendItems("satellites", getSatelliteLegendItems());
|
||||||
setLegendItems("bgp", getBGPLegendItems());
|
setLegendItems("bgp", getBGPLegendItems());
|
||||||
@@ -565,6 +671,75 @@ export async function reloadData() {
|
|||||||
await loadData(true);
|
await loadData(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function setCablesEnabled(enabled) {
|
||||||
|
if (enabled === cablesEnabled) {
|
||||||
|
updateCableToggleUi(enabled);
|
||||||
|
return getCableLines().length;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!enabled) {
|
||||||
|
clearLockedObject();
|
||||||
|
hideInfoCard();
|
||||||
|
disableCables();
|
||||||
|
showStatusMessage("线缆已隐藏", "info");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
setLoadingMessage("正在加载线缆数据...", "重建海缆与登陆点对象");
|
||||||
|
setLoading(true);
|
||||||
|
hideError();
|
||||||
|
|
||||||
|
try {
|
||||||
|
const cableCount = await ensureCablesEnabled();
|
||||||
|
showStatusMessage("线缆已显示", "info");
|
||||||
|
return cableCount;
|
||||||
|
} catch (error) {
|
||||||
|
cablesEnabled = false;
|
||||||
|
clearCableData(getEarth());
|
||||||
|
updateCableToggleUi(false);
|
||||||
|
const message = `线缆加载失败: ${error?.message || String(error)}`;
|
||||||
|
showError(message);
|
||||||
|
showStatusMessage(message, "error");
|
||||||
|
throw error;
|
||||||
|
} finally {
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function setSatellitesEnabled(enabled) {
|
||||||
|
if (enabled === satellitesEnabled) {
|
||||||
|
updateSatelliteToggleUi(enabled);
|
||||||
|
return getSatelliteCount();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!enabled) {
|
||||||
|
clearLockedObject();
|
||||||
|
hideInfoCard();
|
||||||
|
disableSatellites();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
setLoadingMessage("正在加载卫星数据...", "重建卫星点位与轨迹缓存");
|
||||||
|
setLoading(true);
|
||||||
|
hideError();
|
||||||
|
|
||||||
|
try {
|
||||||
|
const satelliteCount = await ensureSatellitesEnabled();
|
||||||
|
showStatusMessage("卫星已显示", "info");
|
||||||
|
return satelliteCount;
|
||||||
|
} catch (error) {
|
||||||
|
satellitesEnabled = false;
|
||||||
|
resetSatelliteState();
|
||||||
|
updateSatelliteToggleUi(false, 0);
|
||||||
|
const message = `卫星加载失败: ${error?.message || String(error)}`;
|
||||||
|
showError(message);
|
||||||
|
showStatusMessage(message, "error");
|
||||||
|
throw error;
|
||||||
|
} finally {
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function setupEventListeners() {
|
function setupEventListeners() {
|
||||||
const handleResize = () => onWindowResize();
|
const handleResize = () => onWindowResize();
|
||||||
const handleMouseMove = (event) => onMouseMove(event);
|
const handleMouseMove = (event) => onMouseMove(event);
|
||||||
|
|||||||
Reference in New Issue
Block a user