fix: make earth satellite and cable toggles fully unload

This commit is contained in:
linkong
2026-03-27 17:11:07 +08:00
parent 7a3ca6e1b3
commit 755729ee5e
5 changed files with 242 additions and 44 deletions

View File

@@ -1 +1 @@
0.21.7 0.21.8

View File

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

View File

@@ -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",

View File

@@ -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");
const satelliteCountEl = document.getElementById("satellite-count"); } else {
if (satelliteCountEl) const satelliteCountEl = document.getElementById("satellite-count");
satelliteCountEl.textContent = getSatelliteCount() + " 颗"; if (satelliteCountEl) {
showStatusMessage(showSats ? "卫星已显示" : "卫星已隐藏", "info"); satelliteCountEl.textContent = `${getSatelliteCount()}`;
}
}
} 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 () => {

View File

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