padding video control

This commit is contained in:
Siddharth
2025-11-28 21:46:05 -07:00
parent c9321240d8
commit 71e2b51f5b
6 changed files with 26 additions and 39 deletions
@@ -78,7 +78,7 @@ const ZOOM_DEPTH_OPTIONS: Array<{ depth: ZoomDepth; label: string }> = [
{ depth: 6, label: "5×" }, { 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<string[]>([]); const [wallpaperPaths, setWallpaperPaths] = useState<string[]>([]);
const [customImages, setCustomImages] = useState<string[]>([]); const [customImages, setCustomImages] = useState<string[]>([]);
const fileInputRef = useRef<HTMLInputElement>(null); const fileInputRef = useRef<HTMLInputElement>(null);
@@ -265,8 +265,8 @@ export function SettingsPanel({ selected, onWallpaperChange, selectedZoomDepth,
{/* Padding Slider */} {/* Padding Slider */}
<div className="p-3 rounded-xl bg-white/5 border border-white/5 space-y-2"> <div className="p-3 rounded-xl bg-white/5 border border-white/5 space-y-2">
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
<div className="text-xs font-medium text-slate-200">Padding (TODO)</div> <div className="text-xs font-medium text-slate-200">Padding</div>
<span className="text-[10px] text-slate-400 font-mono">{padding}px</span> <span className="text-[10px] text-slate-400 font-mono">{padding}%</span>
</div> </div>
<Slider <Slider
value={[padding]} value={[padding]}
+4 -2
View File
@@ -39,7 +39,7 @@ export default function VideoEditor() {
const [showBlur, setShowBlur] = useState(false); const [showBlur, setShowBlur] = useState(false);
const [motionBlurEnabled, setMotionBlurEnabled] = useState(true); const [motionBlurEnabled, setMotionBlurEnabled] = useState(true);
const [borderRadius, setBorderRadius] = useState(0); const [borderRadius, setBorderRadius] = useState(0);
const [padding, setPadding] = useState(0); const [padding, setPadding] = useState(50);
const [cropRegion, setCropRegion] = useState<CropRegion>(DEFAULT_CROP_REGION); const [cropRegion, setCropRegion] = useState<CropRegion>(DEFAULT_CROP_REGION);
const [zoomRegions, setZoomRegions] = useState<ZoomRegion[]>([]); const [zoomRegions, setZoomRegions] = useState<ZoomRegion[]>([]);
const [selectedZoomId, setSelectedZoomId] = useState<string | null>(null); const [selectedZoomId, setSelectedZoomId] = useState<string | null>(null);
@@ -306,6 +306,7 @@ export default function VideoEditor() {
showBlur, showBlur,
motionBlurEnabled, motionBlurEnabled,
borderRadius, borderRadius,
padding,
cropRegion, cropRegion,
onProgress: (progress: ExportProgress) => { onProgress: (progress: ExportProgress) => {
setExportProgress(progress); setExportProgress(progress);
@@ -347,7 +348,7 @@ export default function VideoEditor() {
setIsExporting(false); setIsExporting(false);
exporterRef.current = null; 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(() => { const handleCancelExport = useCallback(() => {
if (exporterRef.current) { if (exporterRef.current) {
@@ -413,6 +414,7 @@ export default function VideoEditor() {
showBlur={showBlur} showBlur={showBlur}
motionBlurEnabled={motionBlurEnabled} motionBlurEnabled={motionBlurEnabled}
borderRadius={borderRadius} borderRadius={borderRadius}
padding={padding}
cropRegion={cropRegion} cropRegion={cropRegion}
trimRegions={trimRegions} trimRegions={trimRegions}
/> />
@@ -29,6 +29,7 @@ interface VideoPlaybackProps {
showBlur?: boolean; showBlur?: boolean;
motionBlurEnabled?: boolean; motionBlurEnabled?: boolean;
borderRadius?: number; borderRadius?: number;
padding?: number;
cropRegion?: import('./types').CropRegion; cropRegion?: import('./types').CropRegion;
trimRegions?: TrimRegion[]; trimRegions?: TrimRegion[];
} }
@@ -59,6 +60,7 @@ const VideoPlayback = forwardRef<VideoPlaybackRef, VideoPlaybackProps>(({
showBlur, showBlur,
motionBlurEnabled = true, motionBlurEnabled = true,
borderRadius = 0, borderRadius = 0,
padding = 50,
cropRegion, cropRegion,
trimRegions = [], trimRegions = [],
}, ref) => { }, ref) => {
@@ -153,6 +155,7 @@ const VideoPlayback = forwardRef<VideoPlaybackRef, VideoPlaybackProps>(({
cropRegion, cropRegion,
lockedVideoDimensions: lockedVideoDimensionsRef.current, lockedVideoDimensions: lockedVideoDimensionsRef.current,
borderRadius, borderRadius,
padding,
}); });
if (result) { if (result) {
@@ -174,7 +177,7 @@ const VideoPlayback = forwardRef<VideoPlaybackRef, VideoPlaybackProps>(({
updateOverlayForRegion(activeRegion); updateOverlayForRegion(activeRegion);
} }
}, [updateOverlayForRegion, cropRegion, borderRadius]); }, [updateOverlayForRegion, cropRegion, borderRadius, padding]);
useEffect(() => { useEffect(() => {
layoutVideoContentRef.current = layoutVideoContent; layoutVideoContentRef.current = layoutVideoContent;
@@ -11,6 +11,7 @@ interface LayoutParams {
cropRegion?: CropRegion; cropRegion?: CropRegion;
lockedVideoDimensions?: { width: number; height: number } | null; lockedVideoDimensions?: { width: number; height: number } | null;
borderRadius?: number; borderRadius?: number;
padding?: number;
} }
interface LayoutResult { interface LayoutResult {
@@ -23,7 +24,7 @@ interface LayoutResult {
} }
export function layoutVideoContent(params: LayoutParams): LayoutResult | null { 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 videoWidth = lockedVideoDimensions?.width || videoElement.videoWidth;
const videoHeight = lockedVideoDimensions?.height || videoElement.videoHeight; const videoHeight = lockedVideoDimensions?.height || videoElement.videoHeight;
@@ -56,8 +57,10 @@ export function layoutVideoContent(params: LayoutParams): LayoutResult | null {
const cropEndY = cropStartY + croppedVideoHeight; const cropEndY = cropStartY + croppedVideoHeight;
// Calculate scale to fit the cropped area in the viewport // Calculate scale to fit the cropped area in the viewport
const maxDisplayWidth = width * VIEWPORT_SCALE; // Padding is a percentage (0-100), where 50 matches the original VIEWPORT_SCALE of 0.8
const maxDisplayHeight = height * VIEWPORT_SCALE; const paddingScale = 1.0 - (padding / 100) * 0.4;
const maxDisplayWidth = width * paddingScale;
const maxDisplayHeight = height * paddingScale;
const scale = Math.min( const scale = Math.min(
maxDisplayWidth / croppedVideoWidth, maxDisplayWidth / croppedVideoWidth,
+7 -30
View File
@@ -16,6 +16,7 @@ interface FrameRenderConfig {
showBlur: boolean; showBlur: boolean;
motionBlurEnabled?: boolean; motionBlurEnabled?: boolean;
borderRadius?: number; borderRadius?: number;
padding?: number;
cropRegion: CropRegion; cropRegion: CropRegion;
videoWidth: number; videoWidth: number;
videoHeight: number; videoHeight: number;
@@ -302,20 +303,10 @@ export class FrameRenderer {
if (!this.app || !this.videoSprite || !this.maskGraphics || !this.videoContainer) return; if (!this.app || !this.videoSprite || !this.maskGraphics || !this.videoContainer) return;
const { width, height } = this.config; 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 videoWidth = this.config.videoWidth;
const videoHeight = this.config.videoHeight; 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 // Calculate cropped video dimensions
const cropStartX = cropRegion.x; const cropStartX = cropRegion.x;
const cropStartY = cropRegion.y; const cropStartY = cropRegion.y;
@@ -325,9 +316,11 @@ export class FrameRenderer {
const croppedVideoWidth = videoWidth * (cropEndX - cropStartX); const croppedVideoWidth = videoWidth * (cropEndX - cropStartX);
const croppedVideoHeight = videoHeight * (cropEndY - cropStartY); const croppedVideoHeight = videoHeight * (cropEndY - cropStartY);
// Calculate scale to fit in viewport (using VIEWPORT_SCALE from constants) // Calculate scale to fit in viewport
const viewportWidth = width * 0.8; // VIEWPORT_SCALE = 0.8 // Padding is a percentage (0-100), where 50% ~ 0.8 scale
const viewportHeight = height * 0.8; const paddingScale = 1.0 - (padding / 100) * 0.4;
const viewportWidth = width * paddingScale;
const viewportHeight = height * paddingScale;
const scale = Math.min(viewportWidth / croppedVideoWidth, viewportHeight / croppedVideoHeight); const scale = Math.min(viewportWidth / croppedVideoWidth, viewportHeight / croppedVideoHeight);
// Position video sprite // Position video sprite
@@ -348,22 +341,6 @@ export class FrameRenderer {
this.videoContainer.y = centerOffsetY; this.videoContainer.y = centerOffsetY;
// Update mask // 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.clear();
this.maskGraphics.roundRect(0, 0, croppedDisplayWidth, croppedDisplayHeight, borderRadius); this.maskGraphics.roundRect(0, 0, croppedDisplayWidth, croppedDisplayHeight, borderRadius);
this.maskGraphics.fill({ color: 0xffffff }); this.maskGraphics.fill({ color: 0xffffff });
+2
View File
@@ -14,6 +14,7 @@ interface VideoExporterConfig extends ExportConfig {
showBlur: boolean; showBlur: boolean;
motionBlurEnabled?: boolean; motionBlurEnabled?: boolean;
borderRadius?: number; borderRadius?: number;
padding?: number;
videoPadding?: number; videoPadding?: number;
cropRegion: CropRegion; cropRegion: CropRegion;
onProgress?: (progress: ExportProgress) => void; onProgress?: (progress: ExportProgress) => void;
@@ -89,6 +90,7 @@ export class VideoExporter {
showBlur: this.config.showBlur, showBlur: this.config.showBlur,
motionBlurEnabled: this.config.motionBlurEnabled, motionBlurEnabled: this.config.motionBlurEnabled,
borderRadius: this.config.borderRadius, borderRadius: this.config.borderRadius,
padding: this.config.padding,
cropRegion: this.config.cropRegion, cropRegion: this.config.cropRegion,
videoWidth: videoInfo.width, videoWidth: videoInfo.width,
videoHeight: videoInfo.height, videoHeight: videoInfo.height,