diff options
| author | leimingsheng <leimingsheng@58.com> | 2025-07-21 19:15:52 +0800 |
|---|---|---|
| committer | leimingsheng <leimingsheng@58.com> | 2025-07-21 19:15:52 +0800 |
| commit | d6724e2552d14140017b194e7020252bda73751a (patch) | |
| tree | 0b0b400b7933e21ad911cb2366b1806ff2e35a64 /apps/web/src/hooks | |
| parent | 263f14ef2a83b0cc78189f764b0c1859fa392539 (diff) | |
| download | opencut-d6724e2552d14140017b194e7020252bda73751a.tar.gz | |
feat: init timeline zoom
Diffstat (limited to 'apps/web/src/hooks')
| -rw-r--r-- | apps/web/src/hooks/use-keyboard-shortcuts-help.ts | 4 | ||||
| -rw-r--r-- | apps/web/src/hooks/use-timeline-zoom-actions.ts | 37 | ||||
| -rw-r--r-- | apps/web/src/hooks/use-timeline-zoom.ts | 64 |
3 files changed, 103 insertions, 2 deletions
diff --git a/apps/web/src/hooks/use-keyboard-shortcuts-help.ts b/apps/web/src/hooks/use-keyboard-shortcuts-help.ts index c7de8466..d0f177f6 100644 --- a/apps/web/src/hooks/use-keyboard-shortcuts-help.ts +++ b/apps/web/src/hooks/use-keyboard-shortcuts-help.ts @@ -60,6 +60,10 @@ const actionDescriptions: Record< category: "Selection", }, "toggle-snapping": { description: "Toggle snapping", category: "Editing" }, + "zoom-in": { description: "Zoom in timeline", category: "View" }, + "zoom-out": { description: "Zoom out timeline", category: "View" }, + "zoom-reset": { description: "Reset timeline zoom", category: "View" }, + "zoom-fit": { description: "Fit timeline to window", category: "View" }, undo: { description: "Undo", category: "History" }, redo: { description: "Redo", category: "History" }, }; diff --git a/apps/web/src/hooks/use-timeline-zoom-actions.ts b/apps/web/src/hooks/use-timeline-zoom-actions.ts new file mode 100644 index 00000000..85003684 --- /dev/null +++ b/apps/web/src/hooks/use-timeline-zoom-actions.ts @@ -0,0 +1,37 @@ +"use client"; + +import { useActionHandler } from "@/constants/actions"; + +interface UseTimelineZoomActionsProps { + zoomLevel: number; + setZoomLevel: (zoomLevel: number | ((prev: number) => number)) => void; + onFitToWindow?: () => void; +} + +export function useTimelineZoomActions({ + zoomLevel, + setZoomLevel, + onFitToWindow, +}: UseTimelineZoomActionsProps) { + // Zoom in action + useActionHandler("zoom-in", () => { + setZoomLevel((prev) => Math.min(5, prev + 0.2)); + }); + + // Zoom out action + useActionHandler("zoom-out", () => { + setZoomLevel((prev) => Math.max(0.1, prev - 0.2)); + }); + + // Reset zoom action + useActionHandler("zoom-reset", () => { + setZoomLevel(1); + }); + + // Fit to window action + useActionHandler("zoom-fit", () => { + if (onFitToWindow) { + onFitToWindow(); + } + }); +} diff --git a/apps/web/src/hooks/use-timeline-zoom.ts b/apps/web/src/hooks/use-timeline-zoom.ts index ce32c9f0..8d41d917 100644 --- a/apps/web/src/hooks/use-timeline-zoom.ts +++ b/apps/web/src/hooks/use-timeline-zoom.ts @@ -3,6 +3,7 @@ import { useState, useCallback, useEffect, RefObject } from "react"; interface UseTimelineZoomProps {
containerRef: RefObject<HTMLDivElement>;
isInTimeline?: boolean;
+ onFitToWindow?: () => void;
}
interface UseTimelineZoomReturn {
@@ -11,22 +12,81 @@ interface UseTimelineZoomReturn { handleWheel: (e: React.WheelEvent) => void;
}
+const ZOOM_STORAGE_KEY = 'opencut-timeline-zoom';
+
export function useTimelineZoom({
containerRef,
isInTimeline = false,
+ onFitToWindow,
}: UseTimelineZoomProps): UseTimelineZoomReturn {
- const [zoomLevel, setZoomLevel] = useState(1);
+ // Load initial zoom level from localStorage
+ const [zoomLevel, setZoomLevel] = useState(() => {
+ if (typeof window !== 'undefined') {
+ const stored = localStorage.getItem(ZOOM_STORAGE_KEY);
+ if (stored) {
+ const parsed = parseFloat(stored);
+ if (!isNaN(parsed) && parsed >= 0.1 && parsed <= 5) {
+ return parsed;
+ }
+ }
+ }
+ return 1;
+ });
+
+ // Save zoom level to localStorage whenever it changes
+ useEffect(() => {
+ if (typeof window !== 'undefined') {
+ localStorage.setItem(ZOOM_STORAGE_KEY, zoomLevel.toString());
+ }
+ }, [zoomLevel]);
const handleWheel = useCallback((e: React.WheelEvent) => {
// Only zoom if user is using pinch gesture (ctrlKey or metaKey is true)
if (e.ctrlKey || e.metaKey) {
e.preventDefault();
const delta = e.deltaY > 0 ? -0.15 : 0.15;
- setZoomLevel((prev) => Math.max(0.1, Math.min(10, prev + delta)));
+ setZoomLevel((prev) => Math.max(0.1, Math.min(5, prev + delta)));
}
// Otherwise, allow normal scrolling
}, []);
+ // Keyboard shortcuts for zoom
+ useEffect(() => {
+ const handleKeyDown = (e: KeyboardEvent) => {
+ // Only handle zoom shortcuts when in timeline or when timeline is focused
+ if (!isInTimeline) return;
+
+ if ((e.ctrlKey || e.metaKey) && !e.shiftKey) {
+ switch (e.key) {
+ case '=':
+ case '+':
+ e.preventDefault();
+ setZoomLevel((prev) => Math.min(5, prev + 0.2));
+ break;
+ case '-':
+ e.preventDefault();
+ setZoomLevel((prev) => Math.max(0.1, prev - 0.2));
+ break;
+ case '0':
+ e.preventDefault();
+ setZoomLevel(1);
+ break;
+ case '9':
+ e.preventDefault();
+ if (onFitToWindow) {
+ onFitToWindow();
+ }
+ break;
+ }
+ }
+ };
+
+ if (isInTimeline) {
+ document.addEventListener('keydown', handleKeyDown);
+ return () => document.removeEventListener('keydown', handleKeyDown);
+ }
+ }, [isInTimeline, setZoomLevel]);
+
// Prevent browser zooming in/out when in timeline
useEffect(() => {
const preventZoom = (e: WheelEvent) => {
|
