feat: implement smooth zoom animation and add zoom level indicator

This commit is contained in:
2025-12-29 15:51:29 +08:00
parent e985c0ee25
commit c0cc08a163
3 changed files with 44 additions and 10 deletions

View File

@@ -6,17 +6,17 @@ import Navbar from "./Navbar.astro";
import Particles from "./Particles.astro";
---
<Navbar />
<Navbar/>
<div class="particles-container" transition:persist>
<Particles />
<Particles/>
</div>
<div class="halo" transition:persist></div>
<div class="grain" transition:persist></div>
<div id="marble-field" transition:persist></div>
<Footer />
<Footer/>
<div class="content">
<CentralIsland titleSvg={titleSvg} />
<CentralIsland titleSvg={titleSvg}/>
</div>
<script>
@@ -242,20 +242,29 @@ import Particles from "./Particles.astro";
// Re-attach Zoom Controls
const zoomInBtn = document.getElementById("zoom-in");
const zoomOutBtn = document.getElementById("zoom-out");
const zoomLabel = document.getElementById("zoom-level");
const sys = window.marbleSystemInstance;
let currentZoom = 1.0;
const updateZoomLabel = () => {
if (zoomLabel) {
zoomLabel.textContent = `${currentZoom.toFixed(1)}x`;
}
};
if (sys) {
if (zoomInBtn) {
zoomInBtn.addEventListener("click", () => {
currentZoom = Math.min(currentZoom + 0.1, 2.0);
sys.updateMarbleSize(currentZoom);
sys.setZoom(currentZoom);
updateZoomLabel();
});
}
if (zoomOutBtn) {
zoomOutBtn.addEventListener("click", () => {
currentZoom = Math.max(currentZoom - 0.1, 0.5);
sys.updateMarbleSize(currentZoom);
sys.setZoom(currentZoom);
updateZoomLabel();
});
}
}
@@ -368,10 +377,7 @@ import Particles from "./Particles.astro";
:global(.marble-wrapper) {
position: absolute;
will-change: transform, width, height;
transition:
opacity 1s ease,
width 0.3s ease,
height 0.3s ease;
transition: opacity 1s ease;
}
:global(.marble-wrapper:hover) {

View File

@@ -39,6 +39,7 @@ const t = getTranslations(lang);
<line x1="5" y1="12" x2="19" y2="12"></line>
</svg>
</button>
<span id="zoom-level">1.0x</span>
</div>
<div class="separator"></div>
@@ -325,6 +326,16 @@ const t = getTranslations(lang);
display: flex;
flex-direction: row;
gap: 0.8rem;
align-items: center; /* Center label vertically */
}
#zoom-level {
color: rgba(255, 255, 255, 0.9);
font-size: 0.9rem;
font-weight: 500;
min-width: 3.5ch;
text-align: center;
font-variant-numeric: tabular-nums; /* Fixed width numbers to prevent jitter */
}
/* Button Styles (Shared) */

View File

@@ -139,9 +139,26 @@ export class MarbleSystem {
}
private currentSubSteps: number = 1;
private currentZoomLevel: number = 1.0;
private targetZoomLevel: number = 1.0;
// Set target zoom level
public setZoom(level: number): void {
// Clamp constraints
this.targetZoomLevel = Math.max(0.2, Math.min(level, 3.0));
}
// Per-frame update logic
private update(dt: number): void {
// 1. Handle Smooth Zoom
if (Math.abs(this.targetZoomLevel - this.currentZoomLevel) > 0.001) {
// Lerp factor (adjust for speed)
const t = 1.0 - 0.1 ** dt; // Framerate independent lerp
this.currentZoomLevel +=
(this.targetZoomLevel - this.currentZoomLevel) * t;
this.updateMarbleSize(this.currentZoomLevel);
}
let subSteps = 1;
if (