movable camera pip

This commit is contained in:
Siddharth
2026-03-21 22:04:10 -07:00
parent 7aca8b8bc1
commit cbbe2d7fbf
10 changed files with 137 additions and 5 deletions
+20 -2
View File
@@ -123,6 +123,7 @@ export function computeCompositeLayout(params: {
screenSize: Size;
webcamSize?: Size | null;
layoutPreset?: WebcamLayoutPreset;
webcamPosition?: { cx: number; cy: number } | null;
}): WebcamCompositeLayout | null {
const {
canvasSize,
@@ -130,6 +131,7 @@ export function computeCompositeLayout(params: {
screenSize,
webcamSize,
layoutPreset = "picture-in-picture",
webcamPosition,
} = params;
const { width: canvasWidth, height: canvasHeight } = canvasSize;
const { width: maxContentWidth, height: maxContentHeight } = maxContentSize;
@@ -214,11 +216,27 @@ export function computeCompositeLayout(params: {
const width = Math.round(webcamWidth * scale);
const height = Math.round(webcamHeight * scale);
let webcamX: number;
let webcamY: number;
if (webcamPosition) {
// Custom position: cx/cy represent the center of the webcam as a fraction of the canvas
webcamX = Math.round(webcamPosition.cx * canvasWidth - width / 2);
webcamY = Math.round(webcamPosition.cy * canvasHeight - height / 2);
// Clamp to stay within canvas bounds
webcamX = Math.max(0, Math.min(canvasWidth - width, webcamX));
webcamY = Math.max(0, Math.min(canvasHeight - height, webcamY));
} else {
// Default: bottom-right with margin
webcamX = Math.max(0, Math.round(canvasWidth - margin - width));
webcamY = Math.max(0, Math.round(canvasHeight - margin - height));
}
return {
screenRect,
webcamRect: {
x: Math.max(0, Math.round(canvasWidth - margin - width)),
y: Math.max(0, Math.round(canvasHeight - margin - height)),
x: webcamX,
y: webcamY,
width,
height,
borderRadius: Math.min(
+2
View File
@@ -61,6 +61,7 @@ interface FrameRenderConfig {
videoHeight: number;
webcamSize?: Size | null;
webcamLayoutPreset?: WebcamLayoutPreset;
webcamPosition?: { cx: number; cy: number } | null;
annotationRegions?: AnnotationRegion[];
speedRegions?: SpeedRegion[];
previewWidth?: number;
@@ -437,6 +438,7 @@ export class FrameRenderer {
screenSize: { width: croppedVideoWidth, height: croppedVideoHeight },
webcamSize: webcamFrame ? this.config.webcamSize : null,
layoutPreset: this.config.webcamLayoutPreset,
webcamPosition: this.config.webcamPosition,
});
if (!compositeLayout) return;
+2
View File
@@ -41,6 +41,7 @@ interface GifExporterConfig {
videoPadding?: number;
cropRegion: CropRegion;
webcamLayoutPreset?: WebcamLayoutPreset;
webcamPosition?: { cx: number; cy: number } | null;
annotationRegions?: AnnotationRegion[];
previewWidth?: number;
previewHeight?: number;
@@ -140,6 +141,7 @@ export class GifExporter {
videoHeight: videoInfo.height,
webcamSize: webcamInfo ? { width: webcamInfo.width, height: webcamInfo.height } : null,
webcamLayoutPreset: this.config.webcamLayoutPreset,
webcamPosition: this.config.webcamPosition,
annotationRegions: this.config.annotationRegions,
speedRegions: this.config.speedRegions,
previewWidth: this.config.previewWidth,
+2
View File
@@ -32,6 +32,7 @@ interface VideoExporterConfig extends ExportConfig {
videoPadding?: number;
cropRegion: CropRegion;
webcamLayoutPreset?: WebcamLayoutPreset;
webcamPosition?: { cx: number; cy: number } | null;
annotationRegions?: AnnotationRegion[];
previewWidth?: number;
previewHeight?: number;
@@ -133,6 +134,7 @@ export class VideoExporter {
videoHeight: videoInfo.height,
webcamSize: webcamInfo ? { width: webcamInfo.width, height: webcamInfo.height } : null,
webcamLayoutPreset: this.config.webcamLayoutPreset,
webcamPosition: this.config.webcamPosition,
annotationRegions: this.config.annotationRegions,
speedRegions: this.config.speedRegions,
previewWidth: this.config.previewWidth,