Files
brittle/src-tauri/dist/zoom-controller-enhanced.js
2026-04-01 01:27:51 +02:00

168 lines
6.1 KiB
JavaScript

/**
* zoom-controller-enhanced.ts — Advanced zoom controller with smooth transitions
*
* Provides smooth zoom animations, adaptive quality during zoom,
* and intelligent zoom level management.
*/
class EnhancedZoomController {
constructor(container, pageManager, onScaleChange, getVisibilityState, zoomLabel) {
this.container = container;
this.pageManager = pageManager;
this.onScaleChange = onScaleChange;
this.getVisibilityState = getVisibilityState;
this.zoomLabel = zoomLabel;
// Zoom state
this.scale = 1.0;
this.targetScale = 1.0;
this.minScale = 0.1;
this.maxScale = 5.0;
this.zoomAnimation = null;
this.isZooming = false;
this.zoomQuality = 1.0;
// Setup event listeners
this.setupEventListeners();
// Update zoom label
this.updateZoomLabel();
}
setupEventListeners() {
// Smooth zoom with animation
this.container.addEventListener('wheel', (event) => {
if (event.ctrlKey || event.metaKey) {
this.handleZoomWheel(event);
event.preventDefault();
}
}, { passive: false });
// Double-click for zoom to fit
this.container.addEventListener('dblclick', (event) => {
this.zoomToFit();
});
}
handleZoomWheel(event) {
// Calculate zoom factor based on wheel delta
const zoomFactor = event.deltaY > 0 ? 0.9 : 1.1;
const newScale = this.scale * zoomFactor;
// Apply constraints
const constrainedScale = Math.max(this.minScale, Math.min(this.maxScale, newScale));
if (Math.abs(constrainedScale - this.scale) > 0.01) {
this.applyScale(constrainedScale);
}
}
applyScale(newScale, options = {}) {
const { animate = true, qualityOverride = null } = options;
// Constrain scale
this.targetScale = Math.max(this.minScale, Math.min(this.maxScale, newScale));
if (Math.abs(this.targetScale - this.scale) < 0.01)
return;
if (animate) {
this.startZoomAnimation(qualityOverride);
}
else {
this.scale = this.targetScale;
const { visibleSet, bufferSet } = this.getVisibilityState();
this.onScaleChange(this.scale, visibleSet, bufferSet);
this.updateZoomLabel();
}
}
startZoomAnimation(qualityOverride = null) {
if (this.zoomAnimation) {
cancelAnimationFrame(this.zoomAnimation);
}
this.isZooming = true;
const startScale = this.scale;
const endScale = this.targetScale;
const startTime = performance.now();
const duration = 150; // 150ms for smooth zoom
// Set lower quality during zoom for performance
this.zoomQuality = qualityOverride !== null ? qualityOverride : 0.8;
const animate = (currentTime) => {
const elapsed = currentTime - startTime;
const progress = Math.min(elapsed / duration, 1);
// Ease-in-out animation for natural feel
const easedProgress = this.easeInOutCubic(progress);
this.scale = startScale + (endScale - startScale) * easedProgress;
// Apply the scale
this.applyScaleToContainer();
// Notify about scale change with current quality
const { visibleSet, bufferSet } = this.getVisibilityState();
this.onScaleChange(this.scale, visibleSet, bufferSet);
if (progress < 1) {
this.zoomAnimation = requestAnimationFrame(animate);
}
else {
this.isZooming = false;
this.scale = endScale;
this.zoomQuality = 1.0; // Restore full quality
// Final high-quality render
const { visibleSet, bufferSet } = this.getVisibilityState();
this.onScaleChange(this.scale, visibleSet, bufferSet);
this.updateZoomLabel();
this.zoomAnimation = null;
}
};
this.zoomAnimation = requestAnimationFrame(animate);
}
applyScaleToContainer() {
// Apply CSS transform for smooth zooming
this.container.style.transform = `scale(${this.scale})`;
// Also update the page manager
if (this.pageManager.setScale) {
this.pageManager.setScale(this.scale);
}
}
easeInOutCubic(t) {
return t < 0.5 ? 4 * t * t * t : 1 - Math.pow(-2 * t + 2, 3) / 2;
}
zoomToFit() {
if (!this.pageManager || !this.pageManager.viewports)
return;
const firstViewport = this.pageManager.viewports[0];
if (!firstViewport)
return;
const containerWidth = this.container.clientWidth;
const containerHeight = this.container.clientHeight;
// Calculate fit scale
const scaleW = (containerWidth - 40) / firstViewport.width;
const scaleH = (containerHeight - 40) / firstViewport.height;
const fitScale = Math.min(scaleW, scaleH);
this.applyScale(Math.max(this.minScale, Math.min(this.maxScale, fitScale)));
}
zoomIn() {
this.applyScale(this.scale * 1.25);
}
zoomOut() {
this.applyScale(this.scale * 0.8);
}
zoomToActualSize() {
this.applyScale(1.0);
}
getCurrentScale() {
return this.scale;
}
getCurrentZoomQuality() {
return this.zoomQuality;
}
isZoomingInProgress() {
return this.isZooming;
}
updateZoomLabel() {
if (this.zoomLabel) {
this.zoomLabel.textContent = `${Math.round(this.scale * 100)}%`;
}
}
cleanup() {
if (this.zoomAnimation) {
cancelAnimationFrame(this.zoomAnimation);
}
}
getZoomStats() {
return {
currentScale: this.scale,
targetScale: this.targetScale,
isZooming: this.isZooming,
zoomQuality: this.zoomQuality
};
}
}
// Export for ES6 modules
export { EnhancedZoomController };
//# sourceMappingURL=zoom-controller-enhanced.js.map