feat(export): add native aspect ratio option for background-free exports

This commit is contained in:
webadderall
2026-03-14 22:53:01 +11:00
parent 55a65073c7
commit e5e869e654
3 changed files with 39 additions and 9 deletions
+13 -3
View File
@@ -1156,9 +1156,10 @@ export default function VideoEditor() {
videoPlaybackRef.current?.pause();
}
const aspectRatioValue = getAspectRatioValue(aspectRatio);
const sourceWidth = video.videoWidth || 1920;
const sourceHeight = video.videoHeight || 1080;
const sourceAspectRatio = sourceHeight > 0 ? sourceWidth / sourceHeight : 16 / 9;
const aspectRatioValue = getAspectRatioValue(aspectRatio, sourceAspectRatio);
// Get preview CONTAINER dimensions for scaling
const playbackRef = videoPlaybackRef.current;
@@ -1241,7 +1242,10 @@ export default function VideoEditor() {
exportWidth = sourceWidth;
exportHeight = sourceHeight;
if (aspectRatioValue === 1) {
if (aspectRatio === 'native') {
exportWidth = Math.floor(sourceWidth / 2) * 2;
exportHeight = Math.floor(sourceHeight / 2) * 2;
} else if (aspectRatioValue === 1) {
// Square (1:1): use smaller dimension to avoid codec limits
const baseDimension = Math.floor(Math.min(sourceWidth, sourceHeight) / 2) * 2;
exportWidth = baseDimension;
@@ -1544,7 +1548,13 @@ export default function VideoEditor() {
<div className="w-full h-full flex flex-col items-center justify-center bg-black/40 rounded-2xl border border-white/5 shadow-2xl overflow-hidden">
{/* Video preview */}
<div className="w-full flex justify-center items-center" style={{ flex: '1 1 auto', margin: '6px 0 0' }}>
<div className="relative" style={{ width: 'auto', height: '100%', aspectRatio: getAspectRatioValue(aspectRatio), maxWidth: '100%', margin: '0 auto', boxSizing: 'border-box' }}>
<div className="relative" style={{ width: 'auto', height: '100%', aspectRatio: getAspectRatioValue(aspectRatio, (() => {
const previewVideo = videoPlaybackRef.current?.video;
if (previewVideo && previewVideo.videoHeight > 0) {
return previewVideo.videoWidth / previewVideo.videoHeight;
}
return 16 / 9;
})()), maxWidth: '100%', margin: '0 auto', boxSizing: 'border-box' }}>
<VideoPlayback
key={videoPath || 'no-video'}
aspectRatio={aspectRatio}
+13 -1
View File
@@ -1029,8 +1029,20 @@ const VideoPlayback = forwardRef<VideoPlaybackRef, VideoPlaybackProps>(({
? { backgroundImage: `url(${resolvedWallpaper || ''})` }
: { background: resolvedWallpaper || '' };
const nativeAspectRatio = (() => {
const locked = lockedVideoDimensionsRef.current;
if (locked && locked.height > 0) {
return locked.width / locked.height;
}
const video = videoRef.current;
if (video && video.videoHeight > 0) {
return video.videoWidth / video.videoHeight;
}
return 16 / 9;
})();
return (
<div className="relative rounded-sm overflow-hidden" style={{ width: '100%', aspectRatio: formatAspectRatioForCSS(aspectRatio) }}>
<div className="relative rounded-sm overflow-hidden" style={{ width: '100%', aspectRatio: formatAspectRatioForCSS(aspectRatio, nativeAspectRatio) }}>
{/* Background layer - always render as DOM element with blur */}
<div
className="absolute inset-0 bg-cover bg-center"
+13 -5
View File
@@ -1,4 +1,4 @@
export const ASPECT_RATIOS = ['16:9', '9:16', '1:1', '4:3', '4:5', '16:10', '10:16'] as const;
export const ASPECT_RATIOS = ['native', '16:9', '9:16', '1:1', '4:3', '4:5', '16:10', '10:16'] as const;
export type AspectRatio = typeof ASPECT_RATIOS[number];
@@ -7,8 +7,9 @@ export type AspectRatio = typeof ASPECT_RATIOS[number];
* Uses exhaustive type checking to ensure all AspectRatio cases are handled.
* If TypeScript errors here, a new ratio was added to the type but not handled.
*/
export function getAspectRatioValue(aspectRatio: AspectRatio): number {
export function getAspectRatioValue(aspectRatio: AspectRatio, nativeAspectRatio = 16 / 9): number {
switch (aspectRatio) {
case 'native': return nativeAspectRatio > 0 ? nativeAspectRatio : 16 / 9;
case '16:9': return 16 / 9;
case '9:16': return 9 / 16;
case '1:1': return 1;
@@ -26,9 +27,10 @@ export function getAspectRatioValue(aspectRatio: AspectRatio): number {
export function getAspectRatioDimensions(
aspectRatio: AspectRatio,
baseWidth: number
baseWidth: number,
nativeAspectRatio?: number,
): { width: number; height: number } {
const ratio = getAspectRatioValue(aspectRatio);
const ratio = getAspectRatioValue(aspectRatio, nativeAspectRatio);
return {
width: baseWidth,
height: baseWidth / ratio,
@@ -36,11 +38,17 @@ export function getAspectRatioDimensions(
}
export function getAspectRatioLabel(aspectRatio: AspectRatio): string {
if (aspectRatio === 'native') {
return 'Native';
}
return aspectRatio;
}
export function formatAspectRatioForCSS(aspectRatio: AspectRatio): string {
export function formatAspectRatioForCSS(aspectRatio: AspectRatio, nativeAspectRatio = 16 / 9): string {
if (aspectRatio === 'native') {
return `${nativeAspectRatio > 0 ? nativeAspectRatio : 16 / 9}`;
}
return aspectRatio.replace(':', '/');
}