From 71e2b51f5bcddee9fc6c2dd21f708fafccdf9c19 Mon Sep 17 00:00:00 2001 From: Siddharth Date: Fri, 28 Nov 2025 21:46:05 -0700 Subject: [PATCH] padding video control --- src/components/video-editor/SettingsPanel.tsx | 6 +-- src/components/video-editor/VideoEditor.tsx | 6 ++- src/components/video-editor/VideoPlayback.tsx | 5 ++- .../video-editor/videoPlayback/layoutUtils.ts | 9 +++-- src/lib/exporter/frameRenderer.ts | 37 ++++--------------- src/lib/exporter/videoExporter.ts | 2 + 6 files changed, 26 insertions(+), 39 deletions(-) diff --git a/src/components/video-editor/SettingsPanel.tsx b/src/components/video-editor/SettingsPanel.tsx index 7f0385a..6ceefdb 100644 --- a/src/components/video-editor/SettingsPanel.tsx +++ b/src/components/video-editor/SettingsPanel.tsx @@ -78,7 +78,7 @@ const ZOOM_DEPTH_OPTIONS: Array<{ depth: ZoomDepth; label: string }> = [ { depth: 6, label: "5×" }, ]; -export function SettingsPanel({ selected, onWallpaperChange, selectedZoomDepth, onZoomDepthChange, selectedZoomId, onZoomDelete, shadowIntensity = 0, onShadowChange, showBlur, onBlurChange, motionBlurEnabled = true, onMotionBlurChange, borderRadius = 0, onBorderRadiusChange, padding = 0, onPaddingChange, cropRegion, onCropChange, videoElement, onExport }: SettingsPanelProps) { +export function SettingsPanel({ selected, onWallpaperChange, selectedZoomDepth, onZoomDepthChange, selectedZoomId, onZoomDelete, shadowIntensity = 0, onShadowChange, showBlur, onBlurChange, motionBlurEnabled = true, onMotionBlurChange, borderRadius = 0, onBorderRadiusChange, padding = 50, onPaddingChange, cropRegion, onCropChange, videoElement, onExport }: SettingsPanelProps) { const [wallpaperPaths, setWallpaperPaths] = useState([]); const [customImages, setCustomImages] = useState([]); const fileInputRef = useRef(null); @@ -265,8 +265,8 @@ export function SettingsPanel({ selected, onWallpaperChange, selectedZoomDepth, {/* Padding Slider */}
-
Padding (TODO)
- {padding}px +
Padding
+ {padding}%
(DEFAULT_CROP_REGION); const [zoomRegions, setZoomRegions] = useState([]); const [selectedZoomId, setSelectedZoomId] = useState(null); @@ -306,6 +306,7 @@ export default function VideoEditor() { showBlur, motionBlurEnabled, borderRadius, + padding, cropRegion, onProgress: (progress: ExportProgress) => { setExportProgress(progress); @@ -347,7 +348,7 @@ export default function VideoEditor() { setIsExporting(false); exporterRef.current = null; } - }, [videoPath, wallpaper, zoomRegions, trimRegions, shadowIntensity, showBlur, motionBlurEnabled, borderRadius, cropRegion, isPlaying]); + }, [videoPath, wallpaper, zoomRegions, trimRegions, shadowIntensity, showBlur, motionBlurEnabled, borderRadius, padding, cropRegion, isPlaying]); const handleCancelExport = useCallback(() => { if (exporterRef.current) { @@ -413,6 +414,7 @@ export default function VideoEditor() { showBlur={showBlur} motionBlurEnabled={motionBlurEnabled} borderRadius={borderRadius} + padding={padding} cropRegion={cropRegion} trimRegions={trimRegions} /> diff --git a/src/components/video-editor/VideoPlayback.tsx b/src/components/video-editor/VideoPlayback.tsx index 44397e3..905e37f 100644 --- a/src/components/video-editor/VideoPlayback.tsx +++ b/src/components/video-editor/VideoPlayback.tsx @@ -29,6 +29,7 @@ interface VideoPlaybackProps { showBlur?: boolean; motionBlurEnabled?: boolean; borderRadius?: number; + padding?: number; cropRegion?: import('./types').CropRegion; trimRegions?: TrimRegion[]; } @@ -59,6 +60,7 @@ const VideoPlayback = forwardRef(({ showBlur, motionBlurEnabled = true, borderRadius = 0, + padding = 50, cropRegion, trimRegions = [], }, ref) => { @@ -153,6 +155,7 @@ const VideoPlayback = forwardRef(({ cropRegion, lockedVideoDimensions: lockedVideoDimensionsRef.current, borderRadius, + padding, }); if (result) { @@ -174,7 +177,7 @@ const VideoPlayback = forwardRef(({ updateOverlayForRegion(activeRegion); } - }, [updateOverlayForRegion, cropRegion, borderRadius]); + }, [updateOverlayForRegion, cropRegion, borderRadius, padding]); useEffect(() => { layoutVideoContentRef.current = layoutVideoContent; diff --git a/src/components/video-editor/videoPlayback/layoutUtils.ts b/src/components/video-editor/videoPlayback/layoutUtils.ts index 4def048..8ddda35 100644 --- a/src/components/video-editor/videoPlayback/layoutUtils.ts +++ b/src/components/video-editor/videoPlayback/layoutUtils.ts @@ -11,6 +11,7 @@ interface LayoutParams { cropRegion?: CropRegion; lockedVideoDimensions?: { width: number; height: number } | null; borderRadius?: number; + padding?: number; } interface LayoutResult { @@ -23,7 +24,7 @@ interface LayoutResult { } export function layoutVideoContent(params: LayoutParams): LayoutResult | null { - const { container, app, videoSprite, maskGraphics, videoElement, cropRegion, lockedVideoDimensions, borderRadius = 0 } = params; + const { container, app, videoSprite, maskGraphics, videoElement, cropRegion, lockedVideoDimensions, borderRadius = 0, padding = 0 } = params; const videoWidth = lockedVideoDimensions?.width || videoElement.videoWidth; const videoHeight = lockedVideoDimensions?.height || videoElement.videoHeight; @@ -56,8 +57,10 @@ export function layoutVideoContent(params: LayoutParams): LayoutResult | null { const cropEndY = cropStartY + croppedVideoHeight; // Calculate scale to fit the cropped area in the viewport - const maxDisplayWidth = width * VIEWPORT_SCALE; - const maxDisplayHeight = height * VIEWPORT_SCALE; + // Padding is a percentage (0-100), where 50 matches the original VIEWPORT_SCALE of 0.8 + const paddingScale = 1.0 - (padding / 100) * 0.4; + const maxDisplayWidth = width * paddingScale; + const maxDisplayHeight = height * paddingScale; const scale = Math.min( maxDisplayWidth / croppedVideoWidth, diff --git a/src/lib/exporter/frameRenderer.ts b/src/lib/exporter/frameRenderer.ts index c0b6b9a..104e6c8 100644 --- a/src/lib/exporter/frameRenderer.ts +++ b/src/lib/exporter/frameRenderer.ts @@ -16,6 +16,7 @@ interface FrameRenderConfig { showBlur: boolean; motionBlurEnabled?: boolean; borderRadius?: number; + padding?: number; cropRegion: CropRegion; videoWidth: number; videoHeight: number; @@ -302,20 +303,10 @@ export class FrameRenderer { if (!this.app || !this.videoSprite || !this.maskGraphics || !this.videoContainer) return; const { width, height } = this.config; - const { cropRegion, borderRadius = 0 } = this.config; + const { cropRegion, borderRadius = 0, padding = 0 } = this.config; const videoWidth = this.config.videoWidth; const videoHeight = this.config.videoHeight; - // Log layout calculation once (only on first layout) - if (!this.layoutCache) { - console.log('[FrameRenderer] Initial updateLayout', { - canvasSize: { width, height }, - videoSize: { width: videoWidth, height: videoHeight }, - cropRegion, - borderRadius, - }); - } - // Calculate cropped video dimensions const cropStartX = cropRegion.x; const cropStartY = cropRegion.y; @@ -325,9 +316,11 @@ export class FrameRenderer { const croppedVideoWidth = videoWidth * (cropEndX - cropStartX); const croppedVideoHeight = videoHeight * (cropEndY - cropStartY); - // Calculate scale to fit in viewport (using VIEWPORT_SCALE from constants) - const viewportWidth = width * 0.8; // VIEWPORT_SCALE = 0.8 - const viewportHeight = height * 0.8; + // Calculate scale to fit in viewport + // Padding is a percentage (0-100), where 50% ~ 0.8 scale + const paddingScale = 1.0 - (padding / 100) * 0.4; + const viewportWidth = width * paddingScale; + const viewportHeight = height * paddingScale; const scale = Math.min(viewportWidth / croppedVideoWidth, viewportHeight / croppedVideoHeight); // Position video sprite @@ -348,22 +341,6 @@ export class FrameRenderer { this.videoContainer.y = centerOffsetY; // Update mask - // Scale the border radius if needed? - // In preview, we use the raw pixel value from the slider. - // In export, the canvas might be much larger (e.g. 4K vs 800px preview). - // If we use the raw value (e.g. 20px), it will look tiny on 4K. - // We should probably scale it based on the resolution ratio relative to a "standard" preview size (e.g. 1920x1080 or similar). - // Or, we can assume the user sees it on a ~1000px wide preview. - // Let's scale it by (width / 1280) as a rough heuristic to match visual appearance? - // Actually, let's just use the raw value for now as requested "fine grain control". - // If the user sets 20px, they might expect 20px. - // BUT, if they are editing on a small screen and exporting to 4K, 20px will look different. - // Let's stick to raw value first as it's safer than guessing. - // Wait, the previous hardcoded value was percentage based: radius = min(w, h) * 0.02 - // If I use raw pixels, I break that "responsiveness". - // However, the slider is in pixels (0-40). - // I will use the raw value for now. - this.maskGraphics.clear(); this.maskGraphics.roundRect(0, 0, croppedDisplayWidth, croppedDisplayHeight, borderRadius); this.maskGraphics.fill({ color: 0xffffff }); diff --git a/src/lib/exporter/videoExporter.ts b/src/lib/exporter/videoExporter.ts index f994d4d..03362c6 100644 --- a/src/lib/exporter/videoExporter.ts +++ b/src/lib/exporter/videoExporter.ts @@ -14,6 +14,7 @@ interface VideoExporterConfig extends ExportConfig { showBlur: boolean; motionBlurEnabled?: boolean; borderRadius?: number; + padding?: number; videoPadding?: number; cropRegion: CropRegion; onProgress?: (progress: ExportProgress) => void; @@ -89,6 +90,7 @@ export class VideoExporter { showBlur: this.config.showBlur, motionBlurEnabled: this.config.motionBlurEnabled, borderRadius: this.config.borderRadius, + padding: this.config.padding, cropRegion: this.config.cropRegion, videoWidth: videoInfo.width, videoHeight: videoInfo.height,