fix: polish earth toolbar controls and loading copy
This commit is contained in:
@@ -47,6 +47,27 @@ Released: 2026-03-26
|
|||||||
- Older satellite records can still fall back to backend-generated TLE lines when raw lines are unavailable.
|
- Older satellite records can still fall back to backend-generated TLE lines when raw lines are unavailable.
|
||||||
- This release is primarily focused on Earth module stability rather than visible admin UI changes.
|
- This release is primarily focused on Earth module stability rather than visible admin UI changes.
|
||||||
|
|
||||||
|
## 0.21.1
|
||||||
|
|
||||||
|
Released: 2026-03-26
|
||||||
|
|
||||||
|
### Highlights
|
||||||
|
|
||||||
|
- Refined the Earth big-screen toolbar with clearer controls, hover hints, and more consistent visual language.
|
||||||
|
- Replaced emoji-based Earth toolbar controls with SVG icons for a cleaner HUD.
|
||||||
|
- Updated the Earth loading splash so manual reloads no longer show legacy wording.
|
||||||
|
|
||||||
|
### Improved
|
||||||
|
|
||||||
|
- Improved zoom controls by adding tooltips for reset view, zoom in, zoom out, and resetting zoom to `100%`.
|
||||||
|
- Improved Earth toolbar readability with larger icons and revised glyphs for rotation, reload, satellites, trails, cables, terrain, and collapse.
|
||||||
|
- Improved loading overlay copy to better distinguish initial initialization from manual refresh.
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- Fixed rotate toggle rendering so play/pause state no longer relies on emoji text replacement.
|
||||||
|
- Fixed Earth autorotation target syncing so inertial drag is preserved while the globe is still coasting.
|
||||||
|
|
||||||
## 0.21.0
|
## 0.21.0
|
||||||
|
|
||||||
Released: 2026-03-26
|
Released: 2026-03-26
|
||||||
|
|||||||
@@ -16,7 +16,7 @@
|
|||||||
## Current Version
|
## Current Version
|
||||||
|
|
||||||
- `main` 当前主线历史推导到:`0.16.5`
|
- `main` 当前主线历史推导到:`0.16.5`
|
||||||
- `dev` 当前开发分支历史推导到:`0.21.0`
|
- `dev` 当前开发分支历史推导到:`0.21.1`
|
||||||
|
|
||||||
## Timeline
|
## Timeline
|
||||||
|
|
||||||
@@ -63,6 +63,7 @@
|
|||||||
| `0.19.0` | feature | `dev` | `020c1d50` | refine data management and collection workflows |
|
| `0.19.0` | feature | `dev` | `020c1d50` | refine data management and collection workflows |
|
||||||
| `0.20.0` | feature | `dev` | `ce5feba3` | stabilize Earth module and fix satellite TLE handling |
|
| `0.20.0` | feature | `dev` | `ce5feba3` | stabilize Earth module and fix satellite TLE handling |
|
||||||
| `0.21.0` | feature | `dev` | `pending` | add Earth inertial drag, sync hover/trail state, and support unlimited satellite loading |
|
| `0.21.0` | feature | `dev` | `pending` | add Earth inertial drag, sync hover/trail state, and support unlimited satellite loading |
|
||||||
|
| `0.21.1` | bugfix | `dev` | `pending` | polish Earth toolbar controls, icons, and loading copy |
|
||||||
|
|
||||||
## Maintenance Commits Not Counted as Version Bumps
|
## Maintenance Commits Not Counted as Version Bumps
|
||||||
|
|
||||||
|
|||||||
@@ -65,6 +65,7 @@ body {
|
|||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
color: #4db8ff;
|
color: #4db8ff;
|
||||||
min-width: 30px;
|
min-width: 30px;
|
||||||
|
display: inline-block;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
padding: 2px 4px;
|
padding: 2px 4px;
|
||||||
@@ -96,6 +97,7 @@ body {
|
|||||||
margin: 0;
|
margin: 0;
|
||||||
flex: 0 0 auto;
|
flex: 0 0 auto;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
#zoom-toolbar .zoom-btn:hover {
|
#zoom-toolbar .zoom-btn:hover {
|
||||||
@@ -104,6 +106,55 @@ body {
|
|||||||
box-shadow: 0 0 10px rgba(77, 184, 255, 0.5);
|
box-shadow: 0 0 10px rgba(77, 184, 255, 0.5);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#zoom-toolbar #reset-view svg {
|
||||||
|
width: 18px;
|
||||||
|
height: 18px;
|
||||||
|
stroke: currentColor;
|
||||||
|
stroke-width: 1.8;
|
||||||
|
fill: none;
|
||||||
|
stroke-linecap: round;
|
||||||
|
stroke-linejoin: round;
|
||||||
|
}
|
||||||
|
|
||||||
|
#zoom-toolbar .zoom-percent {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
#zoom-toolbar .tooltip {
|
||||||
|
position: absolute;
|
||||||
|
right: calc(100% + 10px);
|
||||||
|
top: 50%;
|
||||||
|
transform: translateY(-50%);
|
||||||
|
background: rgba(10, 10, 30, 0.95);
|
||||||
|
color: #fff;
|
||||||
|
padding: 6px 12px;
|
||||||
|
border-radius: 6px;
|
||||||
|
font-size: 12px;
|
||||||
|
white-space: nowrap;
|
||||||
|
opacity: 0;
|
||||||
|
visibility: hidden;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
border: 1px solid rgba(77, 184, 255, 0.4);
|
||||||
|
pointer-events: none;
|
||||||
|
z-index: 100;
|
||||||
|
}
|
||||||
|
|
||||||
|
#zoom-toolbar .zoom-btn:hover .tooltip,
|
||||||
|
#zoom-toolbar .zoom-percent:hover .tooltip {
|
||||||
|
opacity: 1;
|
||||||
|
visibility: visible;
|
||||||
|
}
|
||||||
|
|
||||||
|
#zoom-toolbar .tooltip::after {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
left: 100%;
|
||||||
|
top: 50%;
|
||||||
|
transform: translateY(-50%);
|
||||||
|
border: 6px solid transparent;
|
||||||
|
border-left-color: rgba(77, 184, 255, 0.4);
|
||||||
|
}
|
||||||
|
|
||||||
#loading {
|
#loading {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 50%;
|
top: 50%;
|
||||||
@@ -264,11 +315,23 @@ input[type="range"]::-webkit-slider-thumb {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.toggle-arrow {
|
.toggle-arrow {
|
||||||
font-size: 14px;
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
color: #4db8ff;
|
color: #4db8ff;
|
||||||
transition: transform 0.3s ease;
|
transition: transform 0.3s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.toggle-arrow svg {
|
||||||
|
width: 16px;
|
||||||
|
height: 16px;
|
||||||
|
stroke: currentColor;
|
||||||
|
stroke-width: 2.2;
|
||||||
|
fill: none;
|
||||||
|
stroke-linecap: round;
|
||||||
|
stroke-linejoin: round;
|
||||||
|
}
|
||||||
|
|
||||||
#control-toolbar.collapsed .toggle-arrow {
|
#control-toolbar.collapsed .toggle-arrow {
|
||||||
transform: rotate(0deg);
|
transform: rotate(0deg);
|
||||||
}
|
}
|
||||||
@@ -328,6 +391,31 @@ input[type="range"]::-webkit-slider-thumb {
|
|||||||
box-shadow: 0 0 10px rgba(77, 184, 255, 0.4) inset;
|
box-shadow: 0 0 10px rgba(77, 184, 255, 0.4) inset;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.toolbar-btn .icon {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toolbar-btn svg {
|
||||||
|
width: 18px;
|
||||||
|
height: 18px;
|
||||||
|
stroke: currentColor;
|
||||||
|
stroke-width: 2.1;
|
||||||
|
fill: none;
|
||||||
|
stroke-linecap: round;
|
||||||
|
stroke-linejoin: round;
|
||||||
|
}
|
||||||
|
|
||||||
|
#rotate-toggle .icon-play,
|
||||||
|
#rotate-toggle.is-stopped .icon-pause {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
#rotate-toggle.is-stopped .icon-play {
|
||||||
|
display: inline-flex;
|
||||||
|
}
|
||||||
|
|
||||||
.toolbar-btn .tooltip {
|
.toolbar-btn .tooltip {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
bottom: 50px;
|
bottom: 50px;
|
||||||
|
|||||||
@@ -38,22 +38,104 @@
|
|||||||
|
|
||||||
<div id="right-toolbar-group">
|
<div id="right-toolbar-group">
|
||||||
<div id="zoom-toolbar">
|
<div id="zoom-toolbar">
|
||||||
<button id="reset-view" class="zoom-btn">🎯</button>
|
<button id="reset-view" class="zoom-btn" title="重置视角">
|
||||||
<button id="zoom-in" class="zoom-btn">+</button>
|
<svg viewBox="0 0 24 24" aria-hidden="true">
|
||||||
<span id="zoom-value" class="zoom-percent">100%</span>
|
<circle cx="12" cy="12" r="5"></circle>
|
||||||
<button id="zoom-out" class="zoom-btn">−</button>
|
<path d="M12 3v4"></path>
|
||||||
|
<path d="M12 17v4"></path>
|
||||||
|
<path d="M3 12h4"></path>
|
||||||
|
<path d="M17 12h4"></path>
|
||||||
|
<circle cx="12" cy="12" r="1.5" fill="currentColor" stroke="none"></circle>
|
||||||
|
</svg>
|
||||||
|
<span class="tooltip">重置视角</span>
|
||||||
|
</button>
|
||||||
|
<button id="zoom-in" class="zoom-btn" title="放大">+<span class="tooltip">放大</span></button>
|
||||||
|
<span id="zoom-value" class="zoom-percent" title="重置缩放到100%">100%<span class="tooltip">重置缩放到100%</span></span>
|
||||||
|
<button id="zoom-out" class="zoom-btn" title="缩小">−<span class="tooltip">缩小</span></button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="control-toolbar">
|
<div id="control-toolbar">
|
||||||
<div class="toolbar-items">
|
<div class="toolbar-items">
|
||||||
<button id="rotate-toggle" class="toolbar-btn" title="自动旋转">🔄<span class="tooltip">自动旋转</span></button>
|
<button id="rotate-toggle" class="toolbar-btn" title="自动旋转">
|
||||||
<button id="toggle-cables" class="toolbar-btn active" title="显示/隐藏线缆">🌐<span class="tooltip">隐藏线缆</span></button>
|
<span class="icon rotate-icon icon-pause" aria-hidden="true">
|
||||||
<button id="toggle-terrain" class="toolbar-btn" title="显示/隐藏地形">⛰️<span class="tooltip">显示/隐藏地形</span></button>
|
<svg viewBox="0 0 24 24">
|
||||||
<button id="toggle-satellites" class="toolbar-btn" title="显示/隐藏卫星">🛰️<span class="tooltip">显示卫星</span></button>
|
<path d="M9 6v12"></path>
|
||||||
<button id="toggle-trails" class="toolbar-btn active" title="显示/隐藏轨迹">✨<span class="tooltip">隐藏轨迹</span></button>
|
<path d="M15 6v12"></path>
|
||||||
<button id="reload-data" class="toolbar-btn" title="重新加载数据">🔃<span class="tooltip">重新加载数据</span></button>
|
</svg>
|
||||||
|
</span>
|
||||||
|
<span class="icon rotate-icon icon-play" aria-hidden="true">
|
||||||
|
<svg viewBox="0 0 24 24">
|
||||||
|
<path d="M8 6.5v11l9-5.5z" fill="currentColor" stroke="none"></path>
|
||||||
|
</svg>
|
||||||
|
</span>
|
||||||
|
<span class="tooltip">自动旋转</span>
|
||||||
|
</button>
|
||||||
|
<button id="toggle-cables" class="toolbar-btn active" title="显示/隐藏线缆">
|
||||||
|
<span class="icon" aria-hidden="true">
|
||||||
|
<svg viewBox="0 0 24 24">
|
||||||
|
<circle cx="12" cy="12" r="6.5"></circle>
|
||||||
|
<path d="M5.8 12h12.4"></path>
|
||||||
|
<path d="M12 5.8a8.5 8.5 0 0 1 0 12.4"></path>
|
||||||
|
<path d="M8 16c2-1.8 6-1.8 8 0"></path>
|
||||||
|
</svg>
|
||||||
|
</span>
|
||||||
|
<span class="tooltip">隐藏线缆</span>
|
||||||
|
</button>
|
||||||
|
<button id="toggle-terrain" class="toolbar-btn" title="显示/隐藏地形">
|
||||||
|
<span class="icon" aria-hidden="true">
|
||||||
|
<svg viewBox="0 0 24 24">
|
||||||
|
<path d="M3 18h18"></path>
|
||||||
|
<path d="M4.5 18l5-7 3 4 3.5-6 3.5 9"></path>
|
||||||
|
<path d="M11 18l2-3 1.5 2"></path>
|
||||||
|
</svg>
|
||||||
|
</span>
|
||||||
|
<span class="tooltip">显示/隐藏地形</span>
|
||||||
|
</button>
|
||||||
|
<button id="toggle-satellites" class="toolbar-btn" title="显示/隐藏卫星">
|
||||||
|
<span class="icon" aria-hidden="true">
|
||||||
|
<svg viewBox="0 0 24 24">
|
||||||
|
<rect x="10" y="10" width="4" height="4" rx="0.8"></rect>
|
||||||
|
<rect x="4" y="9" width="4" height="6" rx="0.8"></rect>
|
||||||
|
<rect x="16" y="9" width="4" height="6" rx="0.8"></rect>
|
||||||
|
<path d="M8 12h2"></path>
|
||||||
|
<path d="M14 12h2"></path>
|
||||||
|
<path d="M12 8V6"></path>
|
||||||
|
<path d="M11 6h2"></path>
|
||||||
|
<path d="M12 14v4"></path>
|
||||||
|
<path d="M10 18h4"></path>
|
||||||
|
</svg>
|
||||||
|
</span>
|
||||||
|
<span class="tooltip">显示卫星</span>
|
||||||
|
</button>
|
||||||
|
<button id="toggle-trails" class="toolbar-btn active" title="显示/隐藏轨迹">
|
||||||
|
<span class="icon" aria-hidden="true">
|
||||||
|
<svg viewBox="0 0 24 24">
|
||||||
|
<path d="M5 17h7"></path>
|
||||||
|
<path d="M7 13.5h8"></path>
|
||||||
|
<path d="M10 10h6"></path>
|
||||||
|
<circle cx="17.5" cy="8.5" r="2.2" fill="currentColor" stroke="none"></circle>
|
||||||
|
<path d="M15.8 10.2l2.8-2.8"></path>
|
||||||
|
</svg>
|
||||||
|
</span>
|
||||||
|
<span class="tooltip">隐藏轨迹</span>
|
||||||
|
</button>
|
||||||
|
<button id="reload-data" class="toolbar-btn" title="重新加载数据">
|
||||||
|
<span class="icon" aria-hidden="true">
|
||||||
|
<svg viewBox="0 0 24 24">
|
||||||
|
<path d="M20 5v5h-5"></path>
|
||||||
|
<path d="M20 10a8 8 0 1 0 2 5"></path>
|
||||||
|
</svg>
|
||||||
|
</span>
|
||||||
|
<span class="tooltip">重新加载数据</span>
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<button id="toolbar-toggle" class="toolbar-btn" title="展开/收起工具栏"><span class="toggle-arrow">◀</span></button>
|
<button id="toolbar-toggle" class="toolbar-btn" title="展开/收起工具栏">
|
||||||
|
<span class="toggle-arrow" aria-hidden="true">
|
||||||
|
<svg viewBox="0 0 24 24">
|
||||||
|
<path d="M15 6l-6 6 6 6"></path>
|
||||||
|
</svg>
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -125,8 +207,8 @@
|
|||||||
|
|
||||||
<div id="loading">
|
<div id="loading">
|
||||||
<div id="loading-spinner"></div>
|
<div id="loading-spinner"></div>
|
||||||
<div>正在加载3D地球和电缆数据...</div>
|
<div id="loading-title">正在初始化全球态势数据...</div>
|
||||||
<div style="font-size:0.9rem; margin-top:10px; color:#aaa;">使用8K高分辨率卫星纹理 | 大陆轮廓更清晰</div>
|
<div id="loading-subtitle" style="font-size:0.9rem; margin-top:10px; color:#aaa;">同步卫星、海底光缆与登陆点数据</div>
|
||||||
</div>
|
</div>
|
||||||
<div id="status-message" class="status-message" style="display: none;"></div>
|
<div id="status-message" class="status-message" style="display: none;"></div>
|
||||||
<div id="tooltip" class="tooltip"></div>
|
<div id="tooltip" class="tooltip"></div>
|
||||||
|
|||||||
2
frontend/public/earth/js/controls.js
vendored
2
frontend/public/earth/js/controls.js
vendored
@@ -371,7 +371,7 @@ function updateRotateUI() {
|
|||||||
const btn = document.getElementById("rotate-toggle");
|
const btn = document.getElementById("rotate-toggle");
|
||||||
if (btn) {
|
if (btn) {
|
||||||
btn.classList.toggle("active", autoRotate);
|
btn.classList.toggle("active", autoRotate);
|
||||||
btn.innerHTML = autoRotate ? "⏸️" : "▶️";
|
btn.classList.toggle("is-stopped", !autoRotate);
|
||||||
const tooltip = btn.querySelector(".tooltip");
|
const tooltip = btn.querySelector(".tooltip");
|
||||||
if (tooltip) tooltip.textContent = autoRotate ? "暂停旋转" : "开始旋转";
|
if (tooltip) tooltip.textContent = autoRotate ? "暂停旋转" : "开始旋转";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import {
|
|||||||
updateZoomDisplay,
|
updateZoomDisplay,
|
||||||
updateEarthStats,
|
updateEarthStats,
|
||||||
setLoading,
|
setLoading,
|
||||||
|
setLoadingMessage,
|
||||||
showTooltip,
|
showTooltip,
|
||||||
hideTooltip,
|
hideTooltip,
|
||||||
showError,
|
showError,
|
||||||
@@ -392,6 +393,12 @@ async function loadData(showWhiteSphere = false) {
|
|||||||
const loadToken = ++currentLoadToken;
|
const loadToken = ++currentLoadToken;
|
||||||
isDataLoading = true;
|
isDataLoading = true;
|
||||||
hideError();
|
hideError();
|
||||||
|
setLoadingMessage(
|
||||||
|
showWhiteSphere ? "正在刷新全球态势数据..." : "正在初始化全球态势数据...",
|
||||||
|
showWhiteSphere
|
||||||
|
? "重新同步卫星、海底光缆与登陆点数据"
|
||||||
|
: "同步卫星、海底光缆与登陆点数据",
|
||||||
|
);
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
clearLockedObject();
|
clearLockedObject();
|
||||||
hideInfoCard();
|
hideInfoCard();
|
||||||
@@ -761,12 +768,20 @@ function animate() {
|
|||||||
|
|
||||||
const earth = getEarth();
|
const earth = getEarth();
|
||||||
const deltaTime = clock.getDelta() * 1000;
|
const deltaTime = clock.getDelta() * 1000;
|
||||||
|
const hasInertia =
|
||||||
|
Math.abs(inertialVelocity.x) > INERTIA_MIN_VELOCITY ||
|
||||||
|
Math.abs(inertialVelocity.y) > INERTIA_MIN_VELOCITY;
|
||||||
|
|
||||||
if (getAutoRotate() && earth) {
|
if (getAutoRotate() && earth) {
|
||||||
earth.rotation.y += CONFIG.rotationSpeed * (deltaTime / 16);
|
earth.rotation.y += CONFIG.rotationSpeed * (deltaTime / 16);
|
||||||
|
|
||||||
|
// Keep the drag target aligned with autorotation only when the user is not
|
||||||
|
// actively dragging and there is no residual inertial motion to preserve.
|
||||||
|
if (!isDragging && !hasInertia) {
|
||||||
targetRotation.y = earth.rotation.y;
|
targetRotation.y = earth.rotation.y;
|
||||||
targetRotation.x = earth.rotation.x;
|
targetRotation.x = earth.rotation.x;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (earth) {
|
if (earth) {
|
||||||
if (isDragging) {
|
if (isDragging) {
|
||||||
|
|||||||
@@ -73,6 +73,19 @@ export function setLoading(loading) {
|
|||||||
loadingEl.style.display = loading ? "block" : "none";
|
loadingEl.style.display = loading ? "block" : "none";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function setLoadingMessage(title, subtitle = "") {
|
||||||
|
const titleEl = document.getElementById("loading-title");
|
||||||
|
const subtitleEl = document.getElementById("loading-subtitle");
|
||||||
|
|
||||||
|
if (titleEl) {
|
||||||
|
titleEl.textContent = title;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (subtitleEl) {
|
||||||
|
subtitleEl.textContent = subtitle;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Show tooltip
|
// Show tooltip
|
||||||
export function showTooltip(x, y, content) {
|
export function showTooltip(x, y, content) {
|
||||||
const tooltip = document.getElementById("tooltip");
|
const tooltip = document.getElementById("tooltip");
|
||||||
|
|||||||
Reference in New Issue
Block a user