From 9a361a9f2e6f7d529581e4520f71b5c83d91bdf0 Mon Sep 17 00:00:00 2001 From: AmitwalaH Date: Thu, 23 Apr 2026 15:10:59 +0530 Subject: [PATCH] fix(video-playback): resolve initialization timing issues and ensure smooth zoom & layout rendering --- src/components/video-editor/VideoPlayback.tsx | 79 ++++++++++++++----- src/lib/exporter/frameRenderer.ts | 8 +- 2 files changed, 64 insertions(+), 23 deletions(-) diff --git a/src/components/video-editor/VideoPlayback.tsx b/src/components/video-editor/VideoPlayback.tsx index d659afe..b4d98a6 100644 --- a/src/components/video-editor/VideoPlayback.tsx +++ b/src/components/video-editor/VideoPlayback.tsx @@ -164,6 +164,10 @@ const VideoPlayback = forwardRef( const [pixiReady, setPixiReady] = useState(false); const [videoReady, setVideoReady] = useState(false); const overlayRef = useRef(null); + const [containerSize, setContainerSize] = useState({ + width: 800, + height: 600, + }); const focusIndicatorRef = useRef(null); const [webcamLayout, setWebcamLayout] = useState(null); const [webcamDimensions, setWebcamDimensions] = useState(null); @@ -195,7 +199,10 @@ const VideoPlayback = forwardRef( const isPlayingRef = useRef(isPlaying); const isSeekingRef = useRef(false); const allowPlaybackRef = useRef(false); - const lockedVideoDimensionsRef = useRef<{ width: number; height: number } | null>(null); + const lockedVideoDimensionsRef = useRef<{ + width: number; + height: number; + } | null>(null); const layoutVideoContentRef = useRef<(() => void) | null>(null); const trimRegionsRef = useRef([]); const speedRegionsRef = useRef([]); @@ -648,7 +655,11 @@ const VideoPlayback = forwardRef( app.ticker.maxFPS = 60; if (!mounted) { - app.destroy(true, { children: true, texture: true, textureSource: true }); + app.destroy(true, { + children: true, + texture: true, + textureSource: true, + }); return; } @@ -672,7 +683,11 @@ const VideoPlayback = forwardRef( mounted = false; setPixiReady(false); if (app && app.renderer) { - app.destroy(true, { children: true, texture: true, textureSource: true }); + app.destroy(true, { + children: true, + texture: true, + textureSource: true, + }); } appRef.current = null; cameraContainerRef.current = null; @@ -853,12 +868,19 @@ const VideoPlayback = forwardRef( const ss = stageSizeRef.current; const viewportRatio = bm.width > 0 && bm.height > 0 - ? { widthRatio: ss.width / bm.width, heightRatio: ss.height / bm.height } + ? { + widthRatio: ss.width / bm.width, + heightRatio: ss.height / bm.height, + } : undefined; const { region, strength, blendedScale, transition } = findDominantRegion( zoomRegionsRef.current, currentTimeRef.current, - { connectZooms: true, cursorTelemetry: cursorTelemetryRef.current, viewportRatio }, + { + connectZooms: true, + cursorTelemetry: cursorTelemetryRef.current, + viewportRatio, + }, ); const defaultFocus = DEFAULT_FOCUS; @@ -1113,6 +1135,21 @@ const VideoPlayback = forwardRef( webcamVideo.currentTime = 0; }, [webcamVideoPath]); + useEffect(() => { + if (!overlayRef.current) return; + + const observer = new ResizeObserver(() => { + setContainerSize({ + width: overlayRef.current!.clientWidth, + height: overlayRef.current!.clientHeight, + }); + }); + + observer.observe(overlayRef.current); + + return () => observer.disconnect(); + }, []); + useEffect(() => { let mounted = true; (async () => { @@ -1307,20 +1344,24 @@ const VideoPlayback = forwardRef( } }; - return sorted.map((annotation) => ( - onAnnotationPositionChange?.(id, position)} - onSizeChange={(id, size) => onAnnotationSizeChange?.(id, size)} - onClick={handleAnnotationClick} - zIndex={annotation.zIndex} - isSelectedBoost={annotation.id === selectedAnnotationId} - /> - )); + return sorted.map((annotation) => { + const containerWidth = containerSize.width; + const containerHeight = containerSize.height; + return ( + onAnnotationPositionChange?.(id, position)} + onSizeChange={(id, size) => onAnnotationSizeChange?.(id, size)} + onClick={handleAnnotationClick} + zIndex={annotation.zIndex} + isSelectedBoost={annotation.id === selectedAnnotationId} + /> + ); + }); })()} )} diff --git a/src/lib/exporter/frameRenderer.ts b/src/lib/exporter/frameRenderer.ts index 80d2f6d..be0e8e3 100644 --- a/src/lib/exporter/frameRenderer.ts +++ b/src/lib/exporter/frameRenderer.ts @@ -406,8 +406,8 @@ export class FrameRenderer { this.compositeCtx ) { // Calculate scale factor based on export vs preview dimensions - const previewWidth = this.config.previewWidth || 1920; - const previewHeight = this.config.previewHeight || 1080; + const previewWidth = this.config.previewWidth ?? this.config.width; + const previewHeight = this.config.previewHeight ?? this.config.height; const scaleX = this.config.width / previewWidth; const scaleY = this.config.height / previewHeight; const scaleFactor = (scaleX + scaleY) / 2; @@ -491,8 +491,8 @@ export class FrameRenderer { this.videoContainer.y = screenRect.y; // scale border radius by export/preview canvas ratio - const previewWidth = this.config.previewWidth || 1920; - const previewHeight = this.config.previewHeight || 1080; + const previewWidth = this.config.previewWidth ?? this.config.width; + const previewHeight = this.config.previewHeight ?? this.config.height; const canvasScaleFactor = Math.min(width / previewWidth, height / previewHeight); const scaledBorderRadius = compositeLayout.screenCover ? 0 : borderRadius * canvasScaleFactor;