@@ -25,6 +25,7 @@ import {
2525 type StyledRenderRect ,
2626 type WebcamLayoutPreset ,
2727} from "@/lib/compositeLayout" ;
28+ import { getCssClipPath } from "@/lib/webcamMaskShapes" ;
2829import {
2930 type AspectRatio ,
3031 formatAspectRatioForCSS ,
@@ -63,6 +64,7 @@ interface VideoPlaybackProps {
6364 videoPath : string ;
6465 webcamVideoPath ?: string ;
6566 webcamLayoutPreset : WebcamLayoutPreset ;
67+ webcamMaskShape ?: import ( "./types" ) . WebcamMaskShape ;
6668 webcamPosition ?: { cx : number ; cy : number } | null ;
6769 onWebcamPositionChange ?: ( position : { cx : number ; cy : number } ) => void ;
6870 onWebcamPositionDragEnd ?: ( ) => void ;
@@ -111,6 +113,7 @@ const VideoPlayback = forwardRef<VideoPlaybackRef, VideoPlaybackProps>(
111113 videoPath,
112114 webcamVideoPath,
113115 webcamLayoutPreset,
116+ webcamMaskShape,
114117 webcamPosition,
115118 onWebcamPositionChange,
116119 onWebcamPositionDragEnd,
@@ -272,6 +275,7 @@ const VideoPlayback = forwardRef<VideoPlaybackRef, VideoPlaybackProps>(
272275 webcamDimensions,
273276 webcamLayoutPreset,
274277 webcamPosition,
278+ webcamMaskShape,
275279 } ) ;
276280
277281 if ( result ) {
@@ -302,6 +306,7 @@ const VideoPlayback = forwardRef<VideoPlaybackRef, VideoPlaybackProps>(
302306 webcamDimensions ,
303307 webcamLayoutPreset ,
304308 webcamPosition ,
309+ webcamMaskShape ,
305310 ] ) ;
306311
307312 useEffect ( ( ) => {
@@ -1154,31 +1159,47 @@ const VideoPlayback = forwardRef<VideoPlaybackRef, VideoPlaybackProps>(
11541159 : "none" ,
11551160 } }
11561161 />
1157- { webcamVideoPath && (
1158- < video
1159- ref = { webcamVideoRef }
1160- src = { webcamVideoPath }
1161- className = { `absolute object-cover ${ webcamLayoutPreset === "picture-in-picture" ? "cursor-grab active:cursor-grabbing" : "pointer-events-none" } ` }
1162- style = { {
1163- left : webcamLayout ?. x ?? 0 ,
1164- top : webcamLayout ?. y ?? 0 ,
1165- width : webcamLayout ?. width ?? 0 ,
1166- height : webcamLayout ?. height ?? 0 ,
1167- borderRadius : webcamLayout ?. borderRadius ?? 0 ,
1168- boxShadow : webcamCssBoxShadow ,
1169- zIndex : 20 ,
1170- opacity : webcamLayout ? 1 : 0 ,
1171- backgroundColor : "#000" ,
1172- } }
1173- onPointerDown = { handleWebcamPointerDown }
1174- onPointerMove = { handleWebcamPointerMove }
1175- onPointerUp = { handleWebcamPointerUp }
1176- onPointerLeave = { handleWebcamPointerUp }
1177- muted
1178- preload = "metadata"
1179- playsInline
1180- />
1181- ) }
1162+ { webcamVideoPath &&
1163+ ( ( ) => {
1164+ const clipPath = getCssClipPath ( webcamLayout ?. maskShape ?? "rectangle" ) ;
1165+ const useClipPath = ! ! clipPath ;
1166+ return (
1167+ < div
1168+ className = "absolute"
1169+ style = { {
1170+ left : webcamLayout ?. x ?? 0 ,
1171+ top : webcamLayout ?. y ?? 0 ,
1172+ width : webcamLayout ?. width ?? 0 ,
1173+ height : webcamLayout ?. height ?? 0 ,
1174+ zIndex : 20 ,
1175+ opacity : webcamLayout ? 1 : 0 ,
1176+ filter :
1177+ useClipPath && webcamCssBoxShadow !== "none"
1178+ ? `drop-shadow(${ webcamCssBoxShadow } )`
1179+ : undefined ,
1180+ } }
1181+ >
1182+ < video
1183+ ref = { webcamVideoRef }
1184+ src = { webcamVideoPath }
1185+ className = { `w-full h-full object-cover ${ webcamLayoutPreset === "picture-in-picture" ? "cursor-grab active:cursor-grabbing" : "pointer-events-none" } ` }
1186+ style = { {
1187+ borderRadius : useClipPath ? 0 : ( webcamLayout ?. borderRadius ?? 0 ) ,
1188+ clipPath : clipPath ?? undefined ,
1189+ boxShadow : useClipPath ? "none" : webcamCssBoxShadow ,
1190+ backgroundColor : "#000" ,
1191+ } }
1192+ onPointerDown = { handleWebcamPointerDown }
1193+ onPointerMove = { handleWebcamPointerMove }
1194+ onPointerUp = { handleWebcamPointerUp }
1195+ onPointerLeave = { handleWebcamPointerUp }
1196+ muted
1197+ preload = "metadata"
1198+ playsInline
1199+ />
1200+ </ div >
1201+ ) ;
1202+ } ) ( ) }
11821203 { /* Only render overlay after PIXI and video are fully initialized */ }
11831204 { pixiReady && videoReady && (
11841205 < div
0 commit comments